Skip to content

Commit 3c800f8

Browse files
committed
Consume the AWS API models from Maven instead of checking it out from git
1 parent dbfca8f commit 3c800f8

8 files changed

Lines changed: 112 additions & 81 deletions

File tree

.github/workflows/codegen-compatibility.yml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ jobs:
1919
steps:
2020
- uses: actions/checkout@v6
2121

22-
- name: Checkout api-models-aws
23-
uses: actions/checkout@v6
24-
with:
25-
repository: aws/api-models-aws
26-
path: api-models-aws
27-
2822
- name: Set up JDK 21
2923
uses: actions/setup-java@v5
3024
with:
@@ -46,10 +40,8 @@ jobs:
4640
caches
4741
4842
- name: Run codegen compatibility tests
49-
env:
50-
API_MODELS_AWS_DIR: ${{ github.workspace }}/api-models-aws
5143
run: |
52-
./gradlew :codegen:codegen-plugin:integ --tests "*.AwsModelCodegenCompilationTest" --stacktrace
44+
./gradlew :codegen:codegen-plugin:integ -PawsModelsTests --tests "*.AwsModelCodegenCompilationTest" --stacktrace
5345
5446
- uses: actions/upload-artifact@v7
5547
if: failure()

.github/workflows/endpoint-compatibility.yml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ jobs:
1919
steps:
2020
- uses: actions/checkout@v6
2121

22-
- name: Checkout api-models-aws
23-
uses: actions/checkout@v6
24-
with:
25-
repository: aws/api-models-aws
26-
path: api-models-aws
27-
2822
- name: Set up JDK 21
2923
uses: actions/setup-java@v5
3024
with:
@@ -46,10 +40,8 @@ jobs:
4640
caches
4741
4842
- name: Run endpoint ruleset compatibility tests
49-
env:
50-
API_MODELS_AWS_DIR: ${{ github.workspace }}/api-models-aws
5143
run: |
52-
./gradlew :client:client-rulesengine:integ --tests "*.AwsEndpointRulesetTest" --stacktrace
44+
./gradlew :client:client-rulesengine:integ -PawsModelsTests --tests "*.AwsEndpointRulesetTest" --stacktrace
5345
5446
- uses: actions/upload-artifact@v7
5547
if: failure()

buildSrc/src/main/kotlin/smithy-java.integ-test-conventions.gradle.kts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11

