@@ -10,6 +10,8 @@ import software.amazon.smithy.kotlin.codegen.core.withBlock
10
10
import software.amazon.smithy.kotlin.codegen.core.withInlineBlock
11
11
import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes
12
12
import software.amazon.smithy.kotlin.codegen.model.getTrait
13
+ import software.amazon.smithy.kotlin.codegen.service.contraints.ConstraintGenerator
14
+ import software.amazon.smithy.kotlin.codegen.service.contraints.ConstraintUtilsGenerator
13
15
import software.amazon.smithy.model.shapes.OperationShape
14
16
import software.amazon.smithy.model.traits.AuthTrait
15
17
import software.amazon.smithy.model.traits.HttpBearerAuthTrait
@@ -93,7 +95,7 @@ internal class KtorStubGenerator(
93
95
}
94
96
95
97
private fun renderLogging () {
96
- delegator.useFileWriter(" Logging.kt" , " ${ctx.settings.pkg.name} .utils" ) { writer ->
98
+ delegator.useFileWriter(" Logging.kt" , " $pkgName .utils" ) { writer ->
97
99
98
100
writer.withBlock(" internal fun #T.configureLogging() {" , " }" , RuntimeTypes .KtorServerCore .Application ) {
99
101
withBlock(
@@ -167,20 +169,20 @@ internal class KtorStubGenerator(
167
169
168
170
// Generates `Authentication.kt` with Authenticator interface + configureSecurity().
169
171
override fun renderAuthModule () {
170
- delegator.useFileWriter(" UserPrincipal.kt" , " ${ctx.settings.pkg.name} .auth" ) { writer ->
172
+ delegator.useFileWriter(" UserPrincipal.kt" , " $pkgName .auth" ) { writer ->
171
173
writer.withBlock(" public data class UserPrincipal(" , " )" ) {
172
174
write(" val user: String" )
173
175
}
174
176
}
175
177
176
- delegator.useFileWriter(" Validation.kt" , " ${ctx.settings.pkg.name} .auth" ) { writer ->
178
+ delegator.useFileWriter(" Validation.kt" , " $pkgName .auth" ) { writer ->
177
179
writer.withBlock(" public fun bearerValidation(token: String): UserPrincipal? {" , " }" ) {
178
180
write(" // TODO: implement me" )
179
181
write(" if (true) return UserPrincipal(#S) else return null" , " Authenticated User" )
180
182
}
181
183
}
182
184
183
- delegator.useFileWriter(" Authentication.kt" , " ${ctx.settings.pkg.name} .auth" ) { writer ->
185
+ delegator.useFileWriter(" Authentication.kt" , " $pkgName .auth" ) { writer ->
184
186
writer.withBlock(" internal fun #T.configureAuthentication() {" , " }" , RuntimeTypes .KtorServerCore .Application ) {
185
187
write(" " )
186
188
withBlock(
@@ -201,20 +203,23 @@ internal class KtorStubGenerator(
201
203
202
204
// For every operation request structure, create a `validate()` function file.
203
205
override fun renderConstraintValidators () {
206
+ ConstraintUtilsGenerator (ctx, delegator).render()
207
+ operations.forEach { operation -> ConstraintGenerator (ctx, operation, delegator).render() }
204
208
}
205
209
206
210
// Writes `Routing.kt` that maps Smithy operations → Ktor routes.
207
211
override fun renderRouting () {
208
212
val contentType = ContentType .fromServiceShape(serviceShape)
209
213
210
- delegator.useFileWriter(" Routing.kt" , ctx.settings.pkg.name ) { writer ->
214
+ delegator.useFileWriter(" Routing.kt" , pkgName ) { writer ->
211
215
212
216
operations.forEach { shape ->
213
- writer.addImport(" ${ctx.settings.pkg.name} .serde" , " ${shape.id.name} OperationDeserializer" )
214
- writer.addImport(" ${ctx.settings.pkg.name} .serde" , " ${shape.id.name} OperationSerializer" )
215
- writer.addImport(" ${ctx.settings.pkg.name} .model" , " ${shape.id.name} Request" )
216
- writer.addImport(" ${ctx.settings.pkg.name} .model" , " ${shape.id.name} Response" )
217
- writer.addImport(" ${ctx.settings.pkg.name} .operations" , " handle${shape.id.name} Request" )
217
+ writer.addImport(" $pkgName .serde" , " ${shape.id.name} OperationDeserializer" )
218
+ writer.addImport(" $pkgName .serde" , " ${shape.id.name} OperationSerializer" )
219
+ writer.addImport(" $pkgName .constraints" , " check${shape.id.name} RequestConstraint" )
220
+ writer.addImport(" $pkgName .model" , " ${shape.id.name} Request" )
221
+ writer.addImport(" $pkgName .model" , " ${shape.id.name} Response" )
222
+ writer.addImport(" $pkgName .operations" , " handle${shape.id.name} Request" )
218
223
}
219
224
220
225
writer.withBlock(" internal fun #T.configureRouting(): Unit {" , " }" , RuntimeTypes .KtorServerCore .Application ) {
@@ -271,6 +276,11 @@ internal class KtorStubGenerator(
271
276
" Malformed CBOR input" ,
272
277
)
273
278
}
279
+ write(
280
+ " try { check${shape.id.name} RequestConstraint(requestObj) } catch (ex: Exception) { throw #T(ex?.message ?: #S, ex) }" ,
281
+ RuntimeTypes .KtorServerCore .BadRequestException ,
282
+ " Error while validating constraints" ,
283
+ )
274
284
write(" val responseObj = handle${shape.id.name} Request(requestObj)" )
275
285
write(" val serializer = ${shape.id.name} OperationSerializer()" )
276
286
withBlock(
@@ -356,7 +366,7 @@ internal class KtorStubGenerator(
356
366
}
357
367
358
368
private fun renderErrorHandler () {
359
- delegator.useFileWriter(" ErrorHandler.kt" , " ${ctx.settings.pkg.name} .plugins" ) { writer ->
369
+ delegator.useFileWriter(" ErrorHandler.kt" , " $pkgName .plugins" ) { writer ->
360
370
writer.write(" @#T" , RuntimeTypes .KotlinxCborSerde .Serializable )
361
371
.write(" private data class ErrorPayload(val code: Int, val message: String)" )
362
372
.write(" " )
@@ -386,7 +396,7 @@ internal class KtorStubGenerator(
386
396
write(" val acceptsCbor = request.#T().any { it.value == #S }" , RuntimeTypes .KtorServerRouting .requestAcceptItems, " application/cbor" )
387
397
write(" val acceptsJson = request.#T().any { it.value == #S }" , RuntimeTypes .KtorServerRouting .requestAcceptItems, " application/json" )
388
398
write(" " )
389
- write(" val log = #T.getLogger(#S)" , RuntimeTypes .KtorLoggingSlf4j .LoggerFactory , ctx.settings.pkg.name )
399
+ write(" val log = #T.getLogger(#S)" , RuntimeTypes .KtorLoggingSlf4j .LoggerFactory , pkgName )
390
400
write(" log.info(#S)" , " Route Error Message: \$ {envelope.msg}" )
391
401
write(" " )
392
402
withBlock(" when {" , " }" ) {
@@ -427,6 +437,16 @@ internal class KtorStubGenerator(
427
437
write(" call.respondEnvelope( ErrorEnvelope(status.value, message), status )" )
428
438
}
429
439
write(" " )
440
+ withBlock(" status(#T.NotFound) { call, status ->" , " }" , RuntimeTypes .KtorServerHttp .HttpStatusCode ) {
441
+ write(" val message = #S" , " Resource not found" )
442
+ write(" call.respondEnvelope( ErrorEnvelope(status.value, message), status )" )
443
+ }
444
+ write(" " )
445
+ withBlock(" status(#T.MethodNotAllowed) { call, status ->" , " }" , RuntimeTypes .KtorServerHttp .HttpStatusCode ) {
446
+ write(" val message = #S" , " Method not allowed for this resource" )
447
+ write(" call.respondEnvelope( ErrorEnvelope(status.value, message), status )" )
448
+ }
449
+ write(" " )
430
450
withBlock(" #T<Throwable> { call, cause ->" , " }" , RuntimeTypes .KtorServerStatusPage .exception) {
431
451
withBlock(" val status = when (cause) {" , " }" ) {
432
452
write(
@@ -456,7 +476,7 @@ internal class KtorStubGenerator(
456
476
}
457
477
458
478
private fun renderContentTypeGuard () {
459
- delegator.useFileWriter(" ContentTypeGuard.kt" , " ${ctx.settings.pkg.name} .plugins" ) { writer ->
479
+ delegator.useFileWriter(" ContentTypeGuard.kt" , " $pkgName .plugins" ) { writer ->
460
480
461
481
writer.withBlock(" private fun #T.hasBody(): Boolean {" , " }" , RuntimeTypes .KtorServerRouting .requestApplicationRequest) {
462
482
write(
@@ -569,9 +589,9 @@ internal class KtorStubGenerator(
569
589
override fun renderPerOperationHandlers () {
570
590
operations.forEach { shape ->
571
591
val name = shape.id.name
572
- delegator.useFileWriter(" ${name} Operation.kt" , " ${ctx.settings.pkg.name} .operations" ) { writer ->
573
- writer.addImport(" ${ctx.settings.pkg.name} .model" , " ${shape.id.name} Request" )
574
- writer.addImport(" ${ctx.settings.pkg.name} .model" , " ${shape.id.name} Response" )
592
+ delegator.useFileWriter(" ${name} Operation.kt" , " $pkgName .operations" ) { writer ->
593
+ writer.addImport(" $pkgName .model" , " ${shape.id.name} Request" )
594
+ writer.addImport(" $pkgName .model" , " ${shape.id.name} Response" )
575
595
576
596
writer.withBlock(" public fun handle${name} Request(req: ${name} Request): ${name} Response {" , " }" ) {
577
597
write(" // TODO: implement me" )
0 commit comments