Skip to content

Commit 2657411

Browse files
committed
Merge remote-tracking branch 'protocols/main' into relocate-protocols
This commit relocates AWS protocol support from `awslabs/aws-sdk-kotlin` to `smithy-kotlin` directly. Original commits have been kept and merged as unrelated history.
2 parents d607dad + 61a9de3 commit 2657411

30 files changed

+2958
-0
lines changed

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: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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.codegen.smithyKotlinProjectionSrcDir
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+
data class ProtocolTest(val projectionName: String, val serviceShapeId: String, val sdkId: String? = null) {
18+
val packageName: String = projectionName.lowercase().filter { it.isLetterOrDigit() }
19+
}
20+
21+
// The following section exposes Smithy protocol test suites as gradle test targets
22+
// for the configured protocols in [enabledProtocols].
23+
val enabledProtocols = listOf(
24+
ProtocolTest("aws-ec2-query", "aws.protocoltests.ec2#AwsEc2"),
25+
ProtocolTest("aws-json-10", "aws.protocoltests.json10#JsonRpc10"),
26+
ProtocolTest("aws-json-11", "aws.protocoltests.json#JsonProtocol"),
27+
ProtocolTest("aws-restjson", "aws.protocoltests.restjson#RestJson"),
28+
ProtocolTest("aws-restxml", "aws.protocoltests.restxml#RestXml"),
29+
ProtocolTest("aws-restxml-xmlns", "aws.protocoltests.restxml.xmlns#RestXmlWithNamespace"),
30+
ProtocolTest("aws-query", "aws.protocoltests.query#AwsQuery"),
31+
32+
// service specific tests
33+
ProtocolTest("apigateway", "com.amazonaws.apigateway#BackplaneControlService"),
34+
ProtocolTest("glacier", "com.amazonaws.glacier#Glacier"),
35+
ProtocolTest("machinelearning", "com.amazonaws.machinelearning#AmazonML_20141212", sdkId = "Machine Learning"),
36+
37+
// Custom hand written tests
38+
ProtocolTest("error-correction-json", "aws.protocoltests.errorcorrection#RequiredValueJson"),
39+
ProtocolTest("error-correction-xml", "aws.protocoltests.errorcorrection#RequiredValueXml"),
40+
)
41+
42+
smithyBuild {
43+
enabledProtocols.forEach { test ->
44+
projections.register(test.projectionName) {
45+
imports = listOf(file("model").absolutePath)
46+
47+
transforms = listOf(
48+
"""
49+
{
50+
"name": "includeServices",
51+
"args": {
52+
"services": ["${test.serviceShapeId}"]
53+
}
54+
}
55+
""",
56+
)
57+
58+
smithyKotlinPlugin {
59+
serviceShapeId = test.serviceShapeId
60+
packageName = "aws.sdk.kotlin.services.${test.packageName}"
61+
packageVersion = "1.0"
62+
sdkId = test.sdkId
63+
buildSettings {
64+
generateFullProject = true
65+
optInAnnotations = listOf(
66+
"aws.smithy.kotlin.runtime.InternalApi",
67+
"aws.sdk.kotlin.runtime.InternalSdkApi",
68+
)
69+
}
70+
apiSettings {
71+
defaultValueSerializationMode = "always"
72+
}
73+
}
74+
}
75+
}
76+
}
77+
78+
val codegen by configurations.getting
79+
dependencies {
80+
codegen(project(":codegen:aws-sdk-codegen"))
81+
codegen(libs.smithy.cli)
82+
codegen(libs.smithy.model)
83+
84+
// NOTE: The protocol tests are published to maven as a jar, this ensures that
85+
// the aws-protocol-tests dependency is found when generating code such that the `includeServices` transform
86+
// actually works
87+
codegen(libs.smithy.aws.protocol.tests)
88+
}
89+
90+
tasks.generateSmithyProjections {
91+
// ensure the generated clients use the same version of the runtime as the aws aws-runtime
92+
val smithyKotlinRuntimeVersion = libs.versions.smithy.kotlin.runtime.version.get()
93+
doFirst {
94+
System.setProperty("smithy.kotlin.codegen.clientRuntimeVersion", smithyKotlinRuntimeVersion)
95+
}
96+
}
97+
98+
abstract class ProtocolTestTask @Inject constructor(private val project: Project) : DefaultTask() {
99+
/**
100+
* The projection
101+
*/
102+
@get:Input
103+
abstract val projectionName: Property<String>
104+
105+
/**
106+
* The projection root directory
107+
*/
108+
@get:Input
109+
abstract val projectionRootDirectory: Property<String>
110+
111+
@TaskAction
112+
fun runTests() {
113+
val projectionRootDir = project.file(projectionRootDirectory.get())
114+
println("[$projectionName] buildDir: $projectionRootDir")
115+
if (!projectionRootDir.exists()) {
116+
throw GradleException("$projectionRootDir does not exist")
117+
}
118+
val wrapper = if (System.getProperty("os.name").lowercase().contains("windows")) "gradlew.bat" else "gradlew"
119+
val gradlew = project.rootProject.file(wrapper).absolutePath
120+
121+
// NOTE - this still requires us to publish to maven local.
122+
project.exec {
123+
workingDir = projectionRootDir
124+
executable = gradlew
125+
args = listOf("test")
126+
}
127+
}
128+
}
129+
130+
smithyBuild.projections.forEach {
131+
val protocolName = it.name
132+
133+
tasks.register<ProtocolTestTask>("testProtocol-$protocolName") {
134+
dependsOn(tasks.generateSmithyProjections)
135+
group = "Verification"
136+
projectionName.set(it.name)
137+
projectionRootDirectory.set(smithyBuild.smithyKotlinProjectionPath(it.name).map { it.toString() })
138+
}
139+
140+
// FIXME This is a hack to work around how protocol tests aren't in the actual service model and thus codegen
141+
// separately from service customizations.
142+
val copyStaticFiles = tasks.register<Copy>("copyStaticFiles-$protocolName") {
143+
group = "codegen"
144+
from(rootProject.projectDir.resolve("services/$protocolName/common/src"))
145+
into(smithyBuild.smithyKotlinProjectionSrcDir(it.name))
146+
}
147+
148+
tasks.generateSmithyProjections.configure {
149+
finalizedBy(copyStaticFiles)
150+
}
151+
}
152+
153+
tasks.register("testAllProtocols") {
154+
group = "Verification"
155+
val allTests = tasks.withType<ProtocolTestTask>()
156+
dependsOn(allTests)
157+
}
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)