Skip to content

Commit c39ee38

Browse files
authored
fix: unbreak enum-based waiters and render null-aware acceptors (#842)
1 parent bffc45f commit c39ee38

File tree

8 files changed

+1693
-544
lines changed

8 files changed

+1693
-544
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": "fe9d51c3-98cd-4c45-aed3-a797d278a36c",
3+
"type": "bugfix",
4+
"description": "Fix broken enum-based waiters.",
5+
"issues": [
6+
"awslabs/aws-sdk-kotlin#892"
7+
]
8+
}

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/waiters/AcceptorGenerator.kt

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
1111
import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes
1212
import software.amazon.smithy.kotlin.codegen.core.withBlock
1313
import software.amazon.smithy.kotlin.codegen.utils.dq
14+
import software.amazon.smithy.model.shapes.ShapeId
15+
import software.amazon.smithy.model.shapes.StructureShape
1416
import software.amazon.smithy.waiters.*
1517

1618
/**
1719
* Renders an individual acceptor for a waiter.
1820
*/
19-
private fun KotlinWriter.renderAcceptor(acceptor: Acceptor) {
21+
private fun KotlinWriter.renderAcceptor(wi: WaiterInfo, acceptor: Acceptor) {
2022
addImport(RuntimeTypes.Core.Retries.Policy.RetryDirective)
2123

2224
val directive = when (acceptor.state!!) {
@@ -39,8 +41,8 @@ private fun KotlinWriter.renderAcceptor(acceptor: Acceptor) {
3941
write("ErrorTypeAcceptor(RetryDirective.#L, #L),", directive, matcher.value.dq())
4042
}
4143

42-
is Matcher.InputOutputMember -> renderPathAcceptor(directive, true, matcher.value)
43-
is Matcher.OutputMember -> renderPathAcceptor(directive, false, matcher.value)
44+
is Matcher.InputOutputMember -> renderPathAcceptor(wi, directive, true, matcher.value)
45+
is Matcher.OutputMember -> renderPathAcceptor(wi, directive, false, matcher.value)
4446
else -> throw CodegenException("""Unknown matcher type "${matcher::class}"""")
4547
}
4648
}
@@ -58,38 +60,49 @@ internal fun KotlinWriter.renderAcceptorList(wi: WaiterInfo, asValName: String)
5860
wi.inputSymbol,
5961
wi.outputSymbol,
6062
) {
61-
wi.waiter.acceptors.forEach(::renderAcceptor)
63+
wi.waiter.acceptors.forEach { renderAcceptor(wi, it) }
6264
}
6365
}
6466

6567
/**
6668
* Render a path-based acceptor (i.e., one that uses an output or inputOutput matcher).
6769
*/
68-
private fun KotlinWriter.renderPathAcceptor(directive: String, includeInput: Boolean, matcher: PathMatcher) {
70+
private fun KotlinWriter.renderPathAcceptor(wi: WaiterInfo, directive: String, includeInput: Boolean, matcher: PathMatcher) {
6971
val acceptorType = if (includeInput) {
70-
addImport(RuntimeTypes.Core.Retries.Policy.InputOutputAcceptor)
71-
"InputOutputAcceptor"
72+
RuntimeTypes.Core.Retries.Policy.InputOutputAcceptor
7273
} else {
73-
addImport(RuntimeTypes.Core.Retries.Policy.OutputAcceptor)
74-
"OutputAcceptor"
74+
RuntimeTypes.Core.Retries.Policy.OutputAcceptor
7575
}
7676

77-
withBlock("#L(RetryDirective.#L) {", "},", acceptorType, directive) {
78-
val visitor = KotlinJmespathExpressionVisitor(this)
77+
val visitedShape = if (includeInput) {
78+
buildSyntheticInputOutputShape(wi.op.id.name, wi.input.id, wi.output.id)
79+
} else {
80+
wi.output
81+
}
82+
83+
withBlock("#T(RetryDirective.#L) {", "},", acceptorType, directive) {
84+
val visitor = KotlinJmespathExpressionVisitor(wi.ctx, this, visitedShape)
7985
val expression = JmespathExpression.parse(matcher.path)
8086
val actual = expression.accept(visitor)
8187

8288
val expected = matcher.expected
8389
val comparison = when (matcher.comparator!!) {
84-
PathComparator.STRING_EQUALS -> "$actual?.toString() == ${expected.dq()}"
85-
PathComparator.BOOLEAN_EQUALS -> "$actual == ${expected.toBoolean()}"
86-
PathComparator.ANY_STRING_EQUALS -> "$actual?.any { it?.toString() == ${expected.dq()} } ?: false"
90+
PathComparator.STRING_EQUALS -> "${actual.identifier} == ${expected.dq()}"
91+
PathComparator.BOOLEAN_EQUALS -> "${actual.identifier} == ${expected.toBoolean()}"
92+
PathComparator.ANY_STRING_EQUALS -> "${actual.identifier}?.any { it == ${expected.dq()} } ?: false"
8793

88-
// NOTE: the size > 0 check is necessary because the waiter spec says that `allStringEquals` requires
94+
// NOTE: the isNotEmpty check is necessary because the waiter spec says that `allStringEquals` requires
8995
// at least one value unlike Kotlin's `all` which returns true if the collection is empty
9096
PathComparator.ALL_STRING_EQUALS ->
91-
"$actual != null && $actual.size > 0 && $actual.all { it?.toString() == ${expected.dq()} }"
97+
"!${actual.identifier}.isNullOrEmpty() && ${actual.identifier}.all { it == ${expected.dq()} }"
9298
}
9399
write(comparison)
94100
}
95101
}
102+
103+
private fun buildSyntheticInputOutputShape(opName: String, input: ShapeId, output: ShapeId): StructureShape =
104+
StructureShape.Builder()
105+
.id("software.amazon.smithy.kotlin.codegen.rendering.waiters.synthetic#${opName}RequestResponse")
106+
.addMember("input", input)
107+
.addMember("output", output)
108+
.build()

0 commit comments

Comments
 (0)