Skip to content

Commit 26a722d

Browse files
committed
added a patch for cloudfront. uses a new suffix 2020_05_31
1 parent 9884429 commit 26a722d

4 files changed

Lines changed: 120 additions & 56 deletions

File tree

generated/src/aws-cpp-sdk-s3/include/aws/s3/model/pagination/ListPartsPaginationTraits.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct ListPartsPaginationTraits {
2121

2222
static OutcomeType Invoke(ClientType& client, const RequestType& request) { return client.ListParts(request); }
2323

24-
static bool HasMoreResults(const ResultType& result) { return result.GetNextPartNumberMarker() != 0; }
24+
static bool HasMoreResults(const ResultType& result) { return !result.GetNextPartNumberMarker().empty(); }
2525

2626
static void SetNextRequest(const ResultType& result, RequestType& request) {
2727
request.SetPartNumberMarker(result.GetNextPartNumberMarker());

tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/ServiceNameUtil.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,22 @@ public static String getC2jServiceName(ServiceShape service, Map<String, String>
105105

106106
return c2jMap != null ? c2jMap.getOrDefault(sdkId, sdkId) : sdkId;
107107
}
108+
109+
/**
110+
* Gets the client method name for an operation.
111+
* CloudFront requires a version suffix for backwards compatibility with legacy C2J code generation.
112+
* The legacy C2J generator appended version suffixes to CloudFront operation names in the generated code.
113+
*
114+
* TODO: Consider moving to a map if more services require version suffixes in the future
115+
*
116+
* @param opName The base operation name from the Smithy model
117+
* @param c2jServiceName The C2J service name (e.g., "cloudfront")
118+
* @return The operation name with version suffix if needed (e.g., "ListDistributions2020_05_31")
119+
*/
120+
public static String getClientMethodName(String opName, String c2jServiceName) {
121+
if ("cloudfront".equals(c2jServiceName)) {
122+
return opName + "2020_05_31";
123+
}
124+
return opName;
125+
}
108126
}

tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationClientHeaderGenerator.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ public class PaginationClientHeaderGenerator {
1717
private final ServiceShape service;
1818
private final List<OperationData<PaginatedTrait>> paginatedOps;
1919
private final Map<String, String> c2jMap;
20+
private final String c2jServiceName;
2021

2122
public PaginationClientHeaderGenerator(ServiceShape service, List<OperationData<PaginatedTrait>> paginatedOps, Map<String, String> c2jMap) {
2223
this.service = service;
2324
this.paginatedOps = paginatedOps;
2425
this.c2jMap = c2jMap;
26+
this.c2jServiceName = ServiceNameUtil.getC2jServiceName(service, c2jMap);
2527
}
2628

2729
public void render(CppWriter writer) {
@@ -59,8 +61,9 @@ private void renderNamespaces(CppWriter writer, String serviceName) {
5961

6062
for (OperationData<PaginatedTrait> data : paginatedOps) {
6163
String opName = data.getOperation().getId().getName();
64+
String methodName = ServiceNameUtil.getClientMethodName(opName, c2jServiceName);
6265
writer.write("using $LPaginator = Aws::Utils::Pagination::PagePaginator<$LClient, Model::$LRequest, Pagination::$LPaginationTraits>;",
63-
opName, ServiceNameUtil.getServiceNameUpperCamel(service), opName, opName);
66+
opName, ServiceNameUtil.getServiceNameUpperCamel(service), methodName, opName);
6467
}
6568

6669
writer.write("");

tools/code-generation/smithy/codegen/cpp-pagination-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/templates/PaginationTraitsGenerator.java

Lines changed: 97 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ private void generateTraitsHeader(CppWriter writer, OperationData<PaginatedTrait
6161
// Includes - detect suffix like C2J renameShape logic
6262
String resultSuffix = getResultSuffix(opName);
6363
String capitalizedServiceName = ServiceNameUtil.getServiceNameUpperCamel(service);
64+
String requestFileName = ServiceNameUtil.getClientMethodName(opName, c2jServiceName) + "Request";
65+
String resultFileName = ServiceNameUtil.getClientMethodName(opName, c2jServiceName) + resultSuffix;
6466
writer.writeInclude("aws/" + c2jServiceName + "/" + capitalizedServiceName + "_EXPORTS.h")
65-
.writeInclude("aws/" + c2jServiceName + "/model/" + opName + "Request.h")
66-
.writeInclude("aws/" + c2jServiceName + "/model/" + opName + resultSuffix + ".h")
67+
.writeInclude("aws/" + c2jServiceName + "/model/" + requestFileName + ".h")
68+
.writeInclude("aws/" + c2jServiceName + "/model/" + resultFileName + ".h")
6769
.writeInclude("aws/" + c2jServiceName + "/" + capitalizedServiceName + "Client.h")
6870
.write("");
6971

@@ -76,15 +78,16 @@ private void generateTraitsHeader(CppWriter writer, OperationData<PaginatedTrait
7678
// Struct definition
7779
writer.openBlock("struct " + opName + "PaginationTraits\n{", "};", () -> {
7880
// Use detected suffix to match C2J renameShape logic
79-
writer.write(" using RequestType = Model::$LRequest;", opName)
80-
.write(" using ResultType = Model::$L$L;", opName, resultSuffix)
81-
.write(" using OutcomeType = Model::$LOutcome;", opName)
81+
String methodName = ServiceNameUtil.getClientMethodName(opName, c2jServiceName);
82+
writer.write(" using RequestType = Model::$LRequest;", methodName)
83+
.write(" using ResultType = Model::$L$L;", methodName, resultSuffix)
84+
.write(" using OutcomeType = Model::$LOutcome;", methodName)
8285
.write(" using ClientType = $LClient;", capitalizedServiceName)
8386
.write("");
8487

8588
// Invoke method
8689
writer.openBlock(" static OutcomeType Invoke(ClientType& client, const RequestType& request)\n {", " }", () -> {
87-
writer.write(" return client.$L(request);", opName);
90+
writer.write(" return client.$L(request);", methodName);
8891
});
8992

9093
writer.write("");
@@ -93,7 +96,11 @@ private void generateTraitsHeader(CppWriter writer, OperationData<PaginatedTrait
9396
writer.openBlock(" static bool HasMoreResults(const ResultType& result)\n {", " }", () -> {
9497
if (trait.getOutputToken().isPresent()) {
9598
String outToken = trait.getOutputToken().get();
96-
if (outToken.toLowerCase().contains("marker") || outToken.toLowerCase().contains("number")) {
99+
String nestedListMember = getNestedListMember(op);
100+
String tokenName = extractTokenName(outToken);
101+
if (nestedListMember != null) {
102+
writer.write(" return !result.Get$L().Get$L().empty();", capitalize(nestedListMember), capitalize(tokenName));
103+
} else if (isNumericToken(op, outToken)) {
97104
writer.write(" return result.Get$L() != 0;", capitalize(outToken));
98105
} else {
99106
writer.write(" return !result.Get$L().empty();", capitalize(outToken));
@@ -142,7 +149,13 @@ private void generateTraitsHeader(CppWriter writer, OperationData<PaginatedTrait
142149
}
143150

144151
if (inToken != null && outToken != null) {
145-
writer.write(" request.Set$L(result.Get$L());", capitalize(inToken), capitalize(outToken));
152+
String nestedListMember = getNestedListMember(op);
153+
String tokenName = extractTokenName(outToken);
154+
if (nestedListMember != null) {
155+
writer.write(" request.Set$L(result.Get$L().Get$L());", capitalize(inToken), capitalize(nestedListMember), capitalize(tokenName));
156+
} else {
157+
writer.write(" request.Set$L(result.Get$L());", capitalize(inToken), capitalize(outToken));
158+
}
146159
} else {
147160
// TODO: Check AWS SDK C++ standard for handling null pagination tokens
148161
// Should we throw an exception, log a warning, or silently ignore?
@@ -168,23 +181,42 @@ private String getResultSuffix(String opName) {
168181
return "Response";
169182
}
170183

171-
// For now, use the simple approach that works:
172-
// If the actual generated file exists, use Result; otherwise use SdkResult
184+
// For CloudFront, use the base operation name (without version suffix) for conflict detection
185+
final String baseOpName;
186+
if ("cloudfront".equals(c2jServiceName) && opName.endsWith("2020_05_31")) {
187+
baseOpName = opName.substring(0, opName.length() - "2020_05_31".length());
188+
} else {
189+
baseOpName = opName;
190+
}
173191

174192
// Check for known SdkResult cases (where data model conflicts exist)
175193
Set<Shape> allShapes = context.getModel().toSet();
176194
boolean hasDataModelConflict = allShapes.stream()
177195
.anyMatch(shape -> {
178196
String shapeName = shape.getId().getName();
179-
if (shapeName.equals(opName + "Result")) {
180-
// Found a shape with the same name - check if it's a data model
197+
// Check if there's a conflicting shape with the base name + "Result"
198+
String candidateName = baseOpName + "Result";
199+
if (shapeName.equals(candidateName)) {
200+
// Found a shape with the same name - check if it's a data model (not an operation result)
181201
if (shape instanceof StructureShape) {
182202
StructureShape structShape = (StructureShape) shape;
183-
// If it doesn't have NextToken/nextToken, it's likely a data model
203+
// Check if this is an operation result by looking for pagination-related patterns
184204
Set<String> memberNames = structShape.getAllMembers().keySet();
185-
// TODO: Sanitize member names for other edge cases (e.g., different casing, underscores, etc.)
186-
boolean hasNextToken = memberNames.contains("NextToken") || memberNames.contains("nextToken");
187-
return !hasNextToken;
205+
206+
// Direct pagination tokens
207+
boolean hasDirectPaginationTokens = memberNames.contains("NextToken") || memberNames.contains("nextToken") ||
208+
memberNames.contains("Marker") || memberNames.contains("marker") ||
209+
memberNames.contains("NextMarker") || memberNames.contains("nextMarker") ||
210+
memberNames.contains("IsTruncated") || memberNames.contains("isTruncated");
211+
212+
// Check for nested list structures (common in AWS APIs)
213+
boolean hasNestedListStructure = memberNames.stream()
214+
.anyMatch(memberName -> memberName.toLowerCase().contains("list"));
215+
216+
// If it has direct pagination tokens or nested list structure, it's likely an operation result
217+
boolean isOperationResult = hasDirectPaginationTokens || hasNestedListStructure;
218+
219+
return !isOperationResult;
188220
}
189221
}
190222
return false;
@@ -193,44 +225,6 @@ private String getResultSuffix(String opName) {
193225
return hasDataModelConflict ? "SdkResult" : "Result";
194226
}
195227

196-
// TODO: Delete this method if it's not used - replaced by simpler conflict detection in getResultSuffix
197-
// Replicate the precise conflict detection logic from C2jModelToGeneratorModelTransformer
198-
private boolean hasNamingConflict(String candidateName, String opName) {
199-
// Get all shapes in the model to check for conflicts
200-
Set<Shape> allShapes = context.getModel().toSet();
201-
202-
for (Shape shape : allShapes) {
203-
String shapeName = shape.getId().getName();
204-
205-
// Direct exact name conflict - this is the main case
206-
if (candidateName.equals(shapeName)) {
207-
// If this is a structure, check if it's already a suitable operation result
208-
if (shape instanceof StructureShape) {
209-
StructureShape structShape = (StructureShape) shape;
210-
// If the existing shape has pagination tokens, it's already an operation result - no conflict
211-
// Check for various pagination token field names
212-
boolean hasNextToken = structShape.getAllMembers().keySet().stream()
213-
.anyMatch(memberName -> memberName.toLowerCase().contains("nexttoken") ||
214-
memberName.toLowerCase().contains("token"));
215-
216-
if (hasNextToken) {
217-
// This is already an operation result shape, no conflict
218-
return false;
219-
}
220-
221-
// If it doesn't have pagination tokens, it's a data model - conflict!
222-
return true;
223-
}
224-
return true;
225-
}
226-
}
227-
228-
return false;
229-
}
230-
231-
// Remove the hardcoded method - no longer needed
232-
// private boolean isKnownConflictingOperation(String opName) { ... }
233-
234228
private boolean isEc2Protocol() {
235229
// EC2 protocol services rename all Result shapes to Response
236230
// This matches the logic in Ec2CppClientGenerator.legacyPatchEc2Model
@@ -262,4 +256,53 @@ private String getServiceLevelOutputToken() {
262256
private String capitalize(String str) {
263257
return str.substring(0, 1).toUpperCase() + str.substring(1);
264258
}
259+
260+
private String extractTokenName(String outToken) {
261+
// Pagination token may be in format "MemberName.TokenName", extract just the token name
262+
return outToken.contains(".") ? outToken.substring(outToken.lastIndexOf(".") + 1) : outToken;
263+
}
264+
265+
private String getNestedListMember(OperationShape op) {
266+
// Check if the output has a nested list structure containing pagination tokens
267+
// This pattern is used by CloudFront and potentially other services
268+
String result = op.getOutput()
269+
.flatMap(outputId -> context.getModel().getShape(outputId))
270+
.filter(shape -> shape instanceof StructureShape)
271+
.map(shape -> (StructureShape) shape)
272+
.flatMap(outputShape -> {
273+
// Find a member that contains "list" in its name and has pagination tokens
274+
return outputShape.getAllMembers().entrySet().stream()
275+
.filter(entry -> entry.getKey().toLowerCase().contains("list"))
276+
.filter(entry -> {
277+
// Check if this member's target shape has pagination tokens
278+
return context.getModel().getShape(entry.getValue().getTarget())
279+
.filter(targetShape -> targetShape instanceof StructureShape)
280+
.map(targetShape -> (StructureShape) targetShape)
281+
.map(targetStruct -> {
282+
Set<String> memberNames = targetStruct.getAllMembers().keySet();
283+
return memberNames.contains("NextMarker") || memberNames.contains("nextMarker") ||
284+
memberNames.contains("Marker") || memberNames.contains("marker") ||
285+
memberNames.contains("IsTruncated") || memberNames.contains("isTruncated");
286+
})
287+
.orElse(false);
288+
})
289+
.map(entry -> entry.getKey())
290+
.findFirst();
291+
})
292+
.orElse(null);
293+
294+
return result;
295+
}
296+
297+
private boolean isNumericToken(OperationShape op, String tokenName) {
298+
// Check if the token is numeric by examining the shape type
299+
return op.getOutput()
300+
.flatMap(outputId -> context.getModel().getShape(outputId))
301+
.filter(shape -> shape instanceof StructureShape)
302+
.map(shape -> (StructureShape) shape)
303+
.flatMap(outputShape -> outputShape.getMember(tokenName))
304+
.flatMap(member -> context.getModel().getShape(member.getTarget()))
305+
.map(targetShape -> targetShape instanceof IntegerShape || targetShape instanceof LongShape)
306+
.orElse(false);
307+
}
265308
}

0 commit comments

Comments
 (0)