From 4180f989e71988322b03b4adcb34e7cdcf80a724 Mon Sep 17 00:00:00 2001 From: yibole Date: Sun, 8 Nov 2026 11:31:49 -0800 Subject: [PATCH 1/2] feat(codegen): Read endpoint BDD and read/write timeout exemptions on the Smithy path Master's C2J path generates the endpoint provider from endpoint-bdd-1.json and runs DefaultReadWriteTimeoutExemptionProcessor in its customization chain. The Smithy path did neither, so a migrated service would get the rule-tree endpoint provider and lose its default read/write timeout. - AddSmithyEndpoints reads the @endpointBdd trait into EndpointBddModel, and SmithyIntermediateModelBuilder passes it to the intermediate model. - SmithyIntermediateModelPostprocessor runs DefaultReadWriteTimeoutExemptionProcessor last, as the C2J chain does. --- .../SmithyIntermediateModelPostprocessor.java | 1 + .../codegen/smithy/AddSmithyEndpoints.java | 21 ++++++--- .../SmithyIntermediateModelBuilder.java | 7 ++- .../SmithyIntermediateModelBuilderTest.java | 44 +++++++++++++++++++ ...thyIntermediateModelPostprocessorTest.java | 29 ++++++++++++ 5 files changed, 95 insertions(+), 7 deletions(-) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/customization/processors/SmithyIntermediateModelPostprocessor.java b/codegen/src/main/java/software/amazon/awssdk/codegen/customization/processors/SmithyIntermediateModelPostprocessor.java index 6c4d0f856f2c..201b208219d5 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/customization/processors/SmithyIntermediateModelPostprocessor.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/customization/processors/SmithyIntermediateModelPostprocessor.java @@ -44,6 +44,7 @@ public static SmithyIntermediateModelPostprocessor create(CustomizationConfig co processors.add(new UseLegacyEventGenerationSchemeProcessor()); processors.add(new EventStreamSharedEventProcessor(config.getDuplicateAndRenameSharedEvents())); processors.add(new LongPollingOperationProcessor()); + processors.add(new DefaultReadWriteTimeoutExemptionProcessor()); return new SmithyIntermediateModelPostprocessor(processors); } diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/AddSmithyEndpoints.java b/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/AddSmithyEndpoints.java index 54483a85f4b0..859f9bb5f65c 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/AddSmithyEndpoints.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/AddSmithyEndpoints.java @@ -18,22 +18,22 @@ import java.io.IOException; import software.amazon.awssdk.codegen.internal.Jackson; import software.amazon.awssdk.codegen.model.rules.endpoints.EndpointTestSuiteModel; +import software.amazon.awssdk.codegen.model.service.EndpointBddModel; import software.amazon.awssdk.codegen.model.service.EndpointRuleSetModel; import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.rulesengine.traits.EndpointBddTrait; import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait; import software.amazon.smithy.rulesengine.traits.EndpointTestsTrait; /** - * Reads the endpoint rule-set and endpoint tests from the service's Smithy traits, where C2J reads - * them from the {@code endpoint-rule-set.json} and {@code endpoint-tests.json} sidecar files. + * Reads the endpoint rule-set, endpoint BDD and endpoint tests from the service's Smithy traits, where C2J reads + * them from the {@code endpoint-rule-set.json}, {@code endpoint-bdd-1.json} and {@code endpoint-tests.json} sidecar + * files. * *

The trait content already uses the schema these POJOs expect, so this is a parse rather than a * translation. It goes through the same {@link Jackson} mapper the sidecar path uses, so the two * sources produce the same concrete types and tolerate unknown keys the same way. - * - *

