Migration smithy Part 4 - #3912
Conversation
…etadata Add per-service Smithy model transforms (SourceRegion, Lambda, SQS, ApiGateway, ApiGatewayV2, EC2) and a shared TransformSupport helper, and wire them into the ModelCodegenPlugin transform pipeline. Extend GlobalTransforms to also inject the ResponseMetadata envelope for awsQueryCompatible services (e.g. SQS = awsJson1_0 + @awsQueryCompatible), matching legacy C2J CppClientGenerator.addRequestIdToResults. ResponseMetadata is now reserved via a shared constant and injection fails fast on any modeled collision rather than silently mis-generating.
…Mapper Descend through nested list/map shapes so leaf struct/enum headers reach the generated header even through nested containers (e.g. apigateway Deployment.apiSummary: Map<String, Map<String, MethodSnapshot>>), matching C2J's recursive unwrap. Recursion is bounded by container-nesting depth, and @Sparse still pulls in <aws/crt/Optional.h> at each nested container level.
Emit #if defined(_WIN32) && defined(X) / #undef X guards for enum constants that collide with a Windows preprocessor macro (DynamoDB IN, EC2 interface, S3Crt IGNORE), mirroring C2J PlatformAndKeywordSanitizer.PREDEFINED_SYMBOLS_MAPPING and ModelEnumHeader.vm.
Name result classes and files via ShapeUtil.getResultSuffix instead of a hardcoded "Result", so services like EC2 emit *Response result classes, matching the legacy C2J generator.
…Id stamp Classify @streaming-union events whose sole payload is a single @eventPayload blob member as header-only blob-carrier events (C2J eventPayloadType == "blob") and render them via a new EventPayloadRenderer instead of as JSON sub-objects. Stamp the top-level requestId onto dual-role sub-objects (operation outputs also referenced as members) for JSON-family protocols, gated out for Query/EC2 which inject ResponseMetadata instead. MemberRenderer now renders the injected ResponseMetadata envelope as always-present (no HasBeenSet getter, flag true), initializes required-member flags in useRequiredField contexts, and keeps event stream / raw streaming payload flags false, matching C2J.
…llisions Smithy: renameMember fast-fails on member collision, adds jsonName overload
…h jsonName parity Smithy: drop superseded accessanalyzer + dead cloudsearchdomain collision-map entries
Smithy: failing test for OperationContextParams header declarations Smithy: port OperationContextCppCodeGenerator string-building helper Smithy: port CppEndpointsJmesPathVisitor onto Smithy shape types Smithy: unit tests for OperationContextParams JMESPath visitor Smithy: emit GetOperationContextParams header decl for OperationContextParamsTrait Smithy: emit GetEndpointContextParams body and GetOperationContextParams accessor Smithy: emit Accessor comment before GetOperationContextParams for C2J parity Smithy: end-to-end tests for OperationContextParams JMESPath patterns Smithy: reduce OperationContextParams visitor boilerplate via UnsupportedExpressionVisitor base Smithy: whitespace-tolerant assertions for OperationContextParams visitor tests Smithy: immutable Emit-based OperationContextParams visitor; drop mutable code generator Smithy: correct stale accessor-emission comment after immutable redesign
…t Id2-only, region order
Smithy: EventStreamRenderer emits void() callback typedef for empty events Smithy: EventStreamRenderer emits arg-less default lambda for empty events Smithy: EventStreamRenderer dispatches empty events arg-less (no struct construct) Smithy: ShapeClassifier drops empty-member event structs from subObjects Smithy: drop incoming event-stream union header from subObjects/render Smithy: record event-stream empty-event cleanup as accepted parity divergence Smithy: skip empty-event struct include in event-stream handler header Smithy: mark event-stream handler-include defect resolved in parity-deltas
…ror methods - injectAccessLogTagQuery: mark customizedAccessLogTag @httpQueryParams so RestXml emits AddQueryStringParameters (was missing on 110 requests) - new EmbeddedErrorsTrait + S3Transforms.markEmbeddedErrors (C2J functionsWithEmbeddedErrors set) + RestXmlProtocolTraits emits HasEmbeddedError under the marker (was missing on 92 requests)
The XML error-sniff body is constant (not shape-dependent) across C2J's S3 request-source templates, so there is nothing to defer. Move the HasEmbeddedError helpers out of the base ProtocolTraits (they leaked XML parsing into every protocol) into RestXmlProtocolTraits, where the marker is the only emit site, and emit the real parse-body / root-is-<Error> logic.
…etter customizations
Smithy: drop dead CBOR HTTP-binding wiring and correct REQUEST_SOURCE includes Smithy: share RequestHeaderSerializer value expression across scalar and list paths Smithy: RPC protocols (awsJson, rpcv2-cbor) do not wire-serialize HTTP-binding members Smithy: query enums gate on HasBeenSet only (no NOT_SET), matching C2J Smithy: lowercase request header location names to match C2J Smithy: S3 customizedAccessLogTag x- query filter Smithy: dedupe RequestQuerySerializer timestamp stream expression Smithy: RequestQuerySerializer list and query-params-map serialization Smithy: RequestHeaderSerializer prefix-header map and list serialization Smithy: RequestHeaderSerializer scalar header-member serialization
…cts are not emitted
| @@ -64,11 +66,13 @@ public void generateAll() { | |||
| private List<ShapeRenderer> buildRenderers(ClassifiedShapes classified, RenderContext ctx) { | |||
| List<ShapeRenderer> renderers = new ArrayList<>(); | |||
| renderers.add(new EnumShapeRenderer(classified.enums(), ctx)); | |||
| renderers.add(new SubObjectRenderer(classified.subObjects(), ctx)); | |||
| renderers.add(new SubObjectRenderer(classified.subObjects(), classified.resultOutputIds(), ctx)); | |||
There was a problem hiding this comment.
if we are decomposing the SubObjectRenderer to take two members of the same object seems like it should become SubObjectRenderer(classified)
| // Future: S3Transforms.asTransform(), Ec2Transforms.asTransform(), etc. | ||
| GlobalTransforms.asTransform(), | ||
| SourceRegionTransform.asTransform(), | ||
| LambdaTransforms.asTransform(), |
There was a problem hiding this comment.
can some transformers be run in parallel with eachother? and is there a gain for doing as such? LambdaTransforms, SqsTransforms will only ever run on one model so threading a boolean check is not worth the thread, but is there any cases where several that would take a while or could involve i/o run and can be run in parallel? are there any that need to happen in order and require a graph like pipeline?
There was a problem hiding this comment.
they can, but there's no real benefit to it. they're all pure in-memory model rewrites, no i/o, and each is gated to a single service so only one really does work per run. also, because of that there is no specific ordering to these transforms.
| * service-agnostic. | ||
| */ | ||
| public final class AdditionalRequestHeadersTrait extends AbstractTrait { | ||
| public static final ShapeId ID = ShapeId.from("aws.cpp.internal#additionalRequestHeaders"); |
There was a problem hiding this comment.
so here we are creating traits internal to us. the transforms and renderers are tied together through these synthetic aws.cpp.internal#* traits. im fine with traits being the transport, thats the normal smithy way, but the contract between the two sides is completely implicit and nothing enforces it.
we could actually register them as real smithy traits. right now they have no Provider and no TraitService entry so theyre not first class and wouldnt survive a model round trip. we could give each one a Provider and register it through the SPI.:
// in TopLevelHostIdTrait
public static final class Provider extends AnnotationTrait.Provider<TopLevelHostIdTrait> {
public Provider() { super(ID, node -> new TopLevelHostIdTrait()); }
}
# src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService
com.amazonaws...transforms.TopLevelHostIdTrait$Provider
com.amazonaws...transforms.ChecksumMemberTrait$Provider
# one per internal trait
kotlin does something very similar to this for their own custom traits.
There was a problem hiding this comment.
yeah agreed these would traits would not survive a round-trip but the round trip never happens here since we stamp them in the transform and read them in the renderer within the same in-memory pass in ModelCodegenPlugin, nothing ever serializes the model back out and re-assembles it. so there's no provider lookup to fail. the traits ride along as plain objects and Model.builder() doesn't validate applied-trait defs. however, if you feel strongly about registering them to enforce the contract + be round-trip safe, i dont mind but it will grow the PR
Issue #, if available:
Description of changes:
Check all that applies:
Check which platforms you have built SDK on to verify the correctness of this PR.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.