2+
import javax.xml.parsers.DocumentBuilderFactory
3+
import org.w3c.dom.Element
4+
25
configure<SourceSetContainer> {
36
val main by getting
47
val test by getting
@@ -16,3 +19,54 @@ tasks.register<Test>("integ") {
1619
testClassesDirs = project.the<SourceSetContainer>()["it"].output.classesDirs
1720
classpath = project.the<SourceSetContainer>()["it"].runtimeClasspath
1821
}
22+
23+
// Extension for configuring integration test behavior.
24+
// Projects can opt in to AWS API model tests by adding:
25+
// configureIntegTests { awsModels = true }
26+
// Models are only resolved when the `-PawsModelsTests` Gradle property is also set.
27+
interface IntegTestExtension {
28+
var awsModelTests: Boolean
29+
}
30+
31+
val configureIntegTests = extensions.create<IntegTestExtension>("configureIntegTests").apply {
32+
awsModelTests = false
33+
}
34+
35+
afterEvaluate {
36+
if (!configureIntegTests.awsModelTests || !project.hasProperty("awsModelsTests")) {
37+
return@afterEvaluate
38+
}
39+
40+
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
41+
val bom = libs.findLibrary("aws-api-models-bom").get().get()
42+
val bomCoords = "${bom.module.group}:${bom.module.name}:${bom.version}"
43+
44+
val bomFile = configurations.detachedConfiguration(
45+
dependencies.platform(bomCoords),
46+
dependencies.create("$bomCoords@pom")
47+
).apply {
48+
isTransitive = false
49+
}.files.single { it.extension == "pom" }
50+
51+
val doc = DocumentBuilderFactory.newInstance()
52+
.newDocumentBuilder()
53+
.parse(bomFile)
54+
55+
val deps = doc.getElementsByTagName("dependency")
56+
dependencies.add("itImplementation", dependencies.platform(bomCoords))
57+
58+
for (i in 0 until deps.length) {
59+
val dep = deps.item(i) as? Element ?: continue
60+
val g = dep.getElementsByTagName("groupId").item(0)?.textContent ?: continue
61+
val a = dep.getElementsByTagName("artifactId").item(0)?.textContent ?: continue
62+
63+
if (g == "software.amazon.api.models") {
64+
dependencies.add("itImplementation", "$g:$a")
65+
}
66+
}
67+
68+
tasks.named<Test>("integ") {
69+
systemProperty("awsModelsTests", "true")
70+
maxHeapSize = "4g"
71+
}
72+
}

client/client-rulesengine/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,7 @@ dependencies {
1616
testImplementation(project(":client:dynamic-client"))
1717
testImplementation(project(":aws:client:aws-client-rulesengine"))
1818
}
19+
20+
configureIntegTests {
21+
awsModels = true
22+
}

client/client-rulesengine/src/it/java/software/amazon/smithy/java/client/rulesengine/AwsEndpointRulesetTest.java

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@
99
import static org.junit.jupiter.api.Assertions.assertNotNull;
1010
import static org.junit.jupiter.api.Assertions.assertTrue;
1111

12-
import java.io.IOException;
13-
import java.nio.file.Files;
14-
import java.nio.file.Path;
15-
import java.nio.file.Paths;
12+
import java.net.URL;
13+
import java.util.Comparator;
1614
import java.util.List;
1715
import java.util.stream.Stream;
18-
import org.junit.jupiter.api.condition.EnabledIf;
16+
import org.junit.jupiter.api.Named;
17+
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
1918
import org.junit.jupiter.params.ParameterizedTest;
2019
import org.junit.jupiter.params.provider.MethodSource;
2120
import software.amazon.smithy.java.rulesengine.Bytecode;
2221
import software.amazon.smithy.java.rulesengine.RulesEngineBuilder;
2322
import software.amazon.smithy.model.Model;
2423
import software.amazon.smithy.model.loader.ModelAssembler;
24+
import software.amazon.smithy.model.loader.ModelDiscovery;
2525
import software.amazon.smithy.model.shapes.ServiceShape;
2626
import software.amazon.smithy.rulesengine.language.EndpointRuleSet;
2727
import software.amazon.smithy.rulesengine.language.evaluation.TestEvaluator;
@@ -38,40 +38,36 @@
3838
* evaluated against its endpoint test cases, round-tripped through serialization,
3939
* and disassembled without errors.
4040
*
41-
* <p>Set the {@code API_MODELS_AWS_DIR} environment variable to the root of a local
42-
* checkout of api-models-aws to enable this test.
41+
* <p>AWS service models are discovered on the classpath via
42+
* {@link ModelDiscovery#findModels()}.
4343
*/
44+
@EnabledIfSystemProperty(named = "awsModelsTests", matches = "true")
4445
class AwsEndpointRulesetTest {
4546

46-
private static Path getModelsDir() {
47-
var dir = System.getenv("API_MODELS_AWS_DIR");
48-
return dir != null ? Paths.get(dir, "models") : null;
49-
}
50-
51-
static boolean modelsAvailable() {
52-
var dir = getModelsDir();
53-
return dir != null && Files.isDirectory(dir);
47+
static Stream<Named<URL>> awsModels() {
48+
return ModelDiscovery.findModels()
49+
.stream()
50+
.filter(url -> url.toString().endsWith(".json"))
51+
.map(url -> Named.of(artifactName(url), url))
52+
.sorted(Comparator.comparing(Named::getName));
5453
}
5554

56-
static Stream<Path> awsModels() throws IOException {
57-
var modelsDir = getModelsDir();
58-
try (var paths = Files.find(modelsDir,
59-
4,
60-
(p, a) -> p.toString().endsWith(".json")
61-
&& p.getParent().getParent().getFileName().toString().equals("service"))) {
62-
return paths.sorted().toList().stream();
63-
}
55+
private static String artifactName(URL modelUrl) {
56+
String urlStr = modelUrl.toString();
57+
int bangIdx = urlStr.indexOf("!/");
58+
String jarPath = urlStr.substring(0, bangIdx);
59+
String jarName = jarPath.substring(jarPath.lastIndexOf('/') + 1);
60+
return jarName.replaceFirst("-\\d[\\d.]*\\.jar$", "");
6461
}
6562

66-
@EnabledIf("modelsAvailable")
6763
@ParameterizedTest(name = "{0}")
6864
@MethodSource("awsModels")
69-
void compileEvaluateAndRoundTrip(Path modelPath) {
65+
void compileEvaluateAndRoundTrip(URL modelUrl) {
7066
// Load the model
7167
Model model = Model.assembler()
72-
.addImport(modelPath)
68+
.addImport(modelUrl)
7369
.putProperty(ModelAssembler.ALLOW_UNKNOWN_TRAITS, true)
74-
.discoverModels()
70+
.disableValidation()
7571
.assemble()
7672
.unwrap();
7773

codegen/codegen-plugin/build.gradle.kts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,6 @@ tasks.test {
9292
failOnNoDiscoveredTests = false
9393
}
9494

95-
// If API_MODELS_AWS_DIR is set, add it as a task input so Gradle doesn't cache results.
96-
System.getenv("API_MODELS_AWS_DIR")?.let { modelsDir ->
97-
val dir = file(modelsDir)
98-
if (dir.isDirectory) {
99-
tasks.named<Test>("integ") {
100-
inputs.dir(dir).withPathSensitivity(PathSensitivity.RELATIVE)
101-
maxHeapSize = "4g"
102-
}
103-
}
95+
configureIntegTests {
96+
awsModels = true
10497
}

codegen/codegen-plugin/src/it/java/software/amazon/smithy/java/codegen/AwsModelCodegenCompilationTest.java

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
import java.io.IOException;
1414
import java.io.OutputStream;
1515
import java.net.URI;
16+
import java.net.URL;
1617
import java.nio.file.Files;
1718
import java.nio.file.Path;
18-
import java.nio.file.Paths;
1919
import java.util.ArrayList;
2020
import java.util.Arrays;
21+
import java.util.Comparator;
2122
import java.util.List;
2223
import java.util.Set;
2324
import java.util.stream.Collectors;
@@ -33,7 +34,7 @@
3334
import javax.tools.StandardJavaFileManager;
3435
import javax.tools.ToolProvider;
3536
import org.junit.jupiter.api.Named;
36-
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
37+
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
3738
import org.junit.jupiter.api.parallel.Execution;
3839
import org.junit.jupiter.api.parallel.ExecutionMode;
3940
import org.junit.jupiter.params.ParameterizedTest;
@@ -42,51 +43,52 @@
4243
import software.amazon.smithy.build.PluginContext;
4344
import software.amazon.smithy.model.Model;
4445
import software.amazon.smithy.model.loader.ModelAssembler;
46+
import software.amazon.smithy.model.loader.ModelDiscovery;
4547
import software.amazon.smithy.model.node.ArrayNode;
4648
import software.amazon.smithy.model.node.ObjectNode;
4749
import software.amazon.smithy.model.shapes.ServiceShape;
4850

4951
/**
5052
* Tests that Java code generation produces compilable code for all AWS service models.
5153
*
52-
* <p>Set the {@code API_MODELS_AWS_DIR} environment variable to the root of a local
53-
* checkout of {@code aws/api-models-aws} to enable this test.
54+
* <p>AWS service models are discovered on the classpath via
55+
* {@link ModelDiscovery#findModels()}.
5456
*/
55-
@EnabledIfEnvironmentVariable(named = "API_MODELS_AWS_DIR", matches = ".*")
57+
@EnabledIfSystemProperty(named = "awsModelsTests", matches = "true")
5658
class AwsModelCodegenCompilationTest {
5759

5860
private static final Set<String> IGNORED_SDK_IDS = Set.of(
5961
"timestream-write",
6062
"timestream-query",
61-
"clouddirectory"
63+
"clouddirectory");
6264

63-
);
64-
65-
private static Path getModelsDir() {
66-
var dir = System.getenv("API_MODELS_AWS_DIR");
67-
return Paths.get(dir, "models");
65+
static Stream<Named<URL>> awsModels() {
66+
return ModelDiscovery.findModels()
67+
.stream()
68+
.filter(url -> url.toString().endsWith(".json"))
69+
.map(url -> Named.of(artifactName(url), url))
70+
.sorted(Comparator.comparing(Named::getName));
6871
}
6972

70-
static Stream<Named<Path>> awsModels() throws IOException {
71-
var modelsDir = getModelsDir();
72-
return Files.find(modelsDir,
73-
4,
74-
(p, a) -> p.toString().endsWith(".json")
75-
&& p.getParent().getParent().getFileName().toString().equals("service"))
76-
.map(p -> Named.of(sdkId(modelsDir, p), p));
73+
private static String artifactName(URL modelUrl) {
74+
String urlStr = modelUrl.toString();
75+
int bangIdx = urlStr.indexOf("!/");
76+
String jarPath = urlStr.substring(0, bangIdx);
77+
String jarName = jarPath.substring(jarPath.lastIndexOf('/') + 1);
78+
return jarName.replaceFirst("-\\d[\\d.]*\\.jar$", "");
7779
}
7880

7981
@ParameterizedTest(name = "client: {0}")
8082
@MethodSource("awsModels")
8183
@Execution(ExecutionMode.CONCURRENT)
82-
void compileGeneratedCode(Path modelPath) {
83-
generateAndCompile(modelPath, "client", "server");
84+
void compileGeneratedCode(URL modelUrl) {
85+
generateAndCompile(modelUrl, "client", "server");
8486
}
8587

86-
private void generateAndCompile(Path modelPath, String... modes) {
88+
private void generateAndCompile(URL modelUrl, String... modes) {
8789
// 1. Load model
8890
Model model = Model.assembler()
89-
.addImport(modelPath)
91+
.addImport(modelUrl)
9092
.putProperty(ModelAssembler.ALLOW_UNKNOWN_TRAITS, true)
9193
.disableValidation()
9294
.assemble()
@@ -149,7 +151,7 @@ private void generateAndCompile(Path modelPath, String... modes) {
149151
}
150152
}
151153
} catch (Throwable t) {
152-
if (IGNORED_SDK_IDS.contains(sdkId(getModelsDir(), modelPath))) {
154+
if (IGNORED_SDK_IDS.contains(artifactName(modelUrl))) {
153155
abort("Known failure for " + service.getId() + ": " + t.getMessage());
154156
}
155157
sneakyThrow(t);
@@ -177,10 +179,6 @@ private static Path dumpGeneratedSources(String serviceId, List<JavaFileObject>
177179
}
178180
}
179181

180-
private static String sdkId(Path modelsDir, Path modelPath) {
181-
return modelsDir.relativize(modelPath).getName(0).toString();
182-
}
183-
184182
private static String sanitize(String name) {
185183
return name.toLowerCase().replaceAll("[^a-z0-9]", "");
186184
}

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ jazzer = "0.30.0"
2222
json-schema-validator = "3.0.0"
2323
opentelemetry = "1.60.1"
2424
jspecify = "1.0.0"
25+
aws-api-models = "1.0.204"
2526

2627
[libraries]
2728
smithy-model = { module = "software.amazon.smithy:smithy-model", version.ref = "smithy" }
@@ -73,6 +74,7 @@ jazzer-junit = { module = "com.code-intelligence:jazzer-junit", version.ref = "j
7374
jazzer-api = { module = "com.code-intelligence:jazzer-api", version.ref = "jazzer" }
7475
json-schema-validator = { module = "com.networknt:json-schema-validator", version.ref = "json-schema-validator"}
7576
jspecify = { module = "org.jspecify:jspecify", version.ref = "jspecify" }
77+
aws-api-models-bom = { module = "software.amazon.api.models:all", version.ref = "aws-api-models" }
7678

7779
# plugin artifacts for buildsrc plugins
7880
test-logger-plugin = { module = "com.adarshr:gradle-test-logger-plugin", version.ref = "test-logger-plugin" }

0 commit comments

Comments
 (0)