Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,6 @@ private GenerationParams smithyGenerationParams(ModelRoot r) {
SmithyModels smithyModels = SmithyModels.builder()
.model(model)
.customizationConfig(r.customizationConfig)
.endpointRuleSetModel(loadEndpointRuleSetModel(modelRootPath))
.endpointTestSuiteModel(loadEndpointTestSuiteModel(modelRootPath))
.build();
IntermediateModel intermediateModel = new SmithyIntermediateModelBuilder(smithyModels).build();
return new GenerationParams().withIntermediateModel(intermediateModel)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.codegen.smithy;

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.EndpointRuleSetModel;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.shapes.ServiceShape;
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.
*
* <p>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.
*
* <p>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 {

private AddSmithyEndpoints() {
}

/**
* Returns null when the service has no rule-set trait, letting the caller fall back to the
* sidecar file.
*/
static EndpointRuleSetModel endpointRuleSet(ServiceShape service) {
return service.getTrait(EndpointRuleSetTrait.class)
.map(trait -> parse(EndpointRuleSetModel.class, trait.getRuleSet()))
.orElse(null);
}

/**
* Returns null when the service has no endpoint-tests trait, letting the caller fall back to
* the sidecar file. Uses {@code toNode()} rather than {@code getTestCases()} because the POJO
* expects the whole {@code {version, testCases}} object.
*/
static EndpointTestSuiteModel endpointTests(ServiceShape service) {
return service.getTrait(EndpointTestsTrait.class)
.map(trait -> parse(EndpointTestSuiteModel.class, trait.toNode()))
.orElse(null);
}

private static <T> T parse(Class<T> clazz, Node node) {
try {
return Jackson.load(clazz, Node.printJson(node));
} catch (IOException e) {
throw new RuntimeException("Failed to read " + clazz.getSimpleName() + " from a Smithy trait", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ public SmithyIntermediateModelBuilder(SmithyModels models) {
this.typeUtils = new TypeUtils(namingStrategy);
this.serviceIndex = ServiceIndex.of(model);
this.protocol = ProtocolUtils.resolveProtocol(serviceIndex, service);
this.endpointRuleSet = models.endpointRuleSetModel();
this.endpointTestSuiteModel = models.endpointTestSuiteModel();
this.endpointRuleSet = AddSmithyEndpoints.endpointRuleSet(service);
this.endpointTestSuiteModel = AddSmithyEndpoints.endpointTests(service);
this.shapeProcessors = createShapeProcessors();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
package software.amazon.awssdk.codegen.smithy;

import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig;
import software.amazon.awssdk.codegen.model.rules.endpoints.EndpointTestSuiteModel;
import software.amazon.awssdk.codegen.model.service.EndpointRuleSetModel;
import software.amazon.awssdk.utils.builder.SdkBuilder;
import software.amazon.smithy.model.Model;

Expand All @@ -31,17 +29,10 @@ public final class SmithyModels {

private final Model model;
private final CustomizationConfig customizationConfig;
private final EndpointRuleSetModel endpointRuleSetModel;
private final EndpointTestSuiteModel endpointTestSuiteModel;

private SmithyModels(Model model,
CustomizationConfig customizationConfig,
EndpointRuleSetModel endpointRuleSetModel,
EndpointTestSuiteModel endpointTestSuiteModel) {
private SmithyModels(Model model, CustomizationConfig customizationConfig) {
this.model = model;
this.customizationConfig = customizationConfig;
this.endpointRuleSetModel = endpointRuleSetModel;
this.endpointTestSuiteModel = endpointTestSuiteModel;
}

public static Builder builder() {
Expand All @@ -56,20 +47,10 @@ public CustomizationConfig customizationConfig() {
return customizationConfig;
}

public EndpointRuleSetModel endpointRuleSetModel() {
return endpointRuleSetModel;
}

public EndpointTestSuiteModel endpointTestSuiteModel() {
return endpointTestSuiteModel;
}

public static final class Builder implements SdkBuilder<Builder, SmithyModels> {

private Model model;
private CustomizationConfig customizationConfig;
private EndpointRuleSetModel endpointRuleSetModel;
private EndpointTestSuiteModel endpointTestSuiteModel;

private Builder() {
}
Expand All @@ -84,20 +65,10 @@ public Builder customizationConfig(CustomizationConfig customizationConfig) {
return this;
}

public Builder endpointRuleSetModel(EndpointRuleSetModel endpointRuleSetModel) {
this.endpointRuleSetModel = endpointRuleSetModel;
return this;
}

public Builder endpointTestSuiteModel(EndpointTestSuiteModel endpointTestSuiteModel) {
this.endpointTestSuiteModel = endpointTestSuiteModel;
return this;
}

@Override
public SmithyModels build() {
CustomizationConfig config = customizationConfig != null ? customizationConfig : CustomizationConfig.create();
return new SmithyModels(model, config, endpointRuleSetModel, endpointTestSuiteModel);
return new SmithyModels(model, config);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.codegen.smithy;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import software.amazon.awssdk.codegen.model.rules.endpoints.EndpointTestSuiteModel;
import software.amazon.awssdk.codegen.model.service.EndpointRuleSetModel;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;

/**
* Unit tests for {@link AddSmithyEndpoints}, covering trait present, trait absent, and the
* lower-case parameter {@code type} the traits use where the sidecar files capitalise it.
*/
class AddSmithyEndpointsTest {

/**
* Rule-set parameters must be bound in the service model, and AWS built-ins like
* {@code AWS::Region} are registered by smithy-aws-endpoints, which codegen does not depend on.
* Binding through {@code clientContextParams} keeps the model self-contained.
*/
private static final String RULE_SET =
"@smithy.rules#clientContextParams(\n"
+ " Region: { type: \"string\", documentation: \"The region\" }\n"
+ " UseFIPS: { type: \"boolean\", documentation: \"Use FIPS endpoints\" }\n"
+ ")\n"
+ "@smithy.rules#endpointRuleSet({\n"
+ " version: \"1.0\"\n"
+ " parameters: {\n"
+ " Region: { required: false, documentation: \"The region\", type: \"string\" }\n"
+ " UseFIPS: { required: true, default: false, type: \"boolean\" }\n"
+ " }\n"
+ " rules: [\n"
+ " { conditions: [], endpoint: { url: \"https://example.amazonaws.com\" }, type: \"endpoint\" }\n"
+ " ]\n"
+ "})\n";

private static final String TESTS =
"@smithy.rules#endpointTests({\n"
+ " version: \"1.0\"\n"
+ " testCases: [\n"
+ " {\n"
+ " documentation: \"basic\"\n"
+ " params: { Region: \"us-east-1\", UseFIPS: false }\n"
+ " expect: { endpoint: { url: \"https://example.amazonaws.com\" } }\n"
+ " }\n"
+ " ]\n"
+ "})\n";

private static ServiceShape serviceOf(String serviceTraits) {
String src =
"$version: \"2.0\"\nnamespace demo\n\n"
+ "use aws.api#service\n"
+ "use aws.auth#sigv4\n"
+ "use aws.protocols#restJson1\n"
+ "@service(sdkId: \"Demo\", arnNamespace: \"demo\")\n"
+ "@sigv4(name: \"demo\")\n"
+ "@restJson1\n"
+ serviceTraits
+ "service DemoService { version: \"2024-01-01\", operations: [Op] }\n\n"
+ "@http(method: \"POST\", uri: \"/op\")\n"
+ "operation Op { input: OpRequest, output: OpResponse }\n"
+ "structure OpRequest {}\n"
+ "structure OpResponse {}\n";
Model model = Model.assembler()
.discoverModels(Model.class.getClassLoader())
.addUnparsedModel("test.smithy", src)
.assemble()
.unwrap();
return model.getServiceShapes().iterator().next();
}

@Test
void ruleSetTraitPresent_isTranslated() {
EndpointRuleSetModel ruleSet = AddSmithyEndpoints.endpointRuleSet(serviceOf(RULE_SET));

assertThat(ruleSet).isNotNull();
assertThat(ruleSet.getVersion()).isEqualTo("1.0");
assertThat(ruleSet.getParameters()).containsOnlyKeys("Region", "UseFIPS");
assertThat(ruleSet.getRules()).hasSize(1);
}

@Test
void ruleSetTraitAbsent_isNull() {
assertThat(AddSmithyEndpoints.endpointRuleSet(serviceOf(""))).isNull();
}

@Test
void endpointTestsTraitPresent_isTranslated() {
EndpointTestSuiteModel tests = AddSmithyEndpoints.endpointTests(serviceOf(RULE_SET + TESTS));

assertThat(tests).isNotNull();
assertThat(tests.getTestCases()).hasSize(1);
}

@Test
void endpointTestsTraitAbsent_isNull() {
assertThat(AddSmithyEndpoints.endpointTests(serviceOf(""))).isNull();
}

/**
* The traits write {@code "string"} where the sidecar files write {@code "String"}. The value is
* carried through verbatim; every consumer lower-cases before switching on it.
*/
@Test
void parameterType_keepsTheTraitsLowerCaseForm() {
EndpointRuleSetModel ruleSet = AddSmithyEndpoints.endpointRuleSet(serviceOf(RULE_SET));

assertThat(ruleSet.getParameters().get("Region").getType()).isEqualTo("string");
assertThat(ruleSet.getParameters().get("UseFIPS").getType()).isEqualTo("boolean");
}
}
Loading
Loading