Skip to content

Commit 0db31ec

Browse files
author
luigi
committed
unit test
1 parent 514e764 commit 0db31ec

File tree

15 files changed

+1087
-126
lines changed

15 files changed

+1087
-126
lines changed

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/service/contraints/ConstraintGenerator.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@ internal class ConstraintGenerator(
6161

6262
private fun renderRequestConstraintsValidation() {
6363
delegator.useFileWriter("${opName}RequestConstraints.kt", "$pkgName.constraints") { writer ->
64-
writer.addImport("$pkgName.model", "${operation.id.name}Request")
64+
val inputShape = ctx.model.expectShape(operation.input.get())
65+
val inputSymbol = ctx.symbolProvider.toSymbol(inputShape)
6566

66-
writer.withBlock("public fun check${opName}RequestConstraint(data: ${opName}Request) {", "}") {
67+
writer.withBlock("public fun check${opName}RequestConstraint(data: #T) {", "}", inputSymbol) {
6768
for (memberShape in inputMembers.values) {
6869
generateConstraintValidations("data.", memberShape, writer)
6970
}

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/service/ktor/AuthenticationAWS.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,15 @@ internal fun KtorStubGenerator.writeAWSAuthentication() {
270270
write("")
271271
withBlock("fun matchesRegion(pattern: String, value: String): Boolean {", "}") {
272272
write("if (pattern == #S) return true", "*")
273-
write("val rx = Regex(#S + Regex.escape(pattern).replace(#S, #S) + #S, RegexOption.IGNORE_CASE)\n", "^", "\\*", ".*", "$")
274-
write("return rx.matches(value)")
273+
write("val normalized = pattern.trim().replace(Regex(#S), #S)", "\\*+", "*")
274+
write("val sb = StringBuilder(#S)", "^")
275+
write("val parts = normalized.split(#S)", "*")
276+
withBlock("parts.forEachIndexed { i, part ->", "}") {
277+
write("sb.append(Regex.escape(part))")
278+
write("if (i < parts.lastIndex) sb.append(#S)", "[^-]+")
279+
}
280+
write("sb.append(#S)", "$")
281+
write("return Regex(sb.toString(), RegexOption.IGNORE_CASE).matches(value.trim())")
275282
}
276283
write("if (regionSet.none { matchesRegion(it, region.lowercase()) }) return null")
277284
write("")

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/service/ktor/OperationHandlers.kt

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,23 @@ import software.amazon.smithy.kotlin.codegen.service.KtorStubGenerator
55

66
internal fun KtorStubGenerator.writePerOperationHandlers() {
77
operations.forEach { shape ->
8+
val inputShape = ctx.model.expectShape(shape.input.get())
9+
val inputSymbol = ctx.symbolProvider.toSymbol(inputShape)
10+
11+
val outputShape = ctx.model.expectShape(shape.output.get())
12+
val outputSymbol = ctx.symbolProvider.toSymbol(outputShape)
13+
814
val name = shape.id.name
15+
916
delegator.useFileWriter("${name}Operation.kt", "$pkgName.operations") { writer ->
10-
writer.addImport("$pkgName.model", "${shape.id.name}Request")
11-
writer.addImport("$pkgName.model", "${shape.id.name}Response")
1217

13-
writer.withBlock("public fun handle${name}Request(req: ${name}Request): ${name}Response {", "}") {
18+
writer.withBlock("public fun handle${name}Request(req: #T): #T {", "}", inputSymbol, outputSymbol) {
1419
write("// TODO: implement me")
15-
write("// To build a ${name}Response object:")
16-
write("// 1. Use`${name}Response.Builder()`")
17-
write("// 2. Set fields like `${name}Response.variable = ...`")
18-
write("// 3. Return the built object using `return ${name}Response.build()`")
19-
write("return ${name}Response.Builder().build()")
20+
write("// To build a #T object:", outputSymbol)
21+
write("// 1. Use`#T.Builder()`", outputSymbol)
22+
write("// 2. Set fields like `#T.variable = ...`", outputSymbol)
23+
write("// 3. Return the built object using `return #T.build()`", outputSymbol)
24+
write("return #T.Builder().build()", outputSymbol)
2025
}
2126
}
2227
}

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/service/ktor/Plugins.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private fun KtorStubGenerator.renderErrorHandler() {
8484
write("if (call.attributes.getOrNull(#T) == true) { return@status }", ServiceTypes(pkgName).responseHandledKey)
8585
write("call.attributes.put(#T, true)", ServiceTypes(pkgName).responseHandledKey)
8686
write("val missing = call.request.headers[#S].isNullOrBlank()", "Authorization")
87-
write("val message = if (missing) #S else #S", "Missing bearer token", "Invalid or expired bearer token")
87+
write("val message = if (missing) #S else #S", "Missing bearer token", "Invalid or expired authentication")
8888
write("call.respondEnvelope( ErrorEnvelope(status.value, message), status )")
8989
}
9090
write("")

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/service/ktor/Routing.kt

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ package software.amazon.smithy.kotlin.codegen.service.ktor
22

33
import software.amazon.smithy.aws.traits.auth.SigV4ATrait
44
import software.amazon.smithy.aws.traits.auth.SigV4Trait
5+
import software.amazon.smithy.codegen.core.SymbolReference
56
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
67
import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes
78
import software.amazon.smithy.kotlin.codegen.core.withBlock
89
import software.amazon.smithy.kotlin.codegen.core.withInlineBlock
10+
import software.amazon.smithy.kotlin.codegen.model.buildSymbol
911
import software.amazon.smithy.kotlin.codegen.model.getTrait
12+
import software.amazon.smithy.kotlin.codegen.rendering.serde.deserializerName
13+
import software.amazon.smithy.kotlin.codegen.rendering.serde.serializerName
1014
import software.amazon.smithy.kotlin.codegen.service.KtorStubGenerator
1115
import software.amazon.smithy.kotlin.codegen.service.MediaType
1216
import software.amazon.smithy.kotlin.codegen.service.MediaType.ANY
@@ -32,17 +36,9 @@ import software.amazon.smithy.model.traits.TimestampFormatTrait
3236

3337
internal fun KtorStubGenerator.writeRouting() {
3438
delegator.useFileWriter("Routing.kt", pkgName) { writer ->
35-
3639
operations.forEach { shape ->
37-
writer.addImport("$pkgName.serde", "${shape.id.name}OperationDeserializer")
38-
writer.addImport("$pkgName.serde", "${shape.id.name}OperationSerializer")
3940
writer.addImport("$pkgName.constraints", "check${shape.id.name}RequestConstraint")
40-
writer.addImport("$pkgName.model", "${shape.id.name}Request")
41-
writer.addImport("$pkgName.model", "${shape.id.name}Response")
4241
writer.addImport("$pkgName.operations", "handle${shape.id.name}Request")
43-
shape.errors.forEach { error ->
44-
writer.addImport("$pkgName.serde", "${error.name}Serializer")
45-
}
4642
}
4743

4844
writer.withBlock("internal fun #T.configureRouting(): Unit {", "}", RuntimeTypes.KtorServerCore.Application) {
@@ -52,6 +48,25 @@ internal fun KtorStubGenerator.writeRouting() {
5248
}
5349
operations.filter { it.hasTrait(HttpTrait.ID) }
5450
.forEach { shape ->
51+
val inputShape = ctx.model.expectShape(shape.input.get())
52+
val inputSymbol = ctx.symbolProvider.toSymbol(inputShape)
53+
54+
val outputShape = ctx.model.expectShape(shape.output.get())
55+
val outputSymbol = ctx.symbolProvider.toSymbol(outputShape)
56+
57+
val serializerSymbol = buildSymbol {
58+
definitionFile = "${shape.serializerName()}.kt"
59+
name = shape.serializerName()
60+
namespace = ctx.settings.pkg.serde
61+
reference(inputSymbol, SymbolReference.ContextOption.DECLARE)
62+
}
63+
val deserializerSymbol = buildSymbol {
64+
definitionFile = "${shape.deserializerName()}.kt"
65+
name = shape.deserializerName()
66+
namespace = ctx.settings.pkg.serde
67+
reference(outputSymbol, SymbolReference.ContextOption.DECLARE)
68+
}
69+
5570
val httpTrait = shape.getTrait<HttpTrait>()!!
5671

5772
val uri = httpTrait.uri
@@ -99,7 +114,7 @@ internal fun KtorStubGenerator.writeRouting() {
99114
RuntimeTypes.KtorServerCore.applicationCall,
100115
RuntimeTypes.KtorServerRouting.requestReceive,
101116
)
102-
write("val deserializer = ${shape.id.name}OperationDeserializer()")
117+
write("val deserializer = #T()", deserializerSymbol)
103118
withBlock(
104119
"var requestObj = try { deserializer.deserialize(#T(), call, request) } catch (ex: Exception) {",
105120
"}",
@@ -124,7 +139,7 @@ internal fun KtorStubGenerator.writeRouting() {
124139
"Error while validating constraints",
125140
)
126141
write("val responseObj = handle${shape.id.name}Request(requestObj)")
127-
write("val serializer = ${shape.id.name}OperationSerializer()")
142+
write("val serializer = #T()", serializerSymbol)
128143
withBlock(
129144
"val response = try { serializer.serialize(#T(), responseObj) } catch (ex: Exception) {",
130145
"}",
@@ -151,13 +166,19 @@ internal fun KtorStubGenerator.writeRouting() {
151166
write("else -> null")
152167
}
153168
write("")
154-
write("")
155169
writeInline("val errorResponse: Pair<Any, Int>? = ")
156170
withBlock("when (errorObj) {", "}") {
157171
shape.errors.forEach { errorShapeId ->
158172
val errorShape = ctx.model.expectShape(errorShapeId)
159173
val errorSymbol = ctx.symbolProvider.toSymbol(errorShape)
160-
write("is #T -> Pair(${errorShapeId.name}Serializer().serialize(#T(), errorObj), ${errorShape.getTrait<HttpErrorTrait>()?.code})", errorSymbol, RuntimeTypes.Core.ExecutionContext)
174+
val exceptionSymbol = buildSymbol {
175+
val exceptionName = "${errorSymbol.name}Serializer"
176+
definitionFile = "$errorSymbol.kt"
177+
name = exceptionName
178+
namespace = ctx.settings.pkg.serde
179+
reference(errorSymbol, SymbolReference.ContextOption.DECLARE)
180+
}
181+
write("is #T -> Pair(#T().serialize(#T(), errorObj), ${errorShape.getTrait<HttpErrorTrait>()?.code})", errorSymbol, exceptionSymbol, RuntimeTypes.Core.ExecutionContext)
161182
}
162183
write("else -> null")
163184
}
@@ -255,7 +276,6 @@ private fun KtorStubGenerator.readHttpQuery(shape: OperationShape, writer: Kotli
255276
val httpQueryParamsMemberName = httpQueryParamsMember.memberName
256277
val httpQueryParamsMapShape = ctx.model.expectShape(httpQueryParamsMember.target) as MapShape
257278
val httpQueryParamsMapValueTypeShape = ctx.model.expectShape(httpQueryParamsMapShape.value.target)
258-
println(httpQueryParamsMapShape)
259279
val httpQueryKeysLiteral = httpQueryKeys.joinToString(", ") { "\"$it\"" }
260280
writer.withInlineBlock("$httpQueryParamsMemberName = call.request.queryParameters.entries().filter { (key, _) ->", "}") {
261281
write("key !in setOf($httpQueryKeysLiteral)")

tests/codegen/service-codegen-tests/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ tasks.test {
3737
kotlin {
3838
compilerOptions {
3939
freeCompilerArgs.addAll(
40+
"-opt-in=aws.smithy.kotlin.runtime.InternalApi",
4041
"-opt-in=kotlin.io.path.ExperimentalPathApi",
4142
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
4243
)
@@ -61,6 +62,9 @@ dependencies {
6162
testImplementation(project(":codegen:smithy-kotlin-codegen-testutils"))
6263
testImplementation(project(":codegen:smithy-kotlin-codegen"))
6364
testImplementation(project(":codegen:smithy-aws-kotlin-codegen"))
65+
testApi(project(":runtime:auth:aws-signing-common"))
66+
testApi(project(":runtime:auth:http-auth-aws"))
67+
testApi(project(":runtime:auth:aws-signing-default"))
6468

6569
testImplementation(gradleTestKit())
6670

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
$version: "2.0"
2+
3+
namespace com.authentication
4+
5+
use smithy.api#httpBearerAuth
6+
use aws.auth#sigv4
7+
use aws.auth#sigv4a
8+
use aws.protocols#restJson1
9+
10+
@restJson1
11+
@auth([sigv4a, sigv4, httpBearerAuth])
12+
@httpBearerAuth
13+
@sigv4(name: "service-1")
14+
@sigv4a(name: "service-1")
15+
service AuthenticationServiceTest {
16+
version: "1.0.0"
17+
operations: [
18+
OnlyBearerTest
19+
OnlySigV4Test
20+
SigV4ATest
21+
AllAuthenticationTest
22+
NoAuthenticationTest
23+
SigV4AuthenticationWithBodyTest
24+
SigV4AAuthenticationWithBodyTest
25+
]
26+
}
27+
28+
@auth([httpBearerAuth])
29+
@http(method: "POST", uri: "/only-bearer", code: 201)
30+
operation OnlyBearerTest {
31+
input: OnlyBearerTestInput
32+
output: OnlyBearerTestOutput
33+
}
34+
35+
@input
36+
structure OnlyBearerTestInput {}
37+
38+
@output
39+
structure OnlyBearerTestOutput {}
40+
41+
@auth([sigv4])
42+
@http(method: "POST", uri: "/only-sigv4", code: 201)
43+
operation OnlySigV4Test {
44+
input: OnlySigV4TestInput
45+
output: OnlySigV4TestOutput
46+
}
47+
48+
@input
49+
structure OnlySigV4TestInput {}
50+
51+
@output
52+
structure OnlySigV4TestOutput {}
53+
54+
@auth([sigv4a, sigv4])
55+
@http(method: "POST", uri: "/sigv4a", code: 201)
56+
operation SigV4ATest {
57+
input: SigV4ATestInput
58+
output: SigV4ATestOutput
59+
}
60+
61+
@input
62+
structure SigV4ATestInput {}
63+
64+
@output
65+
structure SigV4ATestOutput {}
66+
67+
@http(method: "POST", uri: "/all-authentication", code: 201)
68+
operation AllAuthenticationTest {
69+
input: AllAuthenticationTestInput
70+
output: AllAuthenticationTestOutput
71+
}
72+
73+
@input
74+
structure AllAuthenticationTestInput {}
75+
76+
@output
77+
structure AllAuthenticationTestOutput {}
78+
79+
@auth([])
80+
@http(method: "POST", uri: "/no-authentication", code: 201)
81+
operation NoAuthenticationTest {
82+
input: NoAuthenticationTestInput
83+
output: NoAuthenticationTestOutput
84+
}
85+
86+
@input
87+
structure NoAuthenticationTestInput {}
88+
89+
@output
90+
structure NoAuthenticationTestOutput {}
91+
92+
@auth([sigv4])
93+
@http(method: "POST", uri: "/sigv4-authentication-body", code: 201)
94+
operation SigV4AuthenticationWithBodyTest {
95+
input: SigV4AuthenticationWithBodyTestInput
96+
output: SigV4AuthenticationWithBodyTestOutput
97+
}
98+
99+
@input
100+
structure SigV4AuthenticationWithBodyTestInput {
101+
input1: String
102+
}
103+
104+
@output
105+
structure SigV4AuthenticationWithBodyTestOutput {}
106+
107+
108+
@auth([sigv4a, sigv4])
109+
@http(method: "POST", uri: "/sigv4a-authentication-body", code: 201)
110+
operation SigV4AAuthenticationWithBodyTest {
111+
input: SigV4AAuthenticationWithBodyTestInput
112+
output: SigV4AAuthenticationWithBodyTestOutput
113+
}
114+
115+
@input
116+
structure SigV4AAuthenticationWithBodyTestInput {
117+
input1: String
118+
}
119+
120+
@output
121+
structure SigV4AAuthenticationWithBodyTestOutput {}
122+

0 commit comments

Comments
 (0)