Skip to content

Commit be453ba

Browse files
authored
feat: add type function to JMESPath visitor (#966)
1 parent 3934d48 commit be453ba

File tree

7 files changed

+307
-2
lines changed

7 files changed

+307
-2
lines changed

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ object RuntimeTypes {
165165
val truthiness = symbol("truthiness")
166166
val urlEncodeComponent = symbol("urlEncodeComponent", "text")
167167
val toNumber = symbol("toNumber")
168+
val type = symbol("type")
168169
}
169170

170171
object Net : RuntimeTypePackage(KotlinDependency.CORE, "net") {

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,14 @@ class KotlinJmespathExpressionVisitor(
219219
private fun FunctionExpression.args(): List<VisitedExpression> =
220220
this.arguments.map { acceptSubexpression(it) }
221221

222-
private fun VisitedExpression.dotFunction(expression: FunctionExpression, expr: String, elvisExpr: String? = null, isObject: Boolean = false): VisitedExpression {
223-
val dotFunctionExpr = ensureNullGuard(shape, expr, elvisExpr)
222+
private fun VisitedExpression.dotFunction(
223+
expression: FunctionExpression,
224+
expr: String,
225+
elvisExpr: String? = null,
226+
isObject: Boolean = false,
227+
ensureNullGuard: Boolean = true,
228+
): VisitedExpression {
229+
val dotFunctionExpr = if (ensureNullGuard) ensureNullGuard(shape, expr, elvisExpr) else ".$expr"
224230
val ident = addTempVar(expression.name.toCamelCase(), "$identifier$dotFunctionExpr")
225231

226232
shape?.let { shapeCursor.addLast(shape) }
@@ -322,6 +328,12 @@ class KotlinJmespathExpressionVisitor(
322328
arg.dotFunction(expression, "toNumber()")
323329
}
324330

331+
"type" -> {
332+
writer.addImport(RuntimeTypes.Core.Utils.type)
333+
val arg = expression.singleArg()
334+
arg.dotFunction(expression, "type()", ensureNullGuard = false)
335+
}
336+
325337
else -> throw CodegenException("Unknown function type in $expression")
326338
}
327339

runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/util/JMESPath.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,19 @@ public inline fun <reified T> Collection<Collection<T>>.flattenIfPossible(): Col
5656
@InternalApi
5757
public val <T> Collection<T>.length: Int
5858
get() = size
59+
60+
/**
61+
* Returns a JS type as a string
62+
*
63+
* See: [JMESPath spec](https://jmespath.org/specification.html#type)
64+
*/
65+
@InternalApi
66+
public fun Any?.type(): String = when (this) {
67+
is String -> "string"
68+
is Boolean -> "boolean"
69+
is List<*>, is Array<*> -> "array"
70+
is Number -> "number"
71+
is Any -> "object"
72+
null -> "null"
73+
else -> throw Exception("Undetected type for: $this")
74+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
$version: "2"
2+
namespace com.test
3+
4+
use smithy.waiters#waitable
5+
6+
@suppress(["WaitableTraitJmespathProblem"])
7+
@waitable(
8+
TypeFunctionStringEquals: {
9+
acceptors: [
10+
{
11+
state: "success",
12+
matcher: {
13+
output: {
14+
path: "type(primitives.string)",
15+
expected: "string",
16+
comparator: "stringEquals"
17+
}
18+
}
19+
}
20+
]
21+
},
22+
TypeFunctionBooleanEquals: {
23+
acceptors: [
24+
{
25+
state: "success",
26+
matcher: {
27+
output: {
28+
path: "type(primitives.boolean)",
29+
expected: "boolean",
30+
comparator: "stringEquals"
31+
}
32+
}
33+
}
34+
]
35+
},
36+
TypeFunctionArrayEquals: {
37+
acceptors: [
38+
{
39+
state: "success",
40+
matcher: {
41+
output: {
42+
path: "type(lists.booleans)",
43+
expected: "array",
44+
comparator: "stringEquals"
45+
}
46+
}
47+
}
48+
]
49+
},
50+
TypeFunctionShortEquals: {
51+
acceptors: [
52+
{
53+
state: "success",
54+
matcher: {
55+
output: {
56+
path: "type(primitives.short)",
57+
expected: "number",
58+
comparator: "stringEquals"
59+
}
60+
}
61+
}
62+
]
63+
},
64+
TypeFunctionIntegerEquals: {
65+
acceptors: [
66+
{
67+
state: "success",
68+
matcher: {
69+
output: {
70+
path: "type(primitives.integer)",
71+
expected: "number",
72+
comparator: "stringEquals"
73+
}
74+
}
75+
}
76+
]
77+
},
78+
TypeFunctionLongEquals: {
79+
acceptors: [
80+
{
81+
state: "success",
82+
matcher: {
83+
output: {
84+
path: "type(primitives.long)",
85+
expected: "number",
86+
comparator: "stringEquals"
87+
}
88+
}
89+
}
90+
]
91+
},
92+
TypeFunctionFloatEquals: {
93+
acceptors: [
94+
{
95+
state: "success",
96+
matcher: {
97+
output: {
98+
path: "type(primitives.float)",
99+
expected: "number",
100+
comparator: "stringEquals"
101+
}
102+
}
103+
}
104+
]
105+
},
106+
TypeFunctionDoubleEquals: {
107+
acceptors: [
108+
{
109+
state: "success",
110+
matcher: {
111+
output: {
112+
path: "type(primitives.double)",
113+
expected: "number",
114+
comparator: "stringEquals"
115+
}
116+
}
117+
}
118+
]
119+
},
120+
TypeFunctionObjectEquals: {
121+
acceptors: [
122+
{
123+
state: "success",
124+
matcher: {
125+
output: {
126+
path: "type(primitives)",
127+
expected: "object",
128+
comparator: "stringEquals"
129+
}
130+
}
131+
}
132+
]
133+
},
134+
TypeFunctionMergedObjectEquals: {
135+
acceptors: [
136+
{
137+
state: "success",
138+
matcher: {
139+
output: {
140+
path: "type(merge(primitives, primitives))",
141+
expected: "object",
142+
comparator: "stringEquals"
143+
}
144+
}
145+
}
146+
]
147+
},
148+
TypeFunctionNullEquals: {
149+
acceptors: [
150+
{
151+
state: "success",
152+
matcher: {
153+
output: {
154+
path: "type(primitives.boolean)",
155+
expected: "null",
156+
comparator: "stringEquals"
157+
}
158+
}
159+
}
160+
]
161+
},
162+
)
163+
@readonly
164+
@http(method: "GET", uri: "/type/{name}", code: 200)
165+
operation GetFunctionTypeEquals {
166+
input: GetEntityRequest,
167+
output: GetEntityResponse,
168+
errors: [NotFound],
169+
}

tests/codegen/waiter-tests/model/utils/waiter-operations.smithy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ service WaitersTestService {
3030
GetFunctionToArrayEquals,
3131
GetFunctionToStringEquals,
3232
GetFunctionToNumberEquals,
33+
GetFunctionTypeEquals,
3334
]
3435
}

tests/codegen/waiter-tests/src/main/kotlin/com/test/DefaultWaitersTestClient.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class DefaultWaitersTestClient<T>(resultList: List<Result<T>>) : WaitersTestClie
7272

7373
override suspend fun getFunctionToNumberEquals(input: GetFunctionToNumberEqualsRequest): GetFunctionToNumberEqualsResponse = findSuccess()
7474

75+
override suspend fun getFunctionTypeEquals(input: GetFunctionTypeEqualsRequest): GetFunctionTypeEqualsResponse = findSuccess()
76+
7577
private fun <Response> findSuccess(): Response {
7678
val nextResult = results.next()
7779
@Suppress("UNCHECKED_CAST")
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package com.test
7+
8+
import com.test.model.EntityLists
9+
import com.test.model.EntityPrimitives
10+
import com.test.model.GetFunctionTypeEqualsRequest
11+
import com.test.model.GetFunctionTypeEqualsResponse
12+
import com.test.utils.successTest
13+
import com.test.waiters.*
14+
import kotlin.test.Test
15+
16+
class FunctionTypeTest {
17+
@Test fun testTypeFunctionStringEquals() = successTest(
18+
GetFunctionTypeEqualsRequest { name = "test" },
19+
WaitersTestClient::waitUntilTypeFunctionStringEquals,
20+
GetFunctionTypeEqualsResponse {
21+
primitives = EntityPrimitives { string = "foo" }
22+
},
23+
)
24+
25+
@Test fun testTypeFunctionBooleanEquals() = successTest(
26+
GetFunctionTypeEqualsRequest { name = "test" },
27+
WaitersTestClient::waitUntilTypeFunctionBooleanEquals,
28+
GetFunctionTypeEqualsResponse {
29+
primitives = EntityPrimitives { boolean = true }
30+
},
31+
)
32+
33+
@Test fun testTypeFunctionArrayEquals() = successTest(
34+
GetFunctionTypeEqualsRequest { name = "test" },
35+
WaitersTestClient::waitUntilTypeFunctionArrayEquals,
36+
GetFunctionTypeEqualsResponse {
37+
lists = EntityLists { booleans = listOf(true) }
38+
},
39+
)
40+
41+
@Test fun testTypeFunctionShortEquals() = successTest(
42+
GetFunctionTypeEqualsRequest { name = "test" },
43+
WaitersTestClient::waitUntilTypeFunctionShortEquals,
44+
GetFunctionTypeEqualsResponse {
45+
primitives = EntityPrimitives { short = 1 }
46+
},
47+
)
48+
49+
@Test fun testTypeFunctionIntegerEquals() = successTest(
50+
GetFunctionTypeEqualsRequest { name = "test" },
51+
WaitersTestClient::waitUntilTypeFunctionIntegerEquals,
52+
GetFunctionTypeEqualsResponse {
53+
primitives = EntityPrimitives { integer = 1 }
54+
},
55+
)
56+
57+
@Test fun testTypeFunctionLongEquals() = successTest(
58+
GetFunctionTypeEqualsRequest { name = "test" },
59+
WaitersTestClient::waitUntilTypeFunctionLongEquals,
60+
GetFunctionTypeEqualsResponse {
61+
primitives = EntityPrimitives { long = 1L }
62+
},
63+
)
64+
65+
@Test fun testTypeFunctionFloatEquals() = successTest(
66+
GetFunctionTypeEqualsRequest { name = "test" },
67+
WaitersTestClient::waitUntilTypeFunctionFloatEquals,
68+
GetFunctionTypeEqualsResponse {
69+
primitives = EntityPrimitives { float = 1f }
70+
},
71+
)
72+
73+
@Test fun testTypeFunctionDoubleEquals() = successTest(
74+
GetFunctionTypeEqualsRequest { name = "test" },
75+
WaitersTestClient::waitUntilTypeFunctionDoubleEquals,
76+
GetFunctionTypeEqualsResponse {
77+
primitives = EntityPrimitives { double = 1.0 }
78+
},
79+
)
80+
81+
@Test fun testTypeFunctionObjectEquals() = successTest(
82+
GetFunctionTypeEqualsRequest { name = "test" },
83+
WaitersTestClient::waitUntilTypeFunctionObjectEquals,
84+
GetFunctionTypeEqualsResponse {
85+
primitives = EntityPrimitives { }
86+
},
87+
)
88+
89+
@Test fun testTypeFunctionMergedObjectEquals() = successTest(
90+
GetFunctionTypeEqualsRequest { name = "test" },
91+
WaitersTestClient::waitUntilTypeFunctionMergedObjectEquals,
92+
GetFunctionTypeEqualsResponse {
93+
primitives = EntityPrimitives { }
94+
},
95+
)
96+
97+
@Test fun testTypeFunctionNullEquals() = successTest(
98+
GetFunctionTypeEqualsRequest { name = "test" },
99+
WaitersTestClient::waitUntilTypeFunctionNullEquals,
100+
GetFunctionTypeEqualsResponse {
101+
primitives = EntityPrimitives { boolean = null }
102+
},
103+
)
104+
}

0 commit comments

Comments
 (0)