The {@code endpointBdd} trait encodes the same rules as a binary decision diagram. Every - * service also carries {@code endpointRuleSet} in tree form, so it is ignored. */ final class AddSmithyEndpoints { @@ -50,6 +50,17 @@ static EndpointRuleSetModel endpointRuleSet(ServiceShape service) { .orElse(null); } + /** + * Returns null when the service has no BDD trait. When present, {@code IntermediateModel} takes the endpoint + * parameters from it and codegen generates the BDD endpoint provider instead of the rule-tree one, as for a C2J + * service that ships {@code endpoint-bdd-1.json}. + */ + static EndpointBddModel endpointBdd(ServiceShape service) { + return service.getTrait(EndpointBddTrait.class) + .map(trait -> parse(EndpointBddModel.class, trait.toNode(), service)) + .orElse(null); + } + /** * Returns null when the service has no endpoint-tests trait. {@code IntermediateModel} then * substitutes an empty test suite. Uses {@code toNode()} rather than {@code getTestCases()} diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelBuilder.java b/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelBuilder.java index 5f62a8f94c9c..612210db62b4 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelBuilder.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelBuilder.java @@ -40,6 +40,7 @@ import software.amazon.awssdk.codegen.model.rules.endpoints.EndpointTestSuiteModel; import software.amazon.awssdk.codegen.model.service.AuthType; import software.amazon.awssdk.codegen.model.service.ClientContextParam; +import software.amazon.awssdk.codegen.model.service.EndpointBddModel; import software.amazon.awssdk.codegen.model.service.EndpointRuleSetModel; import software.amazon.awssdk.codegen.model.service.PaginatorDefinition; import software.amazon.awssdk.codegen.naming.DefaultSmithyNamingStrategy; @@ -81,6 +82,7 @@ public final class SmithyIntermediateModelBuilder { private final ServiceIndex serviceIndex; private final String protocol; private final EndpointRuleSetModel endpointRuleSet; + private final EndpointBddModel endpointBdd; private final EndpointTestSuiteModel endpointTestSuiteModel; private final List shapeProcessors; @@ -94,6 +96,7 @@ public SmithyIntermediateModelBuilder(SmithyModels models) { this.serviceIndex = ServiceIndex.of(model); this.protocol = ProtocolUtils.resolveProtocol(serviceIndex, service, customConfig); this.endpointRuleSet = AddSmithyEndpoints.endpointRuleSet(service); + this.endpointBdd = AddSmithyEndpoints.endpointBdd(service); this.endpointTestSuiteModel = AddSmithyEndpoints.endpointTests(service); this.shapeProcessors = createShapeProcessors(); } @@ -183,7 +186,7 @@ public IntermediateModel build() { AddSmithyMetadata.constructMetadata(model, service, serviceIndex, namingStrategy, customConfig, protocol), operations, shapes, customConfig, endpointOperation, paginators, namingStrategy, Collections.emptyMap(), - endpointRuleSet, endpointTestSuiteModel, null, clientContextParams); + endpointRuleSet, endpointTestSuiteModel, endpointBdd, clientContextParams); SmithyIntermediateModelPostprocessor.create(customConfig).postprocess(fullModel); @@ -202,7 +205,7 @@ public IntermediateModel build() { fullModel.getWaiters(), endpointRuleSet, endpointTestSuiteModel, - null, + endpointBdd, clientContextParams); linkMembersToShapes(trimmedModel); diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelBuilderTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelBuilderTest.java index df7a34b7da1b..954d38f9fe7b 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelBuilderTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelBuilderTest.java @@ -54,6 +54,32 @@ class SmithyIntermediateModelBuilderTest { + " ]\n" + "})\n"; + /** + * The rule set above as Smithy compiles it to a BDD ({@code EndpointBddTrait.from(Cfg.from(ruleSet))}), with its + * own URL. + */ + private static final String BDD = + "@smithy.rules#clientContextParams(\n" + + " Region: { type: \"string\", documentation: \"The region\" }\n" + + ")\n" + + "@smithy.rules#endpointBdd({\n" + + " version: \"1.1\"\n" + + " parameters: {\n" + + " Region: { required: false, type: \"string\" }\n" + + " }\n" + + " conditions: []\n" + + " results: [\n" + + " {\n" + + " conditions: []\n" + + " endpoint: { url: \"https://bdd.example.amazonaws.com\", properties: {}, headers: {} }\n" + + " type: \"endpoint\"\n" + + " }\n" + + " ]\n" + + " root: 100000001\n" + + " nodeCount: 1\n" + + " nodes: \"/////wAAAAH/////\"\n" + + "})\n"; + private static final String TESTS = "@smithy.rules#endpointTests({\n" + " version: \"1.0\"\n" @@ -100,6 +126,23 @@ void endpointTraitsPresent_reachTheIntermediateModel() { assertThat(model.getEndpointParameters()).containsOnlyKeys("Region"); assertThat(model.getEndpointRules()).hasSize(1); assertThat(model.getEndpointTestSuiteModel().getTestCases()).hasSize(1); + assertThat(model.getEndpointBddModel()).isNull(); + } + + /** + * Without a rule set the parameters can only come from the BDD: the default rules declare more than + * {@code Region}. + */ + @Test + void endpointBddTraitPresent_reachesTheIntermediateModel() { + IntermediateModel model = build(BDD); + + assertThat(model.getEndpointBddModel()).isNotNull(); + assertThat(model.getEndpointBddModel().getRoot()).isEqualTo(100000001); + assertThat(model.getEndpointBddModel().getNodeCount()).isEqualTo(1); + assertThat(model.getEndpointBddModel().getNodes()).isEqualTo("/////wAAAAH/////"); + assertThat(model.getEndpointBddModel().getResults()).hasSize(1); + assertThat(model.getEndpointParameters()).containsOnlyKeys("Region"); } /** @@ -115,6 +158,7 @@ void endpointTraitsAbsent_fallBackToTheDefaultRules() { assertThat(model.getEndpointRules()).usingRecursiveComparison().isEqualTo(defaultRules.getRules()); assertThat(model.getEndpointParameters()).usingRecursiveComparison().isEqualTo(defaultRules.getParameters()); assertThat(model.getEndpointTestSuiteModel().getTestCases()).isEmpty(); + assertThat(model.getEndpointBddModel()).isNull(); } private static IntermediateModel buildPaginated(CustomizationConfig config) { diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelPostprocessorTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelPostprocessorTest.java index 77fb06bb69d3..9bfb412d0267 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelPostprocessorTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelPostprocessorTest.java @@ -74,6 +74,20 @@ void longPollingOperation_isMarked() { assertThat(model.getOperation("ReceiveMessage").isLongPolling()).isTrue(); } + @Test + void readWriteTimeoutExemptService_getsItsTimeout() { + IntermediateModel model = build(serviceWithSdkId("Neptune Graph"), CustomizationConfig.create()); + + assertThat(model.getMetadata().getDefaultReadWriteTimeoutMillis()).isEqualTo(900_000L); + } + + @Test + void serviceNotInReadWriteTimeoutExemptions_hasNoTimeout() { + IntermediateModel model = build(serviceWithSdkId("Demo"), CustomizationConfig.create()); + + assertThat(model.getMetadata().getDefaultReadWriteTimeoutMillis()).isNull(); + } + @Test void legacyEventGenerationScheme_twoMembersSharingAShape_fails() { CustomizationConfig config = legacyEventConfig("EventOne", "EventTwo"); @@ -151,6 +165,21 @@ private static String sqsReceiveMessageService() { + "structure OpResponse {}\n"; } + private static String serviceWithSdkId(String sdkId) { + return "$version: \"2.0\"\nnamespace demo\n\n" + + "use aws.api#service\n" + + "use aws.auth#sigv4\n" + + "use aws.protocols#restJson1\n" + + "@service(sdkId: \"" + sdkId + "\", arnNamespace: \"demo\")\n" + + "@sigv4(name: \"demo\")\n" + + "@restJson1\n" + + "service DemoService { version: \"2024-01-01\", operations: [Op] }\n" + + "@http(method: \"POST\", uri: \"/op\")\n" + + "operation Op { input: OpRequest, output: OpResponse }\n" + + "structure OpRequest {}\n" + + "structure OpResponse {}\n"; + } + private static String eventStreamService() { return REST_JSON_SERVICE + "service DemoService { version: \"2024-01-01\", operations: [Subscribe] }\n" From 1461847f5b561556e54399b1483b0b02bdb87e62 Mon Sep 17 00:00:00 2001 From: yibole Date: Tue, 10 Nov 2026 13:55:34 -0800 Subject: [PATCH 2/2] test(codegen): Drop a redundant timeout test and shorten the BDD Javadoc The not-exempt case passes with or without the postprocessor change and is already covered by DefaultReadWriteTimeoutExemptionProcessorTest. --- .../amazon/awssdk/codegen/smithy/AddSmithyEndpoints.java | 5 ++--- .../smithy/SmithyIntermediateModelPostprocessorTest.java | 7 ------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/AddSmithyEndpoints.java b/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/AddSmithyEndpoints.java index 859f9bb5f65c..9ae3ac3fd3da 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/AddSmithyEndpoints.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/smithy/AddSmithyEndpoints.java @@ -51,9 +51,8 @@ static EndpointRuleSetModel endpointRuleSet(ServiceShape service) { } /** - * Returns null when the service has no BDD trait. When present, {@code IntermediateModel} takes the endpoint - * parameters from it and codegen generates the BDD endpoint provider instead of the rule-tree one, as for a C2J - * service that ships {@code endpoint-bdd-1.json}. + * Returns null when the service has no BDD trait; otherwise codegen generates the BDD endpoint provider, as C2J does + * from {@code endpoint-bdd-1.json}. */ static EndpointBddModel endpointBdd(ServiceShape service) { return service.getTrait(EndpointBddTrait.class) diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelPostprocessorTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelPostprocessorTest.java index 9bfb412d0267..abf8869730c7 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelPostprocessorTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/smithy/SmithyIntermediateModelPostprocessorTest.java @@ -81,13 +81,6 @@ void readWriteTimeoutExemptService_getsItsTimeout() { assertThat(model.getMetadata().getDefaultReadWriteTimeoutMillis()).isEqualTo(900_000L); } - @Test - void serviceNotInReadWriteTimeoutExemptions_hasNoTimeout() { - IntermediateModel model = build(serviceWithSdkId("Demo"), CustomizationConfig.create()); - - assertThat(model.getMetadata().getDefaultReadWriteTimeoutMillis()).isNull(); - } - @Test void legacyEventGenerationScheme_twoMembersSharingAShape_fails() { CustomizationConfig config = legacyEventConfig("EventOne", "EventTwo");