computeReachableShapes(Model model, ServiceShape serv
}
return reachable;
}
+
+ /**
+ * Returns this class as a no-op ModelTransform.
+ *
+ * GlobalTransforms currently operates at classification time (via computeReachableShapes),
+ * not at model-transform time. This factory exists to reserve a slot in the pipeline
+ * for future pre-generation model mutations (shape renaming, injection, etc.).
+ */
+ public static ModelTransform asTransform() {
+ return (model, service) -> model;
+ }
}
From d24ead40e0170dd1d3814ad4bee83bad23055b77 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Tue, 21 Jul 2026 14:39:51 -0400
Subject: [PATCH 06/18] Deduplicate MemberRenderer public section methods
Extract shared logic into renderMembers(emitHasBeenSet) to eliminate
~100 lines of near-identical code between request and result rendering.
---
.../generators/model/MemberRenderer.java | 124 +++---------------
1 file changed, 21 insertions(+), 103 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java
index cc9074e1423..5b297e05052 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java
@@ -50,108 +50,7 @@ private MemberRenderer() {
*/
public static void renderPublicSection(CppWriter writer, StructureShape shape,
Model model, String exportMacro, String className) {
- java.util.List> members =
- new java.util.ArrayList<>(shape.getAllMembers().entrySet());
- for (int i = 0; i < members.size(); i++) {
- Map.Entry entry = members.get(i);
- String memberName = entry.getKey();
- MemberShape member = entry.getValue();
- Shape targetShape = model.expectShape(member.getTarget());
- String cppType = CppTypeMapper.getCppType(targetShape, model);
- String fieldName = "m_" + decapitalize(memberName);
- String templateParam = memberName + "T";
-
- writer.write("///@{");
-
- // Documentation comment
- if (member.getTrait(DocumentationTrait.class).isPresent()) {
- writeDocComment(writer, collapseWhitespace(member.getTrait(DocumentationTrait.class).get().getValue()));
- } else {
- writer.write("");
- }
-
- // Getter
- if (isPrimitive(targetShape) || targetShape.isEnumShape()) {
- writer.write("inline $L Get$L() const { return $L; }", cppType, memberName, fieldName);
- } else {
- writer.write("inline const $L& Get$L() const { return $L; }", cppType, memberName, fieldName);
- }
-
- // HasBeenSet
- writer.write("inline bool $LHasBeenSet() const { return $LHasBeenSet; }", memberName, fieldName);
-
- if (targetShape.isEnumShape() || isPrimitive(targetShape)) {
- // Enum and primitive members use non-templated value setters
- writer.openBlock("inline void Set$L($L value) {", "}", memberName, cppType, () -> {
- writer.write("$LHasBeenSet = true;", fieldName);
- writer.write("$L = value;", fieldName);
- });
-
- writer.openBlock("inline $L& With$L($L value) {", "}", className, memberName, cppType, () -> {
- writer.write("Set$L(value);", memberName);
- writer.write("return *this;");
- });
- } else {
- // Templated Set
- writer.write("template ", templateParam, cppType);
- writer.openBlock("void Set$L($L&& value) {", "}", memberName, templateParam, () -> {
- writer.write("$LHasBeenSet = true;", fieldName);
- writer.write("$L = std::forward<$L>(value);", fieldName, templateParam);
- });
-
- // Templated With (fluent)
- writer.write("template ", templateParam, cppType);
- writer.openBlock("$L& With$L($L&& value) {", "}", className, memberName, templateParam, () -> {
- writer.write("Set$L(std::forward<$L>(value));", memberName, templateParam);
- writer.write("return *this;");
- });
-
- // Add method for list types
- if (targetShape.isListShape()) {
- Shape elementShape = model.expectShape(
- targetShape.asListShape().get().getMember().getTarget());
- String elementType = CppTypeMapper.getCppType(elementShape, model);
- if (elementShape.isEnumShape()) {
- writer.openBlock("inline $L& Add$L($L value) {", "}", className, memberName, elementType, () -> {
- writer.write("$LHasBeenSet = true;", fieldName);
- writer.write("$L.push_back(value);", fieldName);
- writer.write("return *this;");
- });
- } else {
- writer.write("template ", templateParam, elementType);
- writer.openBlock("$L& Add$L($L&& value) {", "}", className, memberName, templateParam, () -> {
- writer.write("$LHasBeenSet = true;", fieldName);
- writer.write("$L.emplace_back(std::forward<$L>(value));", fieldName, templateParam);
- writer.write("return *this;");
- });
- }
- }
-
- // Add method for map types (two template params: key + value)
- if (targetShape.isMapShape()) {
- Shape keyShape = model.expectShape(
- targetShape.asMapShape().get().getKey().getTarget());
- Shape valueShape = model.expectShape(
- targetShape.asMapShape().get().getValue().getTarget());
- String keyType = CppTypeMapper.getCppType(keyShape, model);
- String valueType = CppTypeMapper.getCppType(valueShape, model);
- String keyParam = memberName + "KeyT";
- String valueParam = memberName + "ValueT";
- writer.write("template ", keyParam, keyType, valueParam, valueType);
- writer.openBlock("$L& Add$L($L&& key, $L&& value) {", "}", className, memberName, keyParam, valueParam, () -> {
- writer.write("$LHasBeenSet = true;", fieldName);
- writer.write("$L.emplace(std::forward<$L>(key), std::forward<$L>(value));", fieldName, keyParam, valueParam);
- writer.write("return *this;");
- });
- }
- }
-
- writer.write("///@}");
- // Blank line between member blocks, but not after the last one
- if (i < members.size() - 1) {
- writer.write("");
- }
- }
+ renderMembers(writer, shape, model, exportMacro, className, true);
}
/**
@@ -165,6 +64,22 @@ public static void renderPublicSection(CppWriter writer, StructureShape shape,
*/
public static void renderPublicSectionForResult(CppWriter writer, StructureShape shape,
Model model, String exportMacro, String className) {
+ renderMembers(writer, shape, model, exportMacro, className, false);
+ }
+
+ /**
+ * Shared implementation for rendering public accessor methods.
+ *
+ * @param writer the CppWriter to write to
+ * @param shape the structure shape whose members to render
+ * @param model the model (for resolving member targets)
+ * @param exportMacro the export macro (e.g., "AWS_KINESIS_API")
+ * @param className the C++ class name (e.g., "ChildShard")
+ * @param emitHasBeenSet whether to emit HasBeenSet() accessor methods
+ */
+ private static void renderMembers(CppWriter writer, StructureShape shape,
+ Model model, String exportMacro, String className,
+ boolean emitHasBeenSet) {
java.util.List> members =
new java.util.ArrayList<>(shape.getAllMembers().entrySet());
for (int i = 0; i < members.size(); i++) {
@@ -191,7 +106,10 @@ public static void renderPublicSectionForResult(CppWriter writer, StructureShape
writer.write("inline const $L& Get$L() const { return $L; }", cppType, memberName, fieldName);
}
- // No HasBeenSet for results
+ // HasBeenSet (only for non-result shapes)
+ if (emitHasBeenSet) {
+ writer.write("inline bool $LHasBeenSet() const { return $LHasBeenSet; }", memberName, fieldName);
+ }
if (targetShape.isEnumShape() || isPrimitive(targetShape)) {
writer.openBlock("inline void Set$L($L value) {", "}", memberName, cppType, () -> {
From 60892d9b8312d15b2386fb19e7503c9dad1c2bbd Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Tue, 21 Jul 2026 14:41:52 -0400
Subject: [PATCH 07/18] Use FileManifest API in CppWriterDelegator
Enables Smithy's built-in change detection and file tracking
instead of raw Files.writeString().
---
.../generators/CppWriterDelegator.java | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java
index 5253327247e..63b060d0bde 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java
@@ -5,7 +5,6 @@
package com.amazonaws.util.awsclientsmithygenerator.generators;
import software.amazon.smithy.build.FileManifest;
-import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
@@ -25,15 +24,9 @@ public void useFileWriter(String filename, Consumer writerConsumer) {
public void flushWriters() {
writers.forEach((filename, writer) -> {
- try {
- Path outputPath = fileManifest.getBaseDir().resolve(filename);
- java.nio.file.Files.createDirectories(outputPath.getParent());
- // Add UTF-8 BOM to match C2J-generated file format
- String content = "" + writer.toString();
- java.nio.file.Files.writeString(outputPath, content);
- } catch (Exception e) {
- throw new RuntimeException("Failed to write file: " + filename, e);
- }
+ // Add UTF-8 BOM to match C2J-generated file format
+ String content = "" + writer.toString();
+ fileManifest.writeFile(filename, content);
});
}
}
\ No newline at end of file
From 423bcb733cc2bed80c151f55d2a9b1c4a3cc68a6 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Tue, 21 Jul 2026 14:44:15 -0400
Subject: [PATCH 08/18] Replace wildcard imports in ShapeClassifier with
explicit imports
---
.../generators/model/ShapeClassifier.java | 20 +++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java
index cef3712dc27..248a3544762 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java
@@ -8,10 +8,22 @@
import com.amazonaws.util.awsclientsmithygenerator.generators.model.transforms.GlobalTransforms;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.knowledge.TopDownIndex;
-import software.amazon.smithy.model.shapes.*;
-import software.amazon.smithy.model.traits.*;
-
-import java.util.*;
+import software.amazon.smithy.model.shapes.EnumShape;
+import software.amazon.smithy.model.shapes.MemberShape;
+import software.amazon.smithy.model.shapes.OperationShape;
+import software.amazon.smithy.model.shapes.ServiceShape;
+import software.amazon.smithy.model.shapes.Shape;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.shapes.StructureShape;
+import software.amazon.smithy.model.shapes.UnionShape;
+import software.amazon.smithy.model.traits.EnumTrait;
+import software.amazon.smithy.model.traits.ErrorTrait;
+import software.amazon.smithy.model.traits.StreamingTrait;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
/**
From 2a767d4d8415a5c2e1735656e04dcb7d2b9e318e Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Mon, 27 Jul 2026 13:26:08 -0400
Subject: [PATCH 09/18] Add enum NOT_SET case
---
.../generators/model/EnumRenderer.java | 8 +++++---
.../generators/model/EnumRendererTest.java | 6 ++++--
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRenderer.java
index 67f414da4f5..0544261e7ed 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRenderer.java
@@ -151,8 +151,10 @@ public static void renderSource(CppWriter writer, Shape enumShape, String servic
writer.write("");
// GetNameFor
- writer.write(" Aws::String GetNameFor$1L($1L value) {", enumName);
- writer.write(" switch (value) {");
+ writer.write(" Aws::String GetNameFor$1L($1L enumValue) {", enumName);
+ writer.write(" switch (enumValue) {");
+ writer.write(" case $1L::NOT_SET:", enumName);
+ writer.write(" return {};");
for (int i = 0; i < values.size(); i++) {
writer.write(" case $1L::$2L:", enumName, values.get(i));
writer.write(" return \"$L\";", wireValues.get(i));
@@ -160,7 +162,7 @@ public static void renderSource(CppWriter writer, Shape enumShape, String servic
writer.write(" default:");
writer.write(" EnumParseOverflowContainer* overflowContainer = Aws::GetEnumOverflowContainer();");
writer.write(" if (overflowContainer) {");
- writer.write(" return overflowContainer->RetrieveOverflow(static_cast(value));");
+ writer.write(" return overflowContainer->RetrieveOverflow(static_cast(enumValue));");
writer.write(" }");
writer.write(" return {};");
writer.write(" }");
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRendererTest.java
index 23bf9fff966..806c523a5d1 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRendererTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRendererTest.java
@@ -116,8 +116,10 @@ void renderSource_producesGetNameForFunction() {
CppWriter writer = new CppWriter();
EnumRenderer.renderSource(writer, enumShape, "Kinesis", "AWS_KINESIS_API", "kinesis");
String output = writer.toString();
- assertTrue(output.contains("Aws::String GetNameForStatus(Status value)"),
+ assertTrue(output.contains("Aws::String GetNameForStatus(Status enumValue)"),
"Missing GetNameFor function: " + output);
+ assertTrue(output.contains("case Status::NOT_SET:"),
+ "Missing NOT_SET case: " + output);
assertTrue(output.contains("case Status::CREATING:"),
"Missing CREATING case: " + output);
assertTrue(output.contains("case Status::ACTIVE:"),
@@ -137,7 +139,7 @@ void renderSource_includesOverflowContainer() {
"Missing overflow container in GetForName: " + output);
assertTrue(output.contains("overflowContainer->StoreOverflow(hashCode, name)"),
"Missing StoreOverflow: " + output);
- assertTrue(output.contains("overflowContainer->RetrieveOverflow(static_cast(value))"),
+ assertTrue(output.contains("overflowContainer->RetrieveOverflow(static_cast(enumValue))"),
"Missing RetrieveOverflow: " + output);
}
From de07f1cecdfab0a2084ae853688008ff8fa29c94 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Wed, 29 Jul 2026 13:45:44 -0400
Subject: [PATCH 10/18] Add event stream detection helpers to ShapeClassifier
---
.../generators/model/ShapeClassifier.java | 34 +++++++++++
.../generators/model/ShapeClassifierTest.java | 57 +++++++++++++++++++
2 files changed, 91 insertions(+)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java
index 248a3544762..df118be1ac0 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifier.java
@@ -192,4 +192,38 @@ private static boolean hasEventStreamMembers(StructureShape shape, Model model)
return target.isUnionShape() && target.hasTrait(StreamingTrait.class);
});
}
+
+ /**
+ * Returns true if the operation's output structure has an event stream (a member
+ * targeting a {@code @streaming} union). This is the response-side (read) test.
+ *
+ * @param op the operation
+ * @param model the Smithy model
+ * @return true if the operation produces an event stream response
+ */
+ public static boolean isEventStreamResponseOperation(OperationShape op, Model model) {
+ return op.getOutput()
+ .flatMap(model::getShape)
+ .flatMap(Shape::asStructureShape)
+ .map(out -> hasEventStreamMembers(out, model))
+ .orElse(false);
+ }
+
+ /**
+ * Returns true if the operation's input structure has an event stream (a member
+ * targeting a {@code @streaming} union). This is the request-side (write) test,
+ * used only to flag bidirectional requests; input-stream encoder generation is
+ * out of scope.
+ *
+ * @param op the operation
+ * @param model the Smithy model
+ * @return true if the operation consumes an event stream request
+ */
+ public static boolean isEventStreamRequestOperation(OperationShape op, Model model) {
+ return op.getInput()
+ .flatMap(model::getShape)
+ .flatMap(Shape::asStructureShape)
+ .map(in -> hasEventStreamMembers(in, model))
+ .orElse(false);
+ }
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifierTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifierTest.java
index dff69c02d9b..6fe308881af 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifierTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ShapeClassifierTest.java
@@ -246,6 +246,63 @@ void classifiesEventStreamHandler() {
.anyMatch(r -> r.shape().getId().getName().equals("SubscribeRequest")));
}
+ private static Model modelWithStreaming(boolean inputStreams, boolean outputStreams) {
+ StringShape str = StringShape.builder().id("com.example#String").build();
+ UnionShape eventStream = UnionShape.builder()
+ .id("com.example#EventStream")
+ .addTrait(new StreamingTrait())
+ .addMember("event", str.getId())
+ .build();
+
+ StructureShape.Builder inputBuilder = StructureShape.builder().id("com.example#OpInput");
+ if (inputStreams) {
+ inputBuilder.addMember("body", eventStream.getId());
+ } else {
+ inputBuilder.addMember("name", str.getId());
+ }
+ StructureShape input = inputBuilder.build();
+
+ StructureShape.Builder outputBuilder = StructureShape.builder().id("com.example#OpOutput");
+ if (outputStreams) {
+ outputBuilder.addMember("stream", eventStream.getId());
+ } else {
+ outputBuilder.addMember("result", str.getId());
+ }
+ StructureShape output = outputBuilder.build();
+
+ OperationShape op = OperationShape.builder()
+ .id("com.example#Op")
+ .input(input.getId())
+ .output(output.getId())
+ .build();
+
+ return Model.builder().addShapes(str, eventStream, input, output, op).build();
+ }
+
+ @Test
+ void isEventStreamResponseOperation_trueWhenOutputStreams() {
+ Model model = modelWithStreaming(false, true);
+ OperationShape op = model.expectShape(ShapeId.from("com.example#Op"), OperationShape.class);
+ assertTrue(ShapeClassifier.isEventStreamResponseOperation(op, model));
+ assertFalse(ShapeClassifier.isEventStreamRequestOperation(op, model));
+ }
+
+ @Test
+ void isEventStreamRequestOperation_trueWhenInputStreams() {
+ Model model = modelWithStreaming(true, true);
+ OperationShape op = model.expectShape(ShapeId.from("com.example#Op"), OperationShape.class);
+ assertTrue(ShapeClassifier.isEventStreamRequestOperation(op, model));
+ assertTrue(ShapeClassifier.isEventStreamResponseOperation(op, model));
+ }
+
+ @Test
+ void isEventStreamResponseOperation_falseWhenNeitherStreams() {
+ Model model = modelWithStreaming(false, false);
+ OperationShape op = model.expectShape(ShapeId.from("com.example#Op"), OperationShape.class);
+ assertFalse(ShapeClassifier.isEventStreamResponseOperation(op, model));
+ assertFalse(ShapeClassifier.isEventStreamRequestOperation(op, model));
+ }
+
@Test
void classifiesEnumShape() {
// StringShape with @enum trait -> classified as enum
From 97bbfd6edeebf860322b0f62d20f3982580c899f Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Wed, 29 Jul 2026 13:48:33 -0400
Subject: [PATCH 11/18] Add event-stream serde stub helpers to SerdeStub
---
.../generators/model/SerdeStub.java | 40 +++++++++++++++
.../generators/model/SerdeStubTest.java | 49 +++++++++++++++++++
2 files changed, 89 insertions(+)
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStubTest.java
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java
index d17bdf26de2..d383740f107 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java
@@ -56,6 +56,46 @@ public static void renderSourceImplementation(CppWriter writer, StructureShape s
}
}
+ /**
+ * Emits the empty per-protocol serde source bodies for a class that has no backing
+ * StructureShape (event stream union / initial response). Mirrors
+ * {@link #renderSourceImplementation} but keyed only on the class name.
+ */
+ public static void renderSerdeSourceStub(CppWriter writer, Protocol protocol, String className) {
+ if (protocol.isJsonLike()) {
+ renderJsonStub(writer, className);
+ } else if (protocol == Protocol.REST_XML) {
+ renderXmlStub(writer, className);
+ } else if (protocol == Protocol.QUERY_XML || protocol == Protocol.EC2) {
+ renderQueryXmlStub(writer, className);
+ } else {
+ throw new UnsupportedOperationException("Unsupported protocol for serde source stub: " + protocol);
+ }
+ }
+
+ /**
+ * Emits a protocol-agnostic stub for an event-stream event case body: a TODO marker
+ * and a minimal, compilable callback invocation with a default-constructed event.
+ * No protocol-specific deserialization is emitted.
+ *
+ * @param writer the CppWriter
+ * @param eventShapeName the C++ event shape type (e.g. "SubscribeToShardEvent")
+ * @param callbackMember the handler callback member (e.g. "m_onSubscribeToShardEvent")
+ */
+ public static void renderEventPayloadDecodeStub(CppWriter writer, String eventShapeName,
+ String callbackMember) {
+ writer.write("// TODO: protocol-specific event payload deserialization");
+ writer.write("$L($L{});", callbackMember, eventShapeName);
+ }
+
+ /**
+ * Emits a protocol-agnostic stub in place of the protocol-specific error-payload parse.
+ * The header-derived error code/message still flow to MarshallError at the call site.
+ */
+ public static void renderErrorPayloadParseStub(CppWriter writer) {
+ writer.write("// TODO: protocol-specific error payload deserialization");
+ }
+
private static void renderJsonStub(CppWriter writer, String className) {
writer.write("$L::$L(JsonView jsonValue) { *this = jsonValue; }", className, className);
writer.write("");
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStubTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStubTest.java
new file mode 100644
index 00000000000..2335c427e3a
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStubTest.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class SerdeStubTest {
+
+ @Test
+ void renderEventPayloadDecodeStub_emitsTodoAndNoProtocolTokens() {
+ CppWriter writer = new CppWriter();
+ SerdeStub.renderEventPayloadDecodeStub(writer, "SubscribeToShardEvent", "m_onSubscribeToShardEvent");
+ String out = writer.toString();
+ assertTrue(out.contains("// TODO: protocol-specific event payload deserialization"),
+ "Missing TODO marker: " + out);
+ assertTrue(out.contains("m_onSubscribeToShardEvent(SubscribeToShardEvent{});"),
+ "Missing callback invocation: " + out);
+ assertFalse(out.contains("JsonValue"), "Should not emit JSON tokens: " + out);
+ assertFalse(out.contains("Cbor"), "Should not emit CBOR tokens: " + out);
+ }
+
+ @Test
+ void renderErrorPayloadParseStub_emitsTodoOnly() {
+ CppWriter writer = new CppWriter();
+ SerdeStub.renderErrorPayloadParseStub(writer);
+ String out = writer.toString();
+ assertTrue(out.contains("// TODO: protocol-specific error payload deserialization"),
+ "Missing TODO marker: " + out);
+ assertFalse(out.contains("JsonView"), "Should not emit JSON tokens: " + out);
+ }
+
+ @Test
+ void renderSerdeSourceStub_json_emitsEmptyBodies() {
+ CppWriter writer = new CppWriter();
+ SerdeStub.renderSerdeSourceStub(writer, Protocol.JSON, "SubscribeToShardEventStream");
+ String out = writer.toString();
+ assertTrue(out.contains("SubscribeToShardEventStream::SubscribeToShardEventStream(JsonView jsonValue)"),
+ "Missing JSON ctor stub: " + out);
+ assertTrue(out.contains("JsonValue SubscribeToShardEventStream::Jsonize() const"),
+ "Missing Jsonize stub: " + out);
+ }
+}
From 83fca43a1a58fee9ac5ce25e17dfb648aa5d48ea Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Wed, 29 Jul 2026 13:56:17 -0400
Subject: [PATCH 12/18] Add EventStreamRenderer with handler generation
---
.../generators/model/ModelGenerator.java | 6 +-
.../model/renderers/EventStreamRenderer.java | 417 ++++++++++++++++++
.../model/EventStreamRendererTest.java | 119 +++++
3 files changed, 540 insertions(+), 2 deletions(-)
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
index 15ba3057476..b9bc248e29f 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/ModelGenerator.java
@@ -8,6 +8,7 @@
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeClassifier.ClassifiedShapes;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.EnumShapeRenderer;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.EventStreamRenderer;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.RequestRenderer;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.ResultRenderer;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.SubObjectRenderer;
@@ -74,8 +75,9 @@ private List buildRenderers(ClassifiedShapes classified) {
classified.results(), model, service, protocol,
namespace, exportMacro, smithyServiceName));
- // Future: event stream renderers will be registered here
- // renderers.add(new EventStreamRenderer(...));
+ renderers.add(new EventStreamRenderer(
+ classified.eventStreamHandlers(), model, service, protocol,
+ namespace, exportMacro, smithyServiceName));
return renderers;
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
new file mode 100644
index 00000000000..5d0e16fce88
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
@@ -0,0 +1,417 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.CppTypeMapper;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.SerdeStub;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeClassifier.EventStreamInfo;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeRenderer;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.shapes.MemberShape;
+import software.amazon.smithy.model.shapes.ServiceShape;
+import software.amazon.smithy.model.shapes.Shape;
+import software.amazon.smithy.model.shapes.StructureShape;
+import software.amazon.smithy.model.shapes.UnionShape;
+import software.amazon.smithy.model.traits.ErrorTrait;
+import software.amazon.smithy.model.traits.StreamingTrait;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Renders C++ event stream artifacts for response-side (simplex) streaming operations:
+ * the handler, initial response, and event stream union. Driven by the classifier's
+ * {@link EventStreamInfo} list. Event structure shapes themselves are generated
+ * elsewhere (as reachable sub-objects) and only referenced here.
+ *
+ * No protocol-specific serialization is emitted; payload (de)serialization points
+ * are protocol-agnostic TODO stubs via {@link SerdeStub}.
+ */
+public final class EventStreamRenderer implements ShapeRenderer {
+
+ private final List eventStreams;
+ private final Model model;
+ private final ServiceShape service;
+ private final Protocol protocol;
+ private final String namespace;
+ private final String exportMacro;
+ private final String smithyServiceName;
+
+ public EventStreamRenderer(List eventStreams, Model model, ServiceShape service,
+ Protocol protocol, String namespace, String exportMacro,
+ String smithyServiceName) {
+ this.eventStreams = eventStreams;
+ this.model = model;
+ this.service = service;
+ this.protocol = protocol;
+ this.namespace = namespace;
+ this.exportMacro = exportMacro;
+ this.smithyServiceName = smithyServiceName;
+ }
+
+ @Override
+ public void render(CppWriterDelegator writerDelegator) {
+ for (EventStreamInfo info : eventStreams) {
+ UnionShape union = findStreamingUnion(info.resultShape());
+ if (union == null) {
+ continue;
+ }
+ List events = new ArrayList<>();
+ List exceptions = new ArrayList<>();
+ partitionMembers(union, events, exceptions);
+
+ renderHandlerHeader(writerDelegator, info.operationName(), events);
+ renderHandlerSource(writerDelegator, info.operationName(), events);
+ renderInitialResponse(writerDelegator, info.operationName());
+ renderEventStreamUnion(writerDelegator, info.operationName(), union, events, exceptions);
+ }
+ }
+
+ /** Finds the @streaming union targeted by a member of the result structure. */
+ private UnionShape findStreamingUnion(StructureShape resultShape) {
+ for (MemberShape member : resultShape.getAllMembers().values()) {
+ Shape target = model.expectShape(member.getTarget());
+ if (target.isUnionShape() && target.hasTrait(StreamingTrait.class)) {
+ return target.asUnionShape().get();
+ }
+ }
+ return null;
+ }
+
+ /** Splits union members into events (non-exception) and exceptions, preserving order. */
+ private void partitionMembers(UnionShape union, List events, List exceptions) {
+ for (MemberShape member : union.getAllMembers().values()) {
+ Shape target = model.expectShape(member.getTarget());
+ if (target.hasTrait(ErrorTrait.class)) {
+ exceptions.add(member);
+ } else {
+ events.add(member);
+ }
+ }
+ }
+
+ /** The C++ event type identifier (event shape name), e.g. "AlphaEvent". */
+ private String eventShapeName(MemberShape member) {
+ return member.getTarget().getName();
+ }
+
+ /** The wire member key, e.g. "alpha". */
+ private String wireKey(MemberShape member) {
+ return member.getMemberName();
+ }
+
+ /** The EventType enum constant: wire member key uppercased, e.g. "ALPHA". */
+ private String enumConstant(MemberShape member) {
+ return wireKey(member).toUpperCase();
+ }
+
+ // ---- Handler ------------------------------------------------------------
+
+ private void renderHandlerHeader(CppWriterDelegator writerDelegator, String opName,
+ List events) {
+ String className = opName + "Handler";
+ String fileName = "include/aws/" + smithyServiceName + "/model/" + className + ".h";
+ writerDelegator.useFileWriter(fileName, writer -> {
+ writeCopyright(writer);
+ writer.write("#pragma once");
+ writer.write("#include ");
+ writer.write("#include ");
+ writer.write("#include ");
+ writer.write("#include ", smithyServiceName, namespace);
+ writer.write("#include ", smithyServiceName, namespace);
+ writer.write("#include ", smithyServiceName, opName);
+ for (MemberShape event : events) {
+ writer.write("#include ", smithyServiceName, eventShapeName(event));
+ }
+ writer.write("");
+ writer.writeNamespaceOpen("Aws");
+ writer.writeNamespaceOpen(namespace);
+ writer.writeNamespaceOpen("Model");
+
+ // EventType enum
+ StringBuilder enumBody = new StringBuilder("enum class ")
+ .append(opName).append("EventType { INITIAL_RESPONSE, ");
+ for (MemberShape event : events) {
+ enumBody.append(enumConstant(event)).append(", ");
+ }
+ enumBody.append("UNKNOWN };");
+ writer.write("$L", enumBody.toString());
+ writer.write("");
+
+ writer.openBlock("class $1L : public Aws::Utils::Event::EventStreamHandler {", "};", className, () -> {
+ // Callback typedefs
+ writer.write("typedef std::function $1LInitialResponseCallback;", opName);
+ writer.write("typedef std::function $1LInitialResponseCallbackEx;", opName);
+ for (MemberShape event : events) {
+ String ev = eventShapeName(event);
+ writer.write("typedef std::function $1LCallback;", ev);
+ }
+ writer.write("typedef std::function& error)> ErrorCallback;", namespace);
+ writer.write("");
+ writer.dedent();
+ writer.write("public:");
+ writer.indent();
+ writer.write("$1L $2L();", exportMacro, className);
+ writer.write("$1L $2L& operator=(const $2L&) = default;", exportMacro, className);
+ writer.write("$1L $2L(const $2L&) = default;", exportMacro, className);
+ writer.write("");
+ writer.write("$1L virtual void OnEvent() override;", exportMacro);
+ writer.write("");
+ writer.write("inline void SetInitialResponseCallbackEx(const $1LInitialResponseCallbackEx& callback) { m_onInitialResponse = callback; }", opName);
+ writer.openBlock("inline void SetInitialResponseCallback(const $1LInitialResponseCallback& noArgCallback) {", "}", opName, () -> {
+ writer.write("m_onInitialResponse = [noArgCallback](const $1LInitialResponse& rs, const Utils::Event::InitialResponseType) {", opName);
+ writer.write("return noArgCallback(rs);");
+ writer.write("};");
+ });
+ for (MemberShape event : events) {
+ String ev = eventShapeName(event);
+ writer.write("inline void Set$1LCallback(const $1LCallback& callback) { m_on$1L = callback; }", ev);
+ }
+ writer.write("inline void SetOnErrorCallback(const ErrorCallback& callback) { m_onError = callback; }");
+ writer.write("");
+ writer.write("inline $1LInitialResponseCallbackEx& GetInitialResponseCallbackEx() { return m_onInitialResponse; }", opName);
+ writer.write("");
+ writer.dedent();
+ writer.write("private:");
+ writer.indent();
+ writer.write("$1L void HandleEventInMessage();", exportMacro);
+ writer.write("$1L void HandleErrorInMessage();", exportMacro);
+ writer.write("$1L void MarshallError(const Aws::String& errorCode, const Aws::String& errorMessage);", exportMacro);
+ writer.write("");
+ writer.write("$1LInitialResponseCallbackEx m_onInitialResponse;", opName);
+ for (MemberShape event : events) {
+ String ev = eventShapeName(event);
+ writer.write("$1LCallback m_on$1L;", ev);
+ }
+ writer.write("ErrorCallback m_onError;");
+ });
+ writer.write("");
+
+ writer.writeNamespaceOpen(opName + "EventMapper");
+ writer.write("$1L $2LEventType Get$2LEventTypeForName(const Aws::String& name);", exportMacro, opName);
+ writer.write("");
+ writer.write("$1L Aws::String GetNameFor$2LEventType($2LEventType value);", exportMacro, opName);
+ writer.writeNamespaceClose(opName + "EventMapper");
+
+ writer.writeNamespaceClose("Model");
+ writer.writeNamespaceClose(namespace);
+ writer.writeNamespaceClose("Aws");
+ });
+ }
+
+ private void renderHandlerSource(CppWriterDelegator writerDelegator, String opName,
+ List events) {
+ String className = opName + "Handler";
+ String fileName = "source/model/" + className + ".cpp";
+ String tag = opName.toUpperCase() + "_HANDLER_CLASS_TAG";
+ writerDelegator.useFileWriter(fileName, writer -> {
+ writeCopyright(writer);
+ writer.write("#include ");
+ writer.write("#include ");
+ writer.write("#include ");
+ writer.write("#include ", smithyServiceName, namespace);
+ writer.write("#include ", smithyServiceName, opName);
+ writer.write("");
+ writer.write("using namespace Aws::$1L::Model;", namespace);
+ writer.write("using namespace Aws::Utils::Event;");
+ writer.write("");
+ writer.write("AWS_CORE_API extern const char MESSAGE_LOWER_CASE[];");
+ writer.write("AWS_CORE_API extern const char MESSAGE_CAMEL_CASE[];");
+ writer.write("");
+ writer.writeNamespaceOpen("Aws");
+ writer.writeNamespaceOpen(namespace);
+ writer.writeNamespaceOpen("Model");
+ writer.write("using namespace Aws::Client;");
+ writer.write("");
+ writer.write("static const char $1L[] = \"$2LHandler\";", tag, opName);
+ writer.write("");
+
+ // Constructor
+ writer.openBlock("$1L::$1L() : EventStreamHandler() {", "}", className, () -> {
+ writer.openBlock("m_onInitialResponse = [&](const $1LInitialResponse&, const Utils::Event::InitialResponseType eventType) {", "};", opName, () -> {
+ writer.write("AWS_LOGSTREAM_TRACE($1L, \"$2L initial response received from \" << (eventType == Utils::Event::InitialResponseType::ON_EVENT ? \"event\" : \"http headers\"));", tag, opName);
+ });
+ for (MemberShape event : events) {
+ String ev = eventShapeName(event);
+ writer.openBlock("m_on$1L = [&](const $1L&) {", "};", ev, () -> {
+ writer.write("AWS_LOGSTREAM_TRACE($1L, \"$2L received.\");", tag, ev);
+ });
+ }
+ writer.openBlock("m_onError = [&](const AWSError<$1LErrors>& error) {", "};", namespace, () -> {
+ writer.write("AWS_LOGSTREAM_TRACE($1L, \"$2L Errors received, \" << error);", tag, namespace);
+ });
+ });
+ writer.write("");
+
+ // OnEvent
+ writer.openBlock("void $1L::OnEvent() {", "}", className, () -> {
+ writer.openBlock("if (!*this) {", "}", () -> {
+ writer.write("AWSError error = EventStreamErrorsMapper::GetAwsErrorForEventStreamError(GetInternalError());");
+ writer.write("error.SetMessage(GetEventPayloadAsString());");
+ writer.write("m_onError(AWSError<$1LErrors>(error));", namespace);
+ writer.write("return;");
+ });
+ writer.write("const auto& headers = GetEventHeaders();");
+ writer.write("auto messageTypeHeaderIter = headers.find(MESSAGE_TYPE_HEADER);");
+ writer.openBlock("if (messageTypeHeaderIter == headers.end()) {", "}", () -> {
+ writer.write("AWS_LOGSTREAM_WARN($1L, \"Header: \" << MESSAGE_TYPE_HEADER << \" not found in the message.\");", tag);
+ writer.write("return;");
+ });
+ writer.openBlock("switch (Aws::Utils::Event::Message::GetMessageTypeForName(messageTypeHeaderIter->second.GetEventHeaderValueAsString())) {", "}", () -> {
+ writer.write("case Aws::Utils::Event::Message::MessageType::EVENT:");
+ writer.write("HandleEventInMessage();");
+ writer.write("break;");
+ writer.write("case Aws::Utils::Event::Message::MessageType::REQUEST_LEVEL_ERROR:");
+ writer.openBlock("case Aws::Utils::Event::Message::MessageType::REQUEST_LEVEL_EXCEPTION: {", "}", () -> {
+ writer.write("HandleErrorInMessage();");
+ writer.write("break;");
+ });
+ writer.write("default:");
+ writer.write("AWS_LOGSTREAM_WARN($1L, \"Unexpected message type: \" << messageTypeHeaderIter->second.GetEventHeaderValueAsString());", tag);
+ writer.write("break;");
+ });
+ });
+ writer.write("");
+
+ // HandleEventInMessage
+ writer.openBlock("void $1L::HandleEventInMessage() {", "}", className, () -> {
+ writer.write("const auto& headers = GetEventHeaders();");
+ writer.write("auto eventTypeHeaderIter = headers.find(EVENT_TYPE_HEADER);");
+ writer.openBlock("if (eventTypeHeaderIter == headers.end()) {", "}", () -> {
+ writer.write("AWS_LOGSTREAM_WARN($1L, \"Header: \" << EVENT_TYPE_HEADER << \" not found in the message.\");", tag);
+ writer.write("return;");
+ });
+ writer.openBlock("switch ($1LEventMapper::Get$1LEventTypeForName(eventTypeHeaderIter->second.GetEventHeaderValueAsString())) {", "}", opName, () -> {
+ writer.openBlock("case $1LEventType::INITIAL_RESPONSE: {", "}", opName, () -> {
+ writer.write("$1LInitialResponse event(GetEventHeadersAsHttpHeaders());", opName);
+ writer.write("m_onInitialResponse(event, Utils::Event::InitialResponseType::ON_EVENT);");
+ writer.write("break;");
+ });
+ for (MemberShape event : events) {
+ writer.openBlock("case $1LEventType::$2L: {", "}", opName, enumConstant(event), () -> {
+ SerdeStub.renderEventPayloadDecodeStub(writer, eventShapeName(event), "m_on" + eventShapeName(event));
+ writer.write("break;");
+ });
+ }
+ writer.write("default:");
+ writer.write("AWS_LOGSTREAM_WARN($1L, \"Unexpected event type: \" << eventTypeHeaderIter->second.GetEventHeaderValueAsString());", tag);
+ writer.write("break;");
+ });
+ });
+ writer.write("");
+
+ // HandleErrorInMessage
+ writer.openBlock("void $1L::HandleErrorInMessage() {", "}", className, () -> {
+ writer.write("const auto& headers = GetEventHeaders();");
+ writer.write("Aws::String errorCode;");
+ writer.write("Aws::String errorMessage;");
+ writer.write("auto errorHeaderIter = headers.find(ERROR_CODE_HEADER);");
+ writer.openBlock("if (errorHeaderIter == headers.end()) {", "}", () -> {
+ writer.write("errorHeaderIter = headers.find(EXCEPTION_TYPE_HEADER);");
+ writer.openBlock("if (errorHeaderIter == headers.end()) {", "}", () -> {
+ writer.write("AWS_LOGSTREAM_WARN($1L, \"Error type was not found in the event message.\");", tag);
+ writer.write("return;");
+ });
+ });
+ writer.write("errorCode = errorHeaderIter->second.GetEventHeaderValueAsString();");
+ writer.write("errorHeaderIter = headers.find(ERROR_MESSAGE_HEADER);");
+ writer.openBlock("if (errorHeaderIter == headers.end()) {", "}", () -> {
+ writer.write("// TODO: read error message from payload once protocol-specific serde lands");
+ SerdeStub.renderErrorPayloadParseStub(writer);
+ });
+ writer.openBlock("else {", "}", () -> {
+ writer.write("errorMessage = errorHeaderIter->second.GetEventHeaderValueAsString();");
+ });
+ writer.write("MarshallError(errorCode, errorMessage);");
+ });
+ writer.write("");
+
+ // MarshallError (verbatim from C2J)
+ writer.openBlock("void $1L::MarshallError(const Aws::String& errorCode, const Aws::String& errorMessage) {", "}", className, () -> {
+ writer.write("$1LErrorMarshaller errorMarshaller;", namespace);
+ writer.write("AWSError error;");
+ writer.openBlock("if (errorCode.empty()) {", "}", () -> {
+ writer.write("error = AWSError(CoreErrors::UNKNOWN, \"\", errorMessage, false);");
+ });
+ writer.openBlock("else {", "}", () -> {
+ writer.write("error = errorMarshaller.FindErrorByName(errorCode.c_str());");
+ writer.openBlock("if (error.GetErrorType() != CoreErrors::UNKNOWN) {", "}", () -> {
+ writer.write("AWS_LOGSTREAM_WARN($1L, \"Encountered AWSError '\" << errorCode.c_str() << \"': \" << errorMessage.c_str());", tag);
+ writer.write("error.SetExceptionName(errorCode);");
+ writer.write("error.SetMessage(errorMessage);");
+ });
+ writer.openBlock("else {", "}", () -> {
+ writer.write("AWS_LOGSTREAM_WARN($1L, \"Encountered Unknown AWSError '\" << errorCode.c_str() << \"': \" << errorMessage.c_str());", tag);
+ writer.write("error = AWSError(CoreErrors::UNKNOWN, errorCode, \"Unable to parse ExceptionName: \" + errorCode + \" Message: \" + errorMessage, false);");
+ });
+ });
+ writer.write("m_onError(AWSError<$1LErrors>(error));", namespace);
+ });
+ writer.write("");
+
+ // EventMapper
+ writer.writeNamespaceOpen(opName + "EventMapper");
+ writer.write("static const int INITIAL_RESPONSE_HASH = Aws::Utils::HashingUtils::HashString(\"initial-response\");");
+ for (MemberShape event : events) {
+ writer.write("static const int $1L_HASH = Aws::Utils::HashingUtils::HashString(\"$2L\");",
+ enumConstant(event), wireKey(event));
+ }
+ writer.write("");
+ writer.openBlock("$1LEventType Get$1LEventTypeForName(const Aws::String& name) {", "}", opName, () -> {
+ writer.write("int hashCode = Aws::Utils::HashingUtils::HashString(name.c_str());");
+ writer.openBlock("if (hashCode == INITIAL_RESPONSE_HASH) {", "}", () -> {
+ writer.write("return $1LEventType::INITIAL_RESPONSE;", opName);
+ });
+ for (MemberShape event : events) {
+ writer.openBlock("else if (hashCode == $1L_HASH) {", "}", enumConstant(event), () -> {
+ writer.write("return $1LEventType::$2L;", opName, enumConstant(event));
+ });
+ }
+ writer.write("return $1LEventType::UNKNOWN;", opName);
+ });
+ writer.write("");
+ writer.openBlock("Aws::String GetNameFor$1LEventType($1LEventType value) {", "}", opName, () -> {
+ writer.openBlock("switch (value) {", "}", () -> {
+ writer.write("case $1LEventType::INITIAL_RESPONSE:", opName);
+ writer.write("return \"initial-response\";");
+ for (MemberShape event : events) {
+ writer.write("case $1LEventType::$2L:", opName, enumConstant(event));
+ writer.write("return \"$1L\";", wireKey(event));
+ }
+ writer.write("default:");
+ writer.write("return \"Unknown\";");
+ });
+ });
+ writer.writeNamespaceClose(opName + "EventMapper");
+
+ writer.writeNamespaceClose("Model");
+ writer.writeNamespaceClose(namespace);
+ writer.writeNamespaceClose("Aws");
+ });
+ }
+
+ // ---- Placeholders filled in by Tasks 4 and 5 ----------------------------
+
+ private void renderInitialResponse(CppWriterDelegator writerDelegator, String opName) {
+ // Implemented in Task 4.
+ }
+
+ private void renderEventStreamUnion(CppWriterDelegator writerDelegator, String opName,
+ UnionShape union, List events,
+ List exceptions) {
+ // Implemented in Task 5.
+ }
+
+ private void writeCopyright(CppWriter writer) {
+ writer.write("/**");
+ writer.write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.");
+ writer.write(" * SPDX-License-Identifier: Apache-2.0.");
+ writer.write(" */");
+ writer.write("");
+ }
+}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
new file mode 100644
index 00000000000..7d570a6080d
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
@@ -0,0 +1,119 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.EventStreamRenderer;
+import org.junit.jupiter.api.Test;
+import software.amazon.smithy.build.MockManifest;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.shapes.ServiceShape;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.shapes.StringShape;
+import software.amazon.smithy.model.shapes.StructureShape;
+import software.amazon.smithy.model.shapes.UnionShape;
+import software.amazon.smithy.model.traits.ErrorTrait;
+import software.amazon.smithy.model.traits.StreamingTrait;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class EventStreamRendererTest {
+
+ private static Model twoEventModel() {
+ StringShape str = StringShape.builder().id("com.example#String").build();
+ StructureShape eventA = StructureShape.builder()
+ .id("com.example#AlphaEvent")
+ .addMember("data", str.getId())
+ .build();
+ StructureShape eventB = StructureShape.builder()
+ .id("com.example#BetaEvent")
+ .addMember("data", str.getId())
+ .build();
+ StructureShape exc = StructureShape.builder()
+ .id("com.example#BadException")
+ .addTrait(new ErrorTrait("client"))
+ .addMember("message", str.getId())
+ .build();
+ UnionShape stream = UnionShape.builder()
+ .id("com.example#MyStream")
+ .addTrait(new StreamingTrait())
+ .addMember("alpha", eventA.getId())
+ .addMember("beta", eventB.getId())
+ .addMember("badException", exc.getId())
+ .build();
+ StructureShape input = StructureShape.builder()
+ .id("com.example#DoStreamInput")
+ .addMember("name", str.getId())
+ .build();
+ StructureShape output = StructureShape.builder()
+ .id("com.example#DoStreamOutput")
+ .addMember("stream", stream.getId())
+ .build();
+ software.amazon.smithy.model.shapes.OperationShape op =
+ software.amazon.smithy.model.shapes.OperationShape.builder()
+ .id("com.example#DoStream")
+ .input(input.getId())
+ .output(output.getId())
+ .build();
+ ServiceShape service = ServiceShape.builder()
+ .id("com.example#Example")
+ .version("2024-01-01")
+ .addOperation(op.getId())
+ .build();
+ return Model.builder().addShapes(str, stream, eventA, eventB, exc, input, output, op, service).build();
+ }
+
+ private static String render(String fileSuffix) {
+ Model model = twoEventModel();
+ ServiceShape service = model.expectShape(ShapeId.from("com.example#Example"), ServiceShape.class);
+ MockManifest manifest = new MockManifest();
+ CppWriterDelegator delegator = new CppWriterDelegator(manifest);
+ EventStreamRenderer renderer = new EventStreamRenderer(
+ ShapeClassifier.classify(model, service).eventStreamHandlers(),
+ model, service, ProtocolResolver.resolve(service, model),
+ "Example", "AWS_EXAMPLE_API", "example");
+ renderer.render(delegator);
+ delegator.flushWriters();
+ return manifest.getFileString(
+ manifest.getFiles().stream()
+ .filter(p -> p.toString().endsWith(fileSuffix))
+ .findFirst().orElseThrow())
+ .orElseThrow();
+ }
+
+ @Test
+ void handlerHeader_hasEnumAndCallbacksPerEvent() {
+ String h = render("DoStreamHandler.h");
+ assertTrue(h.contains("enum class DoStreamEventType"), "Missing EventType enum: " + h);
+ assertTrue(h.contains("INITIAL_RESPONSE"), "Missing INITIAL_RESPONSE: " + h);
+ assertTrue(h.contains("ALPHA") && h.contains("BETA"), "Missing event enum constants: " + h);
+ assertTrue(h.contains("UNKNOWN"), "Missing UNKNOWN: " + h);
+ assertTrue(h.contains("SetAlphaEventCallback") && h.contains("SetBetaEventCallback"),
+ "Missing per-event setters: " + h);
+ assertTrue(h.contains("m_onAlphaEvent") && h.contains("m_onBetaEvent"),
+ "Missing per-event members: " + h);
+ assertTrue(h.contains("void OnEvent() override"), "Missing OnEvent override: " + h);
+ // Exception must NOT get its own callback
+ assertFalse(h.contains("SetBadExceptionCallback"), "Exception should not get a callback: " + h);
+ }
+
+ @Test
+ void handlerSource_eventCasesUseStubNoProtocolTokens() {
+ String c = render("DoStreamHandler.cpp");
+ assertTrue(c.contains("// TODO: protocol-specific event payload deserialization"),
+ "Missing event decode stub: " + c);
+ assertTrue(c.contains("m_onAlphaEvent(AlphaEvent{});"), "Missing alpha stub body: " + c);
+ assertTrue(c.contains("case DoStreamEventType::INITIAL_RESPONSE:"),
+ "Missing INITIAL_RESPONSE case: " + c);
+ assertTrue(c.contains("// TODO: protocol-specific error payload deserialization"),
+ "Missing error parse stub: " + c);
+ // EventMapper hash uses the wire member key, not the shape name
+ assertTrue(c.contains("HashString(\"alpha\")") && c.contains("HashString(\"beta\")"),
+ "EventMapper must key on wire member name: " + c);
+ assertFalse(c.contains("JsonValue"), "No protocol tokens in handler: " + c);
+ assertFalse(c.contains("Cbor"), "No protocol tokens in handler: " + c);
+ }
+}
From 3839e90c5b3dbd77c958c2196694449ddbd40c63 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Wed, 29 Jul 2026 13:59:24 -0400
Subject: [PATCH 13/18] Add InitialResponse generation to EventStreamRenderer
---
.../model/renderers/EventStreamRenderer.java | 69 ++++++++++++++++++-
.../model/EventStreamRendererTest.java | 18 +++++
2 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
index 5d0e16fce88..f2453c9cd0c 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
@@ -398,7 +398,74 @@ private void renderHandlerSource(CppWriterDelegator writerDelegator, String opNa
// ---- Placeholders filled in by Tasks 4 and 5 ----------------------------
private void renderInitialResponse(CppWriterDelegator writerDelegator, String opName) {
- // Implemented in Task 4.
+ String className = opName + "InitialResponse";
+
+ String headerFile = "include/aws/" + smithyServiceName + "/model/" + className + ".h";
+ writerDelegator.useFileWriter(headerFile, writer -> {
+ writeCopyright(writer);
+ writer.write("#pragma once");
+ writer.write("#include ");
+ writer.write("#include ", smithyServiceName, namespace);
+ writer.write("");
+ writer.writeNamespaceOpen("Aws");
+ renderSerdeForwardDeclarations(writer);
+ writer.writeNamespaceOpen(namespace);
+ writer.writeNamespaceOpen("Model");
+ writer.write("");
+ writer.openBlock("class $L {", "};", className, () -> {
+ writer.write("public:");
+ SerdeStub.renderHeaderDeclarations(writer, protocol, exportMacro, className);
+ writer.write("$1L $2L(const Http::HeaderValueCollection& responseHeaders);", exportMacro, className);
+ });
+ writer.write("");
+ writer.writeNamespaceClose("Model");
+ writer.writeNamespaceClose(namespace);
+ writer.writeNamespaceClose("Aws");
+ });
+
+ String sourceFile = "source/model/" + className + ".cpp";
+ writerDelegator.useFileWriter(sourceFile, writer -> {
+ writeCopyright(writer);
+ if (protocol.isJsonLike()) {
+ writer.write("#include ");
+ } else {
+ writer.write("#include ");
+ }
+ writer.write("#include ");
+ writer.write("#include ", smithyServiceName, className);
+ writer.write("");
+ writer.write("using namespace Aws::$1L::Model;", namespace);
+ if (protocol.isJsonLike()) {
+ writer.write("using namespace Aws::Utils::Json;");
+ } else {
+ writer.write("using namespace Aws::Utils::Xml;");
+ }
+ writer.write("using namespace Aws::Utils;");
+ writer.write("");
+ SerdeStub.renderSerdeSourceStub(writer, protocol, className);
+ writer.write("");
+ writer.openBlock("$1L::$1L(const Http::HeaderValueCollection& responseHeaders) {", "}", className, () -> {
+ writer.write("AWS_UNREFERENCED_PARAM(responseHeaders);");
+ });
+ });
+ }
+
+ /** Forward-declares the protocol serde types under Aws:: for header use. */
+ private void renderSerdeForwardDeclarations(CppWriter writer) {
+ if (protocol.isJsonLike()) {
+ writer.writeNamespaceOpen("Utils");
+ writer.writeNamespaceOpen("Json");
+ writer.write("class JsonValue;");
+ writer.write("class JsonView;");
+ writer.writeNamespaceClose("Json");
+ writer.writeNamespaceClose("Utils");
+ } else {
+ writer.writeNamespaceOpen("Utils");
+ writer.writeNamespaceOpen("Xml");
+ writer.write("class XmlNode;");
+ writer.writeNamespaceClose("Xml");
+ writer.writeNamespaceClose("Utils");
+ }
}
private void renderEventStreamUnion(CppWriterDelegator writerDelegator, String opName,
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
index 7d570a6080d..17fe21f163e 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
@@ -100,6 +100,24 @@ void handlerHeader_hasEnumAndCallbacksPerEvent() {
assertFalse(h.contains("SetBadExceptionCallback"), "Exception should not get a callback: " + h);
}
+ @Test
+ void initialResponseHeader_hasHeaderCollectionCtorAndSerdeDecls() {
+ String h = render("DoStreamInitialResponse.h");
+ assertTrue(h.contains("class DoStreamInitialResponse"), "Missing class: " + h);
+ assertTrue(h.contains("DoStreamInitialResponse(const Http::HeaderValueCollection& responseHeaders)"),
+ "Missing header-collection ctor: " + h);
+ // JSON serde declaration present (test model resolves to a JSON-like protocol)
+ assertTrue(h.contains("Jsonize") || h.contains("XmlNode") || h.contains("OutputToStream"),
+ "Missing serde declarations: " + h);
+ }
+
+ @Test
+ void initialResponseSource_hasHeaderCtorAndSerdeStub() {
+ String c = render("DoStreamInitialResponse.cpp");
+ assertTrue(c.contains("DoStreamInitialResponse::DoStreamInitialResponse(const Http::HeaderValueCollection&"),
+ "Missing header ctor definition: " + c);
+ }
+
@Test
void handlerSource_eventCasesUseStubNoProtocolTokens() {
String c = render("DoStreamHandler.cpp");
From e037dc91f935d51e930d66d46d35bd202af4db50 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Wed, 29 Jul 2026 14:04:15 -0400
Subject: [PATCH 14/18] Add EventStream union generation to EventStreamRenderer
---
.../model/renderers/EventStreamRenderer.java | 114 +++++++++++++++++-
.../model/EventStreamRendererTest.java | 14 ++-
2 files changed, 126 insertions(+), 2 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
index f2453c9cd0c..ea7bb847559 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
@@ -471,7 +471,119 @@ private void renderSerdeForwardDeclarations(CppWriter writer) {
private void renderEventStreamUnion(CppWriterDelegator writerDelegator, String opName,
UnionShape union, List events,
List exceptions) {
- // Implemented in Task 5.
+ String className = union.getId().getName();
+ String errorType = namespace + "Error";
+
+ String headerFile = "include/aws/" + smithyServiceName + "/model/" + className + ".h";
+ writerDelegator.useFileWriter(headerFile, writer -> {
+ writeCopyright(writer);
+ writer.write("#pragma once");
+ writer.write("#include ", smithyServiceName, namespace);
+ writer.write("#include ", smithyServiceName, namespace);
+ for (MemberShape event : events) {
+ writer.write("#include ", smithyServiceName, eventShapeName(event));
+ }
+ writer.write("");
+ writer.write("#include ");
+ writer.write("");
+ writer.writeNamespaceOpen("Aws");
+ renderSerdeForwardDeclarations(writer);
+ writer.writeNamespaceOpen(namespace);
+ writer.writeNamespaceOpen("Model");
+ writer.write("");
+ writer.openBlock("class $L {", "};", className, () -> {
+ writer.write("public:");
+ SerdeStub.renderHeaderDeclarations(writer, protocol, exportMacro, className);
+ writer.write("");
+ // Event member accessors, typed as their concrete shape.
+ for (MemberShape event : events) {
+ String cppType = CppTypeMapper.getCppType(model.expectShape(event.getTarget()), model);
+ renderShapeAccessor(writer, className, cppType, event.getMemberName());
+ }
+ // Exception member accessors, typed as Error.
+ for (MemberShape exc : exceptions) {
+ renderShapeAccessor(writer, className, errorType, exc.getMemberName());
+ }
+ writer.dedent();
+ writer.write("private:");
+ writer.indent();
+ // Data members
+ for (MemberShape event : events) {
+ String cppType = CppTypeMapper.getCppType(model.expectShape(event.getTarget()), model);
+ writer.write("$1L $2L;", cppType, "m_" + decapitalize(event.getMemberName()));
+ }
+ for (MemberShape exc : exceptions) {
+ writer.write("$1L $2L;", errorType, "m_" + decapitalize(exc.getMemberName()));
+ }
+ // HasBeenSet flags
+ for (MemberShape event : events) {
+ writer.write("bool $1LHasBeenSet = false;", "m_" + decapitalize(event.getMemberName()));
+ }
+ for (MemberShape exc : exceptions) {
+ writer.write("bool $1LHasBeenSet = false;", "m_" + decapitalize(exc.getMemberName()));
+ }
+ });
+ writer.write("");
+ writer.writeNamespaceClose("Model");
+ writer.writeNamespaceClose(namespace);
+ writer.writeNamespaceClose("Aws");
+ });
+
+ String sourceFile = "source/model/" + className + ".cpp";
+ writerDelegator.useFileWriter(sourceFile, writer -> {
+ writeCopyright(writer);
+ if (protocol.isJsonLike()) {
+ writer.write("#include ");
+ } else {
+ writer.write("#include ");
+ }
+ writer.write("#include ", smithyServiceName, className);
+ writer.write("");
+ writer.write("using namespace Aws::$1L::Model;", namespace);
+ if (protocol.isJsonLike()) {
+ writer.write("using namespace Aws::Utils::Json;");
+ } else {
+ writer.write("using namespace Aws::Utils::Xml;");
+ }
+ writer.write("using namespace Aws::Utils;");
+ writer.write("");
+ SerdeStub.renderSerdeSourceStub(writer, protocol, className);
+ });
+ }
+
+ /**
+ * Renders a Get/HasBeenSet/Set/With accessor block for a union member, typed by the
+ * given C++ type string. Used for both event members (concrete shape type) and
+ * exception members (the service error wrapper). Mirrors the templated setter style
+ * used by MemberRenderer.
+ */
+ private void renderShapeAccessor(CppWriter writer, String className, String cppType, String memberName) {
+ String getter = capitalize(memberName);
+ String field = "m_" + decapitalize(memberName);
+ String templateParam = getter + "T";
+ writer.write("///@{");
+ writer.write("");
+ writer.write("inline const $1L& Get$2L() const { return $3L; }", cppType, getter, field);
+ writer.write("inline bool $1LHasBeenSet() const { return $2LHasBeenSet; }", getter, field);
+ writer.write("template ", templateParam, cppType);
+ writer.openBlock("void Set$1L($2L&& value) {", "}", getter, templateParam, () -> {
+ writer.write("$1LHasBeenSet = true;", field);
+ writer.write("$1L = std::forward<$2L>(value);", field, templateParam);
+ });
+ writer.write("template ", templateParam, cppType);
+ writer.openBlock("$1L& With$2L($3L&& value) {", "}", className, getter, templateParam, () -> {
+ writer.write("Set$1L(std::forward<$2L>(value));", getter, templateParam);
+ writer.write("return *this;");
+ });
+ writer.write("///@}");
+ }
+
+ private static String capitalize(String s) {
+ return s.substring(0, 1).toUpperCase() + s.substring(1);
+ }
+
+ private static String decapitalize(String s) {
+ return s.substring(0, 1).toLowerCase() + s.substring(1);
}
private void writeCopyright(CppWriter writer) {
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
index 17fe21f163e..225bb0b6ab9 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
@@ -38,7 +38,7 @@ private static Model twoEventModel() {
.addMember("message", str.getId())
.build();
UnionShape stream = UnionShape.builder()
- .id("com.example#MyStream")
+ .id("com.example#MyStreamEventStream")
.addTrait(new StreamingTrait())
.addMember("alpha", eventA.getId())
.addMember("beta", eventB.getId())
@@ -100,6 +100,18 @@ void handlerHeader_hasEnumAndCallbacksPerEvent() {
assertFalse(h.contains("SetBadExceptionCallback"), "Exception should not get a callback: " + h);
}
+ @Test
+ void eventStreamUnionHeader_typesEventAndExceptionMembers() {
+ String h = render("MyStreamEventStream.h");
+ // NOTE: file name derives from the UNION shape name (MyStream), not the operation.
+ assertTrue(h.contains("class MyStreamEventStream"), "Missing union class: " + h);
+ // Event member typed as its concrete shape
+ assertTrue(h.contains("const AlphaEvent& GetAlpha()") || h.contains("GetAlpha"),
+ "Missing event accessor: " + h);
+ // Exception member typed as Error
+ assertTrue(h.contains("ExampleError"), "Exception members must be typed as ExampleError: " + h);
+ }
+
@Test
void initialResponseHeader_hasHeaderCollectionCtorAndSerdeDecls() {
String h = render("DoStreamInitialResponse.h");
From 767d0b8028795cbff741a048f7e8be170a2e9429 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Wed, 29 Jul 2026 14:07:23 -0400
Subject: [PATCH 15/18] Add event-stream augmentation to RequestRenderer
---
.../model/renderers/RequestRenderer.java | 50 ++++++++
.../generators/model/RequestRendererTest.java | 118 ++++++++++++++++++
2 files changed, 168 insertions(+)
create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java
index 888880b4da7..1089ab11615 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java
@@ -9,6 +9,7 @@
import com.amazonaws.util.awsclientsmithygenerator.generators.model.CppTypeMapper;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.MemberRenderer;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeClassifier;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeClassifier.RequestInfo;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeRenderer;
import software.amazon.smithy.model.Model;
@@ -82,6 +83,13 @@ private void renderHeader(CppWriterDelegator writerDelegator,
writer.write("");
writer.write("#include ");
}
+
+ boolean streamingResponse = ShapeClassifier.isEventStreamResponseOperation(operation, model);
+ boolean streamingRequest = ShapeClassifier.isEventStreamRequestOperation(operation, model);
+ if (streamingResponse) {
+ writer.write("#include ");
+ writer.write("#include ", smithyServiceName, operation.getId().getName());
+ }
writer.write("");
writer.writeNamespaceOpen("Aws");
@@ -104,10 +112,47 @@ private void renderHeader(CppWriterDelegator writerDelegator,
writer.write("inline virtual const char* GetServiceRequestName() const override { return \"$L\"; }",
operation.getId().getName());
writer.write("");
+ if (streamingRequest) {
+ writer.write("inline virtual bool IsEventStreamRequest() const override { return true; }");
+ }
+ if (streamingResponse) {
+ writer.write("inline virtual bool HasEventStreamResponse() const override { return true; }");
+ }
writer.write("$L Aws::String SerializePayload() const override;", exportMacro);
writer.write("");
writer.write("$L Aws::Http::HeaderValueCollection GetRequestSpecificHeaders() const override;", exportMacro);
+ if (streamingResponse) {
+ String handlerType = operation.getId().getName() + "Handler";
+ writer.write("");
+ writer.write("/**");
+ writer.write(" * Underlying Event Stream Decoder.");
+ writer.write(" */");
+ writer.write("inline Aws::Utils::Event::EventStreamDecoder& GetEventStreamDecoder() { return m_decoder; }");
+ writer.write("");
+ writer.write("/**");
+ writer.write(" * Underlying Event Stream Handler which is used to define callback functions.");
+ writer.write(" */");
+ writer.write("inline $1L& GetEventStreamHandler() { return m_handler; }", handlerType);
+ writer.write("");
+ writer.write("/**");
+ writer.write(" * Underlying Event Stream Handler which is used to define callback functions.");
+ writer.write(" */");
+ writer.openBlock("inline void SetEventStreamHandler(const $1L& value) {", "}", handlerType, () -> {
+ writer.write("m_handler = value;");
+ writer.write("m_decoder.ResetEventStreamHandler(&m_handler);");
+ });
+ writer.write("");
+ writer.write("/**");
+ writer.write(" * Underlying Event Stream Handler which is used to define callback functions.");
+ writer.write(" */");
+ writer.openBlock("inline $1LRequest& WithEventStreamHandler(const $2L& value) {", "}",
+ operation.getId().getName(), handlerType, () -> {
+ writer.write("SetEventStreamHandler(value);");
+ writer.write("return *this;");
+ });
+ }
+
if (hasEndpointContextParams(operation, shape)) {
writer.write("");
writer.write("/**");
@@ -123,6 +168,11 @@ private void renderHeader(CppWriterDelegator writerDelegator,
writer.dedent();
writer.write("private:");
writer.indent();
+ if (streamingResponse) {
+ String handlerType = operation.getId().getName() + "Handler";
+ writer.write("$1L m_handler;", handlerType);
+ writer.write("Aws::Utils::Event::EventStreamDecoder m_decoder{Utils::Event::EventStreamDecoder(&m_handler)};");
+ }
MemberRenderer.renderPrivateSection(writer, shape, model);
}
});
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
new file mode 100644
index 00000000000..fda307537e6
--- /dev/null
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+package com.amazonaws.util.awsclientsmithygenerator.generators.model;
+
+import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.renderers.RequestRenderer;
+import org.junit.jupiter.api.Test;
+import software.amazon.smithy.build.MockManifest;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.shapes.OperationShape;
+import software.amazon.smithy.model.shapes.ServiceShape;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.shapes.StringShape;
+import software.amazon.smithy.model.shapes.StructureShape;
+import software.amazon.smithy.model.shapes.UnionShape;
+import software.amazon.smithy.model.traits.StreamingTrait;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class RequestRendererTest {
+
+ private static Model streamingModel(boolean inputStreams, boolean outputStreams) {
+ StringShape str = StringShape.builder().id("com.example#String").build();
+ StructureShape eventA = StructureShape.builder()
+ .id("com.example#AlphaEvent")
+ .addMember("data", str.getId())
+ .build();
+ UnionShape stream = UnionShape.builder()
+ .id("com.example#MyStreamEventStream")
+ .addTrait(new StreamingTrait())
+ .addMember("alpha", eventA.getId())
+ .build();
+
+ StructureShape.Builder inputBuilder = StructureShape.builder().id("com.example#DoStreamInput");
+ inputBuilder.addMember("name", str.getId());
+ if (inputStreams) {
+ inputBuilder.addMember("body", stream.getId());
+ }
+ StructureShape input = inputBuilder.build();
+
+ StructureShape.Builder outputBuilder = StructureShape.builder().id("com.example#DoStreamOutput");
+ if (outputStreams) {
+ outputBuilder.addMember("stream", stream.getId());
+ } else {
+ outputBuilder.addMember("result", str.getId());
+ }
+ StructureShape output = outputBuilder.build();
+
+ OperationShape op = OperationShape.builder()
+ .id("com.example#DoStream")
+ .input(input.getId())
+ .output(output.getId())
+ .build();
+ ServiceShape service = ServiceShape.builder()
+ .id("com.example#Example")
+ .version("2024-01-01")
+ .addOperation(op.getId())
+ .build();
+ return Model.builder().addShapes(str, eventA, stream, input, output, op, service).build();
+ }
+
+ private static String renderRequestHeaderForStreamingOp(boolean inputStreams, boolean outputStreams) {
+ Model model = streamingModel(inputStreams, outputStreams);
+ ServiceShape service = model.expectShape(ShapeId.from("com.example#Example"), ServiceShape.class);
+ MockManifest manifest = new MockManifest();
+ CppWriterDelegator delegator = new CppWriterDelegator(manifest);
+ RequestRenderer renderer = new RequestRenderer(
+ ShapeClassifier.classify(model, service).requests(),
+ model, service, ProtocolResolver.resolve(service, model),
+ "Example", "AWS_EXAMPLE_API", "example");
+ renderer.render(delegator);
+ delegator.flushWriters();
+ return manifest.getFileString(
+ manifest.getFiles().stream()
+ .filter(p -> p.toString().endsWith("DoStreamRequest.h"))
+ .findFirst().orElseThrow())
+ .orElseThrow();
+ }
+
+ @Test
+ void streamingResponseRequest_hasEventStreamAugmentation() {
+ // Model: operation with streaming OUTPUT only (like SubscribeToShard / ConverseStream)
+ String h = renderRequestHeaderForStreamingOp(false, true);
+ assertTrue(h.contains("bool HasEventStreamResponse() const override { return true; }"),
+ "Missing HasEventStreamResponse: " + h);
+ assertTrue(h.contains("GetEventStreamDecoder()"), "Missing decoder accessor: " + h);
+ assertTrue(h.contains("GetEventStreamHandler()"), "Missing handler accessor: " + h);
+ assertTrue(h.contains("SetEventStreamHandler") && h.contains("WithEventStreamHandler"),
+ "Missing handler setters: " + h);
+ assertTrue(h.contains("m_decoder.ResetEventStreamHandler(&m_handler)"),
+ "Missing decoder reset in setter: " + h);
+ assertTrue(h.contains("EventStreamDecoder m_decoder"), "Missing decoder member: " + h);
+ assertTrue(h.contains("#include "),
+ "Missing decoder include: " + h);
+ assertFalse(h.contains("IsEventStreamRequest"),
+ "Response-only op must not declare IsEventStreamRequest: " + h);
+ }
+
+ @Test
+ void bidirectionalRequest_alsoHasIsEventStreamRequest() {
+ String h = renderRequestHeaderForStreamingOp(true, true);
+ assertTrue(h.contains("bool IsEventStreamRequest() const override { return true; }"),
+ "Missing IsEventStreamRequest for bidirectional op: " + h);
+ assertTrue(h.contains("bool HasEventStreamResponse() const override { return true; }"),
+ "Bidirectional op must also declare HasEventStreamResponse: " + h);
+ }
+
+ @Test
+ void nonStreamingRequest_hasNoEventStreamAugmentation() {
+ String h = renderRequestHeaderForStreamingOp(false, false);
+ assertFalse(h.contains("HasEventStreamResponse"), "Non-streaming request must be clean: " + h);
+ assertFalse(h.contains("GetEventStreamDecoder"), "Non-streaming request must be clean: " + h);
+ assertFalse(h.contains("IsEventStreamRequest"), "Non-streaming request must be clean: " + h);
+ }
+}
From b9e58f563029187897593838783e524a5d81b157 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Thu, 30 Jul 2026 13:33:45 -0400
Subject: [PATCH 16/18] Generate event stream model artifacts in Smithy codegen
Add response-side event stream generation (handler, initial response, and
event stream union) plus request augmentation for simplex streaming
operations, closing the gap versus C2J for kinesis SubscribeToShard and
bedrock-runtime streaming operations.
- Add EventStreamRenderer producing the handler and initial-response (with
serde stubs) and the header-only event stream union, matching C2J which
emits no .cpp for the union.
- Augment RequestRenderer with HasEventStreamResponse/IsEventStreamRequest
and the decoder/handler accessors, placing members to match C2J ordering.
- Add event stream detection helpers to ShapeClassifier and event-stream
serde stub helpers to SerdeStub.
- Restore handler and union documentation to match C2J.
- Deduplicate renderClassDocComment into MemberRenderer and emit the file
copyright header once from CppWriterDelegator.
---
.../BaseCompilationTestGenerator.java | 8 +--
.../generators/BaseHeaderGenerator.java | 8 +--
.../generators/BaseTraitsGenerator.java | 8 +--
.../generators/CppWriter.java | 13 ++++
.../generators/CppWriterDelegator.java | 5 +-
.../generators/model/EnumRenderer.java | 10 ---
.../generators/model/MemberRenderer.java | 25 +++++++
.../generators/model/SerdeStub.java | 24 +++++++
.../model/renderers/EventStreamRenderer.java | 68 ++++++++-----------
.../model/renderers/RequestRenderer.java | 35 +++-------
.../model/renderers/ResultRenderer.java | 27 +-------
.../model/renderers/SubObjectRenderer.java | 27 +-------
.../waiters/WaiterEmptyHeaderGenerator.java | 5 --
.../model/EventStreamRendererTest.java | 35 +++++++++-
.../generators/model/RequestRendererTest.java | 10 +++
15 files changed, 156 insertions(+), 152 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseCompilationTestGenerator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseCompilationTestGenerator.java
index 210be2c5c65..007b54bf32c 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseCompilationTestGenerator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseCompilationTestGenerator.java
@@ -58,12 +58,8 @@ private void render(CppWriter writer) {
}
private void writeHeader(CppWriter writer, String serviceName) {
- writer.write("/**")
- .write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.")
- .write(" * SPDX-License-Identifier: Apache-2.0.")
- .write(" */")
- .write("")
- .write("// Header compilation test for " + serviceName + " " + getTestType().toLowerCase() + " headers")
+ // Copyright/SPDX header is emitted by CppWriterDelegator when the file is created.
+ writer.write("// Header compilation test for " + serviceName + " " + getTestType().toLowerCase() + " headers")
.write("// " + getTestDescription())
.write("");
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseHeaderGenerator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseHeaderGenerator.java
index e549174a829..8ea6eb98897 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseHeaderGenerator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseHeaderGenerator.java
@@ -39,12 +39,8 @@ public final void render(CppWriter writer) {
}
private void writeHeader(CppWriter writer) {
- writer.write("/**")
- .write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.")
- .write(" * SPDX-License-Identifier: Apache-2.0.")
- .write(" */")
- .write("")
- .write("#pragma once");
+ // Copyright/SPDX header is emitted by CppWriterDelegator when the file is created.
+ writer.write("#pragma once");
}
private void writeNamespaceOpen(CppWriter writer, String serviceName) {
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseTraitsGenerator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseTraitsGenerator.java
index 7dfc99f9ae6..09696b0c88a 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseTraitsGenerator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/BaseTraitsGenerator.java
@@ -46,11 +46,7 @@ public final void write() {
}
private void writeHeader(CppWriter writer) {
- writer.write("/**")
- .write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.")
- .write(" * SPDX-License-Identifier: Apache-2.0.")
- .write(" */")
- .write("")
- .write("#pragma once");
+ // Copyright/SPDX header is emitted by CppWriterDelegator when the file is created.
+ writer.write("#pragma once");
}
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriter.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriter.java
index bf57c4ad3b8..6ddbed5c14b 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriter.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriter.java
@@ -34,6 +34,19 @@ public CppWriter writeInclude(String header) {
write("#include <$L>", header);
return this;
}
+
+ /**
+ * Writes the standard Amazon copyright / SPDX license header that begins every
+ * generated file, followed by a blank line.
+ */
+ public CppWriter writeCopyright() {
+ write("/**");
+ write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.");
+ write(" * SPDX-License-Identifier: Apache-2.0.");
+ write(" */");
+ write("");
+ return this;
+ }
public CppWriter writeNamespaceOpen(String namespace) {
openBlock("namespace $L\n{", namespace);
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java
index 63b060d0bde..c162427b684 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java
@@ -18,7 +18,10 @@ public CppWriterDelegator(FileManifest fileManifest) {
}
public void useFileWriter(String filename, Consumer writerConsumer) {
- CppWriter writer = writers.computeIfAbsent(filename, k -> new CppWriter());
+ // Every generated file begins with the standard copyright/SPDX header. Emit it once,
+ // when the writer is first created, so callers don't repeat it (and reopening a file
+ // for appending does not duplicate it).
+ CppWriter writer = writers.computeIfAbsent(filename, k -> new CppWriter().writeCopyright());
writerConsumer.accept(writer);
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRenderer.java
index 0544261e7ed..c04466d3a7c 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EnumRenderer.java
@@ -45,11 +45,6 @@ public static void renderHeader(CppWriter writer, Shape enumShape, String servic
String enumName = enumShape.getId().getName();
List values = getEnumValues(enumShape);
- writer.write("/**");
- writer.write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.");
- writer.write(" * SPDX-License-Identifier: Apache-2.0.");
- writer.write(" */");
- writer.write("");
writer.write("#pragma once");
writer.write("#include ");
writer.write("#include ",
@@ -107,11 +102,6 @@ public static void renderSource(CppWriter writer, Shape enumShape, String servic
List values = getEnumValues(enumShape);
List wireValues = getEnumWireValues(enumShape);
- writer.write("/**");
- writer.write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.");
- writer.write(" * SPDX-License-Identifier: Apache-2.0.");
- writer.write(" */");
- writer.write("");
writer.write("#include ", projectName, enumName);
writer.write("#include ");
writer.write("#include ");
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java
index 5b297e05052..3123f563cba 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java
@@ -248,6 +248,31 @@ public static void writeDocComment(CppWriter writer, String doc) {
writer.write(" */");
}
+ /**
+ * Renders the class-level documentation comment for a shape: its {@code @documentation}
+ * text followed by a "See Also" link to the AWS API reference. Emits an empty doc comment
+ * ({@code /** *}{@code /}) when the shape carries no documentation. Shared by the
+ * request, result, sub-object, and event-stream union renderers.
+ *
+ * @param writer the CppWriter to write to
+ * @param shape the shape whose class doc to render (structure or union)
+ * @param smithyServiceName the service name used in the reference URL (e.g. "kinesis")
+ * @param version the service API version used in the reference URL
+ */
+ public static void renderClassDocComment(CppWriter writer, Shape shape,
+ String smithyServiceName, String version) {
+ if (shape.getTrait(DocumentationTrait.class).isPresent()) {
+ String docText = collapseWhitespace(shape.getTrait(DocumentationTrait.class).get().getValue());
+ String seeAlso = String.format(
+ "See Also:
AWS API Reference
",
+ smithyServiceName, version, shape.getId().getName());
+ writeDocComment(writer, docText + seeAlso);
+ } else {
+ writer.write("/**");
+ writer.write(" */");
+ }
+ }
+
public static String collapseWhitespace(String text) {
if (text == null) {
return null;
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java
index d383740f107..0efd09b7dc2 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/SerdeStub.java
@@ -20,20 +20,44 @@ private SerdeStub() {
public static void renderHeaderDeclarations(CppWriter writer, Protocol protocol,
String exportMacro, String className) {
+ renderHeaderDeclarations(writer, protocol, exportMacro, className, null);
+ }
+
+ /**
+ * Emits the protocol-specific serde method declarations for a class.
+ *
+ * @param beforeSerializeMethod optional hook run after the (de)serialization constructor
+ * and {@code operator=} declarations but before the serialize method
+ * ({@code Jsonize}/{@code AddToNode}/{@code OutputToStream}). Used to inject
+ * additional constructors (e.g. the event-stream initial-response header ctor)
+ * at the mainline position. Ignored when {@code null}.
+ */
+ public static void renderHeaderDeclarations(CppWriter writer, Protocol protocol,
+ String exportMacro, String className,
+ Runnable beforeSerializeMethod) {
if (protocol.isJsonLike()) {
writer.write("$L $L() = default;", exportMacro, className);
writer.write("$L $L(Aws::Utils::Json::JsonView jsonValue);", exportMacro, className);
writer.write("$L $L& operator=(Aws::Utils::Json::JsonView jsonValue);", exportMacro, className);
+ if (beforeSerializeMethod != null) {
+ beforeSerializeMethod.run();
+ }
writer.write("$L Aws::Utils::Json::JsonValue Jsonize() const;", exportMacro);
} else if (protocol == Protocol.REST_XML) {
writer.write("$L $L() = default;", exportMacro, className);
writer.write("$L $L(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
writer.write("$L $L& operator=(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
+ if (beforeSerializeMethod != null) {
+ beforeSerializeMethod.run();
+ }
writer.write("$L void AddToNode(Aws::Utils::Xml::XmlNode& parentNode) const;", exportMacro);
} else if (protocol == Protocol.QUERY_XML || protocol == Protocol.EC2) {
writer.write("$L $L() = default;", exportMacro, className);
writer.write("$L $L(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
writer.write("$L $L& operator=(const Aws::Utils::Xml::XmlNode& xmlNode);", exportMacro, className);
+ if (beforeSerializeMethod != null) {
+ beforeSerializeMethod.run();
+ }
writer.write("$L void OutputToStream(Aws::OStream& ostream, const char* location, unsigned index, const char* locationValue) const;", exportMacro);
writer.write("$L void OutputToStream(Aws::OStream& ostream, const char* location) const;", exportMacro);
} else {
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
index ea7bb847559..4d30d1aeb9f 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
@@ -7,6 +7,7 @@
import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter;
import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.CppTypeMapper;
+import com.amazonaws.util.awsclientsmithygenerator.generators.model.MemberRenderer;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ProtocolResolver.Protocol;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.SerdeStub;
import com.amazonaws.util.awsclientsmithygenerator.generators.model.ShapeClassifier.EventStreamInfo;
@@ -17,6 +18,7 @@
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.shapes.UnionShape;
+import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.model.traits.ErrorTrait;
import software.amazon.smithy.model.traits.StreamingTrait;
@@ -117,7 +119,6 @@ private void renderHandlerHeader(CppWriterDelegator writerDelegator, String opNa
String className = opName + "Handler";
String fileName = "include/aws/" + smithyServiceName + "/model/" + className + ".h";
writerDelegator.useFileWriter(fileName, writer -> {
- writeCopyright(writer);
writer.write("#pragma once");
writer.write("#include ");
writer.write("#include ");
@@ -162,12 +163,23 @@ private void renderHandlerHeader(CppWriterDelegator writerDelegator, String opNa
writer.write("");
writer.write("$1L virtual void OnEvent() override;", exportMacro);
writer.write("");
+ writer.write("///@{");
+ writer.write("/**");
+ writer.write(" * Sets an initial response callback. This callback gets called on the initial $1L Operation response.", opName);
+ writer.write(" * This can be either \"initial-response\" decoded event frame or decoded HTTP headers received on connection.");
+ writer.write(" * This callback may get called more than once (i.e. on connection headers received and then on the initial-response event received).");
+ writer.write(" * @param callback");
+ writer.write(" */");
writer.write("inline void SetInitialResponseCallbackEx(const $1LInitialResponseCallbackEx& callback) { m_onInitialResponse = callback; }", opName);
+ writer.write("/**");
+ writer.write(" * Sets an initial response callback (a legacy one that does not distinguish whether response originates from headers or from the event).");
+ writer.write(" */");
writer.openBlock("inline void SetInitialResponseCallback(const $1LInitialResponseCallback& noArgCallback) {", "}", opName, () -> {
writer.write("m_onInitialResponse = [noArgCallback](const $1LInitialResponse& rs, const Utils::Event::InitialResponseType) {", opName);
writer.write("return noArgCallback(rs);");
writer.write("};");
});
+ writer.write("///@}");
for (MemberShape event : events) {
String ev = eventShapeName(event);
writer.write("inline void Set$1LCallback(const $1LCallback& callback) { m_on$1L = callback; }", ev);
@@ -210,7 +222,6 @@ private void renderHandlerSource(CppWriterDelegator writerDelegator, String opNa
String fileName = "source/model/" + className + ".cpp";
String tag = opName.toUpperCase() + "_HANDLER_CLASS_TAG";
writerDelegator.useFileWriter(fileName, writer -> {
- writeCopyright(writer);
writer.write("#include ");
writer.write("#include ");
writer.write("#include ");
@@ -402,7 +413,6 @@ private void renderInitialResponse(CppWriterDelegator writerDelegator, String op
String headerFile = "include/aws/" + smithyServiceName + "/model/" + className + ".h";
writerDelegator.useFileWriter(headerFile, writer -> {
- writeCopyright(writer);
writer.write("#pragma once");
writer.write("#include ");
writer.write("#include ", smithyServiceName, namespace);
@@ -414,8 +424,9 @@ private void renderInitialResponse(CppWriterDelegator writerDelegator, String op
writer.write("");
writer.openBlock("class $L {", "};", className, () -> {
writer.write("public:");
- SerdeStub.renderHeaderDeclarations(writer, protocol, exportMacro, className);
- writer.write("$1L $2L(const Http::HeaderValueCollection& responseHeaders);", exportMacro, className);
+ // The header-collection ctor sits before the serialize method (mainline ordering).
+ SerdeStub.renderHeaderDeclarations(writer, protocol, exportMacro, className,
+ () -> writer.write("$1L $2L(const Http::HeaderValueCollection& responseHeaders);", exportMacro, className));
});
writer.write("");
writer.writeNamespaceClose("Model");
@@ -425,7 +436,6 @@ private void renderInitialResponse(CppWriterDelegator writerDelegator, String op
String sourceFile = "source/model/" + className + ".cpp";
writerDelegator.useFileWriter(sourceFile, writer -> {
- writeCopyright(writer);
if (protocol.isJsonLike()) {
writer.write("#include ");
} else {
@@ -476,7 +486,6 @@ private void renderEventStreamUnion(CppWriterDelegator writerDelegator, String o
String headerFile = "include/aws/" + smithyServiceName + "/model/" + className + ".h";
writerDelegator.useFileWriter(headerFile, writer -> {
- writeCopyright(writer);
writer.write("#pragma once");
writer.write("#include ", smithyServiceName, namespace);
writer.write("#include ", smithyServiceName, namespace);
@@ -491,6 +500,7 @@ private void renderEventStreamUnion(CppWriterDelegator writerDelegator, String o
writer.writeNamespaceOpen(namespace);
writer.writeNamespaceOpen("Model");
writer.write("");
+ MemberRenderer.renderClassDocComment(writer, union, smithyServiceName, service.getVersion());
writer.openBlock("class $L {", "};", className, () -> {
writer.write("public:");
SerdeStub.renderHeaderDeclarations(writer, protocol, exportMacro, className);
@@ -498,11 +508,11 @@ private void renderEventStreamUnion(CppWriterDelegator writerDelegator, String o
// Event member accessors, typed as their concrete shape.
for (MemberShape event : events) {
String cppType = CppTypeMapper.getCppType(model.expectShape(event.getTarget()), model);
- renderShapeAccessor(writer, className, cppType, event.getMemberName());
+ renderShapeAccessor(writer, className, cppType, event.getMemberName(), event);
}
// Exception member accessors, typed as Error.
for (MemberShape exc : exceptions) {
- renderShapeAccessor(writer, className, errorType, exc.getMemberName());
+ renderShapeAccessor(writer, className, errorType, exc.getMemberName(), exc);
}
writer.dedent();
writer.write("private:");
@@ -529,26 +539,9 @@ private void renderEventStreamUnion(CppWriterDelegator writerDelegator, String o
writer.writeNamespaceClose("Aws");
});
- String sourceFile = "source/model/" + className + ".cpp";
- writerDelegator.useFileWriter(sourceFile, writer -> {
- writeCopyright(writer);
- if (protocol.isJsonLike()) {
- writer.write("#include ");
- } else {
- writer.write("#include ");
- }
- writer.write("#include ", smithyServiceName, className);
- writer.write("");
- writer.write("using namespace Aws::$1L::Model;", namespace);
- if (protocol.isJsonLike()) {
- writer.write("using namespace Aws::Utils::Json;");
- } else {
- writer.write("using namespace Aws::Utils::Xml;");
- }
- writer.write("using namespace Aws::Utils;");
- writer.write("");
- SerdeStub.renderSerdeSourceStub(writer, protocol, className);
- });
+ // C2J generates the event stream union as a header-only type: the serde methods are
+ // declared but never defined or referenced (the handler dispatches on the concrete
+ // event shape, not the union). No .cpp is emitted, to match mainline parity.
}
/**
@@ -557,12 +550,18 @@ private void renderEventStreamUnion(CppWriterDelegator writerDelegator, String o
* exception members (the service error wrapper). Mirrors the templated setter style
* used by MemberRenderer.
*/
- private void renderShapeAccessor(CppWriter writer, String className, String cppType, String memberName) {
+ private void renderShapeAccessor(CppWriter writer, String className, String cppType, String memberName,
+ MemberShape member) {
String getter = capitalize(memberName);
String field = "m_" + decapitalize(memberName);
String templateParam = getter + "T";
writer.write("///@{");
- writer.write("");
+ if (member.getTrait(DocumentationTrait.class).isPresent()) {
+ MemberRenderer.writeDocComment(writer,
+ MemberRenderer.collapseWhitespace(member.getTrait(DocumentationTrait.class).get().getValue()));
+ } else {
+ writer.write("");
+ }
writer.write("inline const $1L& Get$2L() const { return $3L; }", cppType, getter, field);
writer.write("inline bool $1LHasBeenSet() const { return $2LHasBeenSet; }", getter, field);
writer.write("template ", templateParam, cppType);
@@ -586,11 +585,4 @@ private static String decapitalize(String s) {
return s.substring(0, 1).toLowerCase() + s.substring(1);
}
- private void writeCopyright(CppWriter writer) {
- writer.write("/**");
- writer.write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.");
- writer.write(" * SPDX-License-Identifier: Apache-2.0.");
- writer.write(" */");
- writer.write("");
- }
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java
index 1089ab11615..44e02264a0c 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/RequestRenderer.java
@@ -17,7 +17,6 @@
import software.amazon.smithy.model.shapes.OperationShape;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.StructureShape;
-import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.rulesengine.traits.ContextParamTrait;
import software.amazon.smithy.rulesengine.traits.StaticContextParamsTrait;
@@ -64,7 +63,6 @@ private void renderHeader(CppWriterDelegator writerDelegator,
String className = operation.getId().getName() + "Request";
String fileName = "include/aws/" + smithyServiceName + "/model/" + className + ".h";
writerDelegator.useFileWriter(fileName, writer -> {
- writeCopyright(writer);
writer.write("#pragma once");
Set includes = new TreeSet<>();
@@ -97,7 +95,7 @@ private void renderHeader(CppWriterDelegator writerDelegator,
writer.writeNamespaceOpen("Model");
writer.write("");
- renderClassDocComment(writer, shape);
+ MemberRenderer.renderClassDocComment(writer, shape, smithyServiceName, service.getVersion());
String baseClass = namespace + "Request";
writer.openBlock("class $L : public $L {", "};", className, baseClass, () -> {
@@ -169,11 +167,17 @@ private void renderHeader(CppWriterDelegator writerDelegator,
writer.write("private:");
writer.indent();
if (streamingResponse) {
+ // Mainline places the handler/decoder after the data members and
+ // before the HasBeenSet flags.
+ MemberRenderer.renderPrivateDataMembers(writer, shape, model);
String handlerType = operation.getId().getName() + "Handler";
writer.write("$1L m_handler;", handlerType);
writer.write("Aws::Utils::Event::EventStreamDecoder m_decoder{Utils::Event::EventStreamDecoder(&m_handler)};");
+ writer.write("");
+ MemberRenderer.renderPrivateHasBeenSetFlags(writer, shape, model);
+ } else {
+ MemberRenderer.renderPrivateSection(writer, shape, model);
}
- MemberRenderer.renderPrivateSection(writer, shape, model);
}
});
writer.write("");
@@ -189,7 +193,6 @@ private void renderSource(CppWriterDelegator writerDelegator,
String className = operation.getId().getName() + "Request";
String fileName = "source/model/" + className + ".cpp";
writerDelegator.useFileWriter(fileName, writer -> {
- writeCopyright(writer);
if (protocol.isJsonLike()) {
writer.write("#include ");
@@ -289,26 +292,4 @@ public Void booleanNode(software.amazon.smithy.model.node.BooleanNode node) {
});
}
- private void renderClassDocComment(CppWriter writer, StructureShape shape) {
- if (shape.getTrait(DocumentationTrait.class).isPresent()) {
- String docText = MemberRenderer.collapseWhitespace(
- shape.getTrait(DocumentationTrait.class).get().getValue());
- String version = service.getVersion();
- String seeAlso = String.format(
- "See Also:
AWS API Reference
",
- smithyServiceName, version, shape.getId().getName());
- MemberRenderer.writeDocComment(writer, docText + seeAlso);
- } else {
- writer.write("/**");
- writer.write(" */");
- }
- }
-
- private void writeCopyright(CppWriter writer) {
- writer.write("/**");
- writer.write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.");
- writer.write(" * SPDX-License-Identifier: Apache-2.0.");
- writer.write(" */");
- writer.write("");
- }
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/ResultRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/ResultRenderer.java
index 4ffa1c62d89..f1741bcb084 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/ResultRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/ResultRenderer.java
@@ -59,7 +59,6 @@ private void renderHeader(CppWriterDelegator writerDelegator,
String className = operation.getId().getName() + "Result";
String fileName = "include/aws/" + smithyServiceName + "/model/" + className + ".h";
writerDelegator.useFileWriter(fileName, writer -> {
- writeCopyright(writer);
writer.write("#pragma once");
Set includes = new TreeSet<>();
@@ -86,7 +85,7 @@ private void renderHeader(CppWriterDelegator writerDelegator,
writer.writeNamespaceOpen("Model");
if (shape.getTrait(DocumentationTrait.class).isPresent()) {
- renderClassDocComment(writer, shape);
+ MemberRenderer.renderClassDocComment(writer, shape, smithyServiceName, service.getVersion());
}
writer.openBlock("class $L {", "};", className, () -> {
@@ -145,7 +144,6 @@ private void renderSource(CppWriterDelegator writerDelegator,
String className = operation.getId().getName() + "Result";
String fileName = "source/model/" + className + ".cpp";
writerDelegator.useFileWriter(fileName, writer -> {
- writeCopyright(writer);
writer.write("#include ");
writer.write("#include ");
@@ -205,27 +203,4 @@ private void renderForwardDeclarations(CppWriter writer) {
writer.writeNamespaceClose("Utils");
}
}
-
- private void renderClassDocComment(CppWriter writer, StructureShape shape) {
- if (shape.getTrait(DocumentationTrait.class).isPresent()) {
- String docText = MemberRenderer.collapseWhitespace(
- shape.getTrait(DocumentationTrait.class).get().getValue());
- String version = service.getVersion();
- String seeAlso = String.format(
- "See Also:
AWS API Reference",
- smithyServiceName, version, shape.getId().getName());
- MemberRenderer.writeDocComment(writer, docText + seeAlso);
- } else {
- writer.write("/**");
- writer.write(" */");
- }
- }
-
- private void writeCopyright(CppWriter writer) {
- writer.write("/**");
- writer.write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.");
- writer.write(" * SPDX-License-Identifier: Apache-2.0.");
- writer.write(" */");
- writer.write("");
- }
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/SubObjectRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/SubObjectRenderer.java
index 5038e949c16..5c4d3fab022 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/SubObjectRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/SubObjectRenderer.java
@@ -15,7 +15,6 @@
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.StructureShape;
-import software.amazon.smithy.model.traits.DocumentationTrait;
import java.util.List;
import java.util.Set;
@@ -63,7 +62,6 @@ private void renderHeader(CppWriterDelegator writerDelegator, StructureShape sha
String className = shape.getId().getName();
String fileName = "include/aws/" + smithyServiceName + "/model/" + className + ".h";
writerDelegator.useFileWriter(fileName, writer -> {
- writeCopyright(writer);
writer.write("#pragma once");
// Includes
@@ -90,7 +88,7 @@ private void renderHeader(CppWriterDelegator writerDelegator, StructureShape sha
writer.writeNamespaceOpen("Model");
writer.write("");
- renderClassDocComment(writer, shape);
+ MemberRenderer.renderClassDocComment(writer, shape, smithyServiceName, service.getVersion());
writer.openBlock("class $L {", "};", className, () -> {
writer.write("public:");
@@ -114,7 +112,6 @@ private void renderSource(CppWriterDelegator writerDelegator, StructureShape sha
String className = shape.getId().getName();
String fileName = "source/model/" + className + ".cpp";
writerDelegator.useFileWriter(fileName, writer -> {
- writeCopyright(writer);
if (protocol.isJsonLike()) {
writer.write("#include ");
@@ -167,26 +164,4 @@ private void renderForwardDeclarations(CppWriter writer) {
}
}
- private void renderClassDocComment(CppWriter writer, StructureShape shape) {
- if (shape.getTrait(DocumentationTrait.class).isPresent()) {
- String docText = MemberRenderer.collapseWhitespace(
- shape.getTrait(DocumentationTrait.class).get().getValue());
- String version = service.getVersion();
- String seeAlso = String.format(
- "See Also:
AWS API Reference",
- smithyServiceName, version, shape.getId().getName());
- MemberRenderer.writeDocComment(writer, docText + seeAlso);
- } else {
- writer.write("/**");
- writer.write(" */");
- }
- }
-
- private void writeCopyright(CppWriter writer) {
- writer.write("/**");
- writer.write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.");
- writer.write(" * SPDX-License-Identifier: Apache-2.0.");
- writer.write(" */");
- writer.write("");
- }
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/waiters/WaiterEmptyHeaderGenerator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/waiters/WaiterEmptyHeaderGenerator.java
index af15a7d96fb..1b2cc978dc7 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/waiters/WaiterEmptyHeaderGenerator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/waiters/WaiterEmptyHeaderGenerator.java
@@ -36,11 +36,6 @@ public static void generate(PluginContext context) {
CppWriterDelegator writers = new CppWriterDelegator(context.getFileManifest());
writers.useFileWriter("include/aws/" + c2jName + "/" + serviceName + "Waiter.h", writer -> {
- writer.write("/**");
- writer.write(" * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.");
- writer.write(" * SPDX-License-Identifier: Apache-2.0.");
- writer.write(" */");
- writer.write("");
writer.write("#pragma once");
writer.write("");
writer.writeNamespaceOpen("Aws");
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
index 225bb0b6ab9..78d86e3d000 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/EventStreamRendererTest.java
@@ -14,6 +14,7 @@
import software.amazon.smithy.model.shapes.StringShape;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.shapes.UnionShape;
+import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.model.traits.ErrorTrait;
import software.amazon.smithy.model.traits.StreamingTrait;
@@ -40,7 +41,9 @@ private static Model twoEventModel() {
UnionShape stream = UnionShape.builder()
.id("com.example#MyStreamEventStream")
.addTrait(new StreamingTrait())
- .addMember("alpha", eventA.getId())
+ .addTrait(new DocumentationTrait("Tagged union of stream events.
"))
+ .addMember("alpha", eventA.getId(),
+ b -> b.addTrait(new DocumentationTrait("Alpha event doc.
")))
.addMember("beta", eventB.getId())
.addMember("badException", exc.getId())
.build();
@@ -100,6 +103,21 @@ void handlerHeader_hasEnumAndCallbacksPerEvent() {
assertFalse(h.contains("SetBadExceptionCallback"), "Exception should not get a callback: " + h);
}
+ @Test
+ void handlerHeader_wrapsInitialResponseSettersInDocGroup() {
+ String h = render("DoStreamHandler.h");
+ assertTrue(h.contains("Sets an initial response callback."),
+ "Missing initial-response setter doc: " + h);
+ assertTrue(h.contains("a legacy one that does not distinguish"),
+ "Missing legacy initial-response setter doc: " + h);
+ // The two initial-response setters must be inside a ///@{ ... ///@} group.
+ int groupOpen = h.indexOf("///@{");
+ int exSetter = h.indexOf("SetInitialResponseCallbackEx");
+ int groupClose = h.indexOf("///@}", exSetter);
+ assertTrue(groupOpen >= 0 && groupOpen < exSetter && groupClose > exSetter,
+ "Initial-response setters must be wrapped in a ///@{ ... ///@} group: " + h);
+ }
+
@Test
void eventStreamUnionHeader_typesEventAndExceptionMembers() {
String h = render("MyStreamEventStream.h");
@@ -112,6 +130,16 @@ void eventStreamUnionHeader_typesEventAndExceptionMembers() {
assertTrue(h.contains("ExampleError"), "Exception members must be typed as ExampleError: " + h);
}
+ @Test
+ void eventStreamUnionHeader_rendersClassAndMemberDocs() {
+ String h = render("MyStreamEventStream.h");
+ // Union class-level documentation + See Also link.
+ assertTrue(h.contains("Tagged union of stream events."), "Missing union class doc: " + h);
+ assertTrue(h.contains("See Also:"), "Missing See Also block on union class: " + h);
+ // Member-level doc flows to the accessor for the member that has one.
+ assertTrue(h.contains("Alpha event doc."), "Missing alpha member doc: " + h);
+ }
+
@Test
void initialResponseHeader_hasHeaderCollectionCtorAndSerdeDecls() {
String h = render("DoStreamInitialResponse.h");
@@ -121,6 +149,11 @@ void initialResponseHeader_hasHeaderCollectionCtorAndSerdeDecls() {
// JSON serde declaration present (test model resolves to a JSON-like protocol)
assertTrue(h.contains("Jsonize") || h.contains("XmlNode") || h.contains("OutputToStream"),
"Missing serde declarations: " + h);
+ // The HeaderValueCollection ctor must precede Jsonize (mainline ordering).
+ int headerCtor = h.indexOf("const Http::HeaderValueCollection& responseHeaders");
+ int jsonize = h.indexOf("Jsonize");
+ assertTrue(headerCtor >= 0 && jsonize >= 0 && headerCtor < jsonize,
+ "HeaderValueCollection ctor must come before Jsonize: " + h);
}
@Test
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
index fda307537e6..d08fe24fe09 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/RequestRendererTest.java
@@ -97,6 +97,16 @@ void streamingResponseRequest_hasEventStreamAugmentation() {
"Missing decoder include: " + h);
assertFalse(h.contains("IsEventStreamRequest"),
"Response-only op must not declare IsEventStreamRequest: " + h);
+ // Mainline ordering: handler/decoder sit AFTER the data members and BEFORE the
+ // HasBeenSet flags (not at the top of the private block). Target the member
+ // DECLARATION ("DoStreamHandler m_handler;"), not the public getter body.
+ int dataMember = h.indexOf("Aws::String m_name;");
+ int handlerDecl = h.indexOf("DoStreamHandler m_handler;");
+ int firstFlag = h.indexOf("HasBeenSet = false;");
+ assertTrue(dataMember >= 0 && handlerDecl > dataMember,
+ "m_handler declaration must come after data members: " + h);
+ assertTrue(firstFlag >= 0 && handlerDecl < firstFlag,
+ "m_handler declaration must come before HasBeenSet flags: " + h);
}
@Test
From 4f2daea0ae7f3c12e6728b37b242f1f41e168ea5 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Thu, 30 Jul 2026 13:52:30 -0400
Subject: [PATCH 17/18] Fix utf-8 bom
---
.../generators/CppWriterDelegator.java | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java
index c162427b684..0aef2a5a273 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/CppWriterDelegator.java
@@ -10,6 +10,9 @@
import java.util.function.Consumer;
public class CppWriterDelegator {
+ /** UTF-8 byte order mark (U+FEFF), prepended to every file to match C2J-generated output. */
+ private static final String UTF8_BOM = "\uFEFF";
+
private final FileManifest fileManifest;
private final Map writers = new HashMap<>();
@@ -28,7 +31,7 @@ public void useFileWriter(String filename, Consumer writerConsumer) {
public void flushWriters() {
writers.forEach((filename, writer) -> {
// Add UTF-8 BOM to match C2J-generated file format
- String content = "" + writer.toString();
+ String content = UTF8_BOM + writer.toString();
fileManifest.writeFile(filename, content);
});
}
From 29d03d1ed4c092e6032a1af77688d61fc2efcf53 Mon Sep 17 00:00:00 2001
From: sbaluja
Date: Thu, 30 Jul 2026 14:15:09 -0400
Subject: [PATCH 18/18] Null checks -> Optional
---
.../generators/model/MemberRenderer.java | 9 ++++++---
.../model/renderers/EventStreamRenderer.java | 14 ++++++++------
2 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java
index 3123f563cba..e9554b96b1c 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRenderer.java
@@ -273,10 +273,13 @@ public static void renderClassDocComment(CppWriter writer, Shape shape,
}
}
+ /**
+ * Collapses all runs of whitespace in {@code text} to single spaces and trims the result.
+ *
+ * @param text the documentation text to normalize; must not be null
+ * @return the whitespace-collapsed text
+ */
public static String collapseWhitespace(String text) {
- if (text == null) {
- return null;
- }
return text.replaceAll("\\s+", " ").trim();
}
diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
index 4d30d1aeb9f..52567d284e7 100644
--- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
+++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/renderers/EventStreamRenderer.java
@@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Renders C++ event stream artifacts for response-side (simplex) streaming operations:
@@ -59,10 +60,11 @@ public EventStreamRenderer(List eventStreams, Model model, Serv
@Override
public void render(CppWriterDelegator writerDelegator) {
for (EventStreamInfo info : eventStreams) {
- UnionShape union = findStreamingUnion(info.resultShape());
- if (union == null) {
+ Optional streamingUnion = findStreamingUnion(info.resultShape());
+ if (streamingUnion.isEmpty()) {
continue;
}
+ UnionShape union = streamingUnion.get();
List events = new ArrayList<>();
List exceptions = new ArrayList<>();
partitionMembers(union, events, exceptions);
@@ -74,15 +76,15 @@ public void render(CppWriterDelegator writerDelegator) {
}
}
- /** Finds the @streaming union targeted by a member of the result structure. */
- private UnionShape findStreamingUnion(StructureShape resultShape) {
+ /** Finds the @streaming union targeted by a member of the result structure, if any. */
+ private Optional findStreamingUnion(StructureShape resultShape) {
for (MemberShape member : resultShape.getAllMembers().values()) {
Shape target = model.expectShape(member.getTarget());
if (target.isUnionShape() && target.hasTrait(StreamingTrait.class)) {
- return target.asUnionShape().get();
+ return target.asUnionShape();
}
}
- return null;
+ return Optional.empty();
}
/** Splits union members into events (non-exception) and exceptions, preserving order. */