Skip to content

Commit 636b86f

Browse files
authored
refactor: relocate AWS protocol support #1051
Relocates AWS protocol support and protocol tests from `aws-sdk-kotlin` into `smithy-kotlin`. Commit history has been preserved. If you want to see the actual changes look at commits starting from `ae7a697`
2 parents d607dad + ae7a697 commit 636b86f

File tree

38 files changed

+3012
-2
lines changed

38 files changed

+3012
-2
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"id": "512f819d-8276-4437-9fdf-89342106aa40",
3+
"type": "misc",
4+
"description": "Relocate AWS protocol codegen support from `aws-sdk-kotlin`"
5+
}

.github/workflows/continuous-integration.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,23 @@ jobs:
7070
name: test-reports-${{ matrix.os }}
7171
path: '**/build/reports'
7272

73+
protocol-tests:
74+
runs-on: ubuntu-latest
75+
steps:
76+
- name: Checkout sources
77+
uses: actions/checkout@v4
78+
- name: Configure JDK
79+
uses: actions/setup-java@v3
80+
with:
81+
distribution: 'corretto'
82+
java-version: 17
83+
cache: 'gradle'
84+
- name: Test
85+
shell: bash
86+
run: |
87+
./gradlew publishToMavenLocal
88+
./gradlew testAllProtocols
89+
7390
downstream:
7491
runs-on: ubuntu-latest
7592
steps:

build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ apiValidation {
126126
"testing",
127127
"smithy-kotlin-codegen",
128128
"smithy-kotlin-codegen-testutils",
129+
"smithy-aws-kotlin-codegen",
130+
"protocol-tests",
129131
"aws-signing-benchmarks",
130132
"channel-benchmarks",
131133
"http-benchmarks",

codegen/protocol-tests/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
smithy-build.json
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import aws.sdk.kotlin.gradle.codegen.dsl.generateSmithyProjections
6+
import aws.sdk.kotlin.gradle.codegen.dsl.smithyKotlinPlugin
7+
import aws.sdk.kotlin.gradle.codegen.smithyKotlinProjectionPath
8+
import aws.sdk.kotlin.gradle.dsl.skipPublishing
9+
10+
plugins {
11+
kotlin("jvm")
12+
alias(libs.plugins.aws.kotlin.repo.tools.smithybuild)
13+
}
14+
15+
description = "Smithy protocol test suite"
16+
17+
skipPublishing()
18+
19+
data class ProtocolTest(val projectionName: String, val serviceShapeId: String, val sdkId: String? = null) {
20+
val packageName: String = projectionName.lowercase().filter { it.isLetterOrDigit() }
21+
}
22+
23+
// The following section exposes Smithy protocol test suites as gradle test targets
24+
// for the configured protocols in [enabledProtocols].
25+
val enabledProtocols = listOf(
26+
ProtocolTest("aws-ec2-query", "aws.protocoltests.ec2#AwsEc2"),
27+
ProtocolTest("aws-json-10", "aws.protocoltests.json10#JsonRpc10"),
28+
ProtocolTest("aws-json-11", "aws.protocoltests.json#JsonProtocol"),
29+
ProtocolTest("aws-restjson", "aws.protocoltests.restjson#RestJson"),
30+
ProtocolTest("aws-restxml", "aws.protocoltests.restxml#RestXml"),
31+
ProtocolTest("aws-restxml-xmlns", "aws.protocoltests.restxml.xmlns#RestXmlWithNamespace"),
32+
ProtocolTest("aws-query", "aws.protocoltests.query#AwsQuery"),
33+
34+
// Custom hand written tests
35+
ProtocolTest("error-correction-json", "aws.protocoltests.errorcorrection#RequiredValueJson"),
36+
ProtocolTest("error-correction-xml", "aws.protocoltests.errorcorrection#RequiredValueXml"),
37+
)
38+
39+
smithyBuild {
40+
enabledProtocols.forEach { test ->
41+
projections.register(test.projectionName) {
42+
imports = listOf(file("model").absolutePath)
43+
44+
transforms = listOf(
45+
"""
46+
{
47+
"name": "includeServices",
48+
"args": {
49+
"services": ["${test.serviceShapeId}"]
50+
}
51+
}
52+
""",
53+
)
54+
55+
smithyKotlinPlugin {
56+
serviceShapeId = test.serviceShapeId
57+
packageName = "aws.sdk.kotlin.services.${test.packageName}"
58+
packageVersion = "1.0"
59+
sdkId = test.sdkId
60+
buildSettings {
61+
generateFullProject = true
62+
optInAnnotations = listOf(
63+
"aws.smithy.kotlin.runtime.InternalApi",
64+
"aws.sdk.kotlin.runtime.InternalSdkApi",
65+
)
66+
}
67+
apiSettings {
68+
defaultValueSerializationMode = "always"
69+
}
70+
}
71+
}
72+
}
73+
}
74+
75+
val codegen by configurations.getting
76+
dependencies {
77+
codegen(project(":codegen:smithy-aws-kotlin-codegen"))
78+
codegen(libs.smithy.cli)
79+
codegen(libs.smithy.model)
80+
81+
// NOTE: The protocol tests are published to maven as a jar, this ensures that
82+
// the aws-protocol-tests dependency is found when generating code such that the `includeServices` transform
83+
// actually works
84+
codegen(libs.smithy.aws.protocol.tests)
85+
}
86+
87+
tasks.generateSmithyProjections {
88+
// ensure the generated clients use the same version of the runtime as the aws aws-runtime
89+
val sdkVersion: String by project
90+
val smithyKotlinRuntimeVersion = sdkVersion
91+
doFirst {
92+
System.setProperty("smithy.kotlin.codegen.clientRuntimeVersion", smithyKotlinRuntimeVersion)
93+
}
94+
}
95+
96+
abstract class ProtocolTestTask @Inject constructor(private val project: Project) : DefaultTask() {
97+
/**
98+
* The projection
99+
*/
100+
@get:Input
101+
abstract val projectionName: Property<String>
102+
103+
/**
104+
* The projection root directory
105+
*/
106+
@get:Input
107+
abstract val projectionRootDirectory: Property<String>
108+
109+
@TaskAction
110+
fun runTests() {
111+
val projectionRootDir = project.file(projectionRootDirectory.get())
112+
println("[$projectionName] buildDir: $projectionRootDir")
113+
if (!projectionRootDir.exists()) {
114+
throw GradleException("$projectionRootDir does not exist")
115+
}
116+
val wrapper = if (System.getProperty("os.name").lowercase().contains("windows")) "gradlew.bat" else "gradlew"
117+
val gradlew = project.rootProject.file(wrapper).absolutePath
118+
119+
// NOTE - this still requires us to publish to maven local.
120+
project.exec {
121+
workingDir = projectionRootDir
122+
executable = gradlew
123+
args = listOf("test")
124+
}
125+
}
126+
}
127+
128+
smithyBuild.projections.forEach {
129+
val protocolName = it.name
130+
131+
tasks.register<ProtocolTestTask>("testProtocol-$protocolName") {
132+
dependsOn(tasks.generateSmithyProjections)
133+
group = "Verification"
134+
projectionName.set(it.name)
135+
projectionRootDirectory.set(smithyBuild.smithyKotlinProjectionPath(it.name).map { it.toString() })
136+
}
137+
}
138+
139+
tasks.register("testAllProtocols") {
140+
group = "Verification"
141+
val allTests = tasks.withType<ProtocolTestTask>()
142+
dependsOn(allTests)
143+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
$version: "2.0"
2+
3+
namespace aws.protocoltests.errorcorrection
4+
5+
use aws.api#service
6+
use aws.protocols#awsJson1_0
7+
use aws.protocols#restXml
8+
use smithy.test#httpResponseTests
9+
10+
@service(sdkId: "Error Correction Json")
11+
@awsJson1_0
12+
service RequiredValueJson {
13+
operations: [SayHello],
14+
version: "1"
15+
}
16+
17+
18+
@service(sdkId: "Error Correction Xml")
19+
@restXml
20+
service RequiredValueXml {
21+
operations: [SayHelloXml],
22+
version: "1"
23+
}
24+
25+
@error("client")
26+
structure Error {
27+
@required
28+
requestId: String
29+
30+
@required
31+
message: String
32+
}
33+
34+
@http(method: "POST", uri: "/")
35+
operation SayHello { output: TestOutputDocument, errors: [Error] }
36+
37+
@http(method: "POST", uri: "/")
38+
operation SayHelloXml { output: TestOutput, errors: [Error] }
39+
40+
structure TestOutputDocument with [TestStruct] { innerField: Nested, @required document: Document }
41+
structure TestOutput with [TestStruct] { innerField: Nested }
42+
43+
@mixin
44+
structure TestStruct {
45+
@required
46+
foo: String,
47+
48+
@required
49+
byteValue: Byte,
50+
51+
@required
52+
intValue: Integer,
53+
54+
@required
55+
listValue: StringList,
56+
57+
@required
58+
mapValue: ListMap,
59+
60+
@required
61+
nestedListValue: NestedList
62+
63+
@required
64+
nested: Nested
65+
66+
@required
67+
blob: Blob
68+
69+
@required
70+
enum: MyEnum
71+
72+
@required
73+
union: MyUnion
74+
75+
notRequired: String
76+
77+
@required
78+
timestampValue: Timestamp
79+
}
80+
81+
enum MyEnum {
82+
A,
83+
B,
84+
C
85+
}
86+
87+
union MyUnion {
88+
A: Integer,
89+
B: String,
90+
C: Unit
91+
}
92+
93+
structure Nested {
94+
@required
95+
a: String
96+
}
97+
98+
list StringList {
99+
member: String
100+
}
101+
102+
list NestedList {
103+
member: StringList
104+
}
105+
106+
map ListMap {
107+
key: String,
108+
value: StringList
109+
}
110+
111+
// NOTE: there is no way to model enum or union defaults in an `httpResponseTest` because the default is the generated
112+
// "SdkUnknown" variant.
113+
apply SayHello @httpResponseTests([
114+
{
115+
id: "error_recovery_json",
116+
protocol: awsJson1_0,
117+
params: {
118+
union: { A: 5 },
119+
enum: "A",
120+
foo: "",
121+
byteValue: 0,
122+
intValue: 0,
123+
blob: "",
124+
listValue: [],
125+
mapValue: {},
126+
nestedListValue: [],
127+
document: null,
128+
nested: null,
129+
timestampValue: 0
130+
},
131+
code: 200,
132+
body: "{\"union\": { \"A\": 5 }, \"enum\": \"A\" }"
133+
}
134+
])
135+
136+
apply SayHelloXml @httpResponseTests([
137+
{
138+
id: "error_recovery_xml",
139+
protocol: restXml,
140+
params: {
141+
union: { A: 5 },
142+
enum: "A",
143+
foo: "",
144+
byteValue: 0,
145+
intValue: 0,
146+
blob: "",
147+
listValue: [],
148+
mapValue: {},
149+
nestedListValue: [],
150+
nested: null,
151+
timestampValue: 0
152+
},
153+
code: 200,
154+
body: "<TestOutput><union><A>5</A></union><enum>A</enum></TestOutput>"
155+
}
156+
])

0 commit comments

Comments
 (0)