Skip to content

Commit edd6371

Browse files
author
luigi
committed
Merge branch 'server-sdk-main' of github.com:smithy-lang/smithy-kotlin into merge-main-into-server-sdk
# Conflicts: # codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/CodegenVisitor.kt # codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/KotlinDependency.kt
2 parents b5d177c + 23606bd commit edd6371

File tree

16 files changed

+1269
-132
lines changed

16 files changed

+1269
-132
lines changed

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/RpcV2Cbor.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,16 @@ class RpcV2Cbor : AwsHttpBindingProtocolGenerator() {
103103
if (!op.hasHttpBody(ctx)) return
104104

105105
// payload member(s)
106-
val requestBindings = resolver.requestBindings(op)
107-
val httpPayload = requestBindings.firstOrNull { it.location == HttpBinding.Location.PAYLOAD }
106+
val bindings = if (ctx.settings.build.generateServiceProject) {
107+
resolver.responseBindings(op)
108+
} else {
109+
resolver.requestBindings(op)
110+
}
111+
val httpPayload = bindings.firstOrNull { it.location == HttpBinding.Location.PAYLOAD }
108112
if (httpPayload != null) {
109113
renderExplicitHttpPayloadSerializer(ctx, httpPayload, writer)
110114
} else {
111-
val documentMembers = requestBindings.filterDocumentBoundMembers()
115+
val documentMembers = bindings.filterDocumentBoundMembers()
112116
// Unbound document members that should be serialized into the document format for the protocol.
113117
// delegate to the generate operation body serializer function
114118
val sdg = structuredDataSerializer(ctx)

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/StaticHttpBindingResolver.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,16 @@ open class StaticHttpBindingResolver(
5656
/**
5757
* By default returns all inputs as [HttpBinding.Location.DOCUMENT]
5858
*/
59-
override fun requestBindings(operationShape: OperationShape): List<HttpBindingDescriptor> {
60-
if (!operationShape.input.isPresent) return emptyList()
61-
val input = model.expectShape(operationShape.input.get())
62-
return input.members().map { member -> HttpBindingDescriptor(member, HttpBinding.Location.DOCUMENT) }.toList()
59+
override fun requestBindings(shape: Shape): List<HttpBindingDescriptor> {
60+
when (shape) {
61+
is OperationShape -> {
62+
if (!shape.input.isPresent) return emptyList()
63+
val input = model.expectShape(shape.input.get())
64+
return input.members().map { member -> HttpBindingDescriptor(member, HttpBinding.Location.DOCUMENT) }.toList()
65+
}
66+
is StructureShape -> return shape.members().map { member -> member.toHttpBindingDescriptor() }.toList()
67+
else -> error("unimplemented shape type for http response bindings: $shape")
68+
}
6369
}
6470

6571
/**

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/CodegenVisitor.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import software.amazon.smithy.kotlin.codegen.model.hasTrait
2020
import software.amazon.smithy.kotlin.codegen.rendering.*
2121
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ApplicationProtocol
2222
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
23+
import software.amazon.smithy.kotlin.codegen.service.AbstractStubGenerator
2324
import software.amazon.smithy.model.Model
2425
import software.amazon.smithy.model.knowledge.ServiceIndex
2526
import software.amazon.smithy.model.neighbor.Walker
@@ -114,7 +115,12 @@ class CodegenVisitor(context: PluginContext) : ShapeVisitor.Default<Unit>() {
114115
}
115116

116117
fun execute() {
117-
logger.info("Generating Kotlin client for service ${settings.service}")
118+
val generateServiceProject = settings.build.generateServiceProject
119+
if (generateServiceProject) {
120+
logger.info("Generating Kotlin service ${settings.service}")
121+
} else {
122+
logger.info("Generating Kotlin client for service ${settings.service}")
123+
}
118124

119125
logger.info("Walking shapes from ${settings.service} to find shapes to generate")
120126
val modelWithoutTraits = ModelTransformer.create().getModelWithoutTraitShapes(model)
@@ -138,11 +144,18 @@ class CodegenVisitor(context: PluginContext) : ShapeVisitor.Default<Unit>() {
138144
logger.info("[${service.id}] Generating service client for protocol $protocol")
139145
generateProtocolClient(ctx)
140146

141-
logger.info("[${service.id}] Generating endpoint provider for protocol $protocol")
142-
generateEndpointsSources(ctx)
147+
if (!generateServiceProject) {
148+
logger.info("[${service.id}] Generating endpoint provider for protocol $protocol")
149+
generateEndpointsSources(ctx)
150+
151+
logger.info("[${service.id}] Generating auth scheme provider for protocol $protocol")
152+
generateAuthSchemeProvider(ctx)
153+
}
154+
}
143155

144-
logger.info("[${service.id}] Generating auth scheme provider for protocol $protocol")
145-
generateAuthSchemeProvider(ctx)
156+
if (generateServiceProject) {
157+
val serviceStubGenerator: AbstractStubGenerator = settings.serviceStub.framework.getServiceFrameworkGenerator(baseGenerationContext, writers, fileManifest)
158+
serviceStubGenerator.render()
146159
}
147160

148161
writers.finalize()

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/KotlinSettings.kt

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package software.amazon.smithy.kotlin.codegen
88
import software.amazon.smithy.aws.traits.protocols.*
99
import software.amazon.smithy.codegen.core.CodegenException
1010
import software.amazon.smithy.kotlin.codegen.lang.isValidPackageName
11+
import software.amazon.smithy.kotlin.codegen.service.ServiceFramework
1112
import software.amazon.smithy.kotlin.codegen.utils.getOrNull
1213
import software.amazon.smithy.kotlin.codegen.utils.toCamelCase
1314
import software.amazon.smithy.model.Model
@@ -32,6 +33,7 @@ private const val PACKAGE_VERSION = "version"
3233
private const val PACKAGE_DESCRIPTION = "description"
3334
private const val BUILD_SETTINGS = "build"
3435
private const val API_SETTINGS = "api"
36+
private const val SERVICE_STUB_SETTINGS = "serviceStub"
3537

3638
// Optional specification of sdkId for models that provide them, otherwise Service's shape id name is used
3739
private const val SDK_ID = "sdkId"
@@ -56,6 +58,7 @@ data class KotlinSettings(
5658
val sdkId: String,
5759
val build: BuildSettings = BuildSettings.Default,
5860
val api: ApiSettings = ApiSettings.Default,
61+
val serviceStub: ServiceStubSettings = ServiceStubSettings.Default,
5962
val debug: Boolean = false,
6063
) {
6164

@@ -98,7 +101,7 @@ data class KotlinSettings(
98101
* @return Returns the extracted settings
99102
*/
100103
fun from(model: Model, config: ObjectNode): KotlinSettings {
101-
config.warnIfAdditionalProperties(listOf(SERVICE, PACKAGE_SETTINGS, BUILD_SETTINGS, SDK_ID, API_SETTINGS))
104+
config.warnIfAdditionalProperties(listOf(SERVICE, PACKAGE_SETTINGS, BUILD_SETTINGS, SDK_ID, API_SETTINGS, SERVICE_STUB_SETTINGS))
102105

103106
val serviceId = config.getStringMember(SERVICE)
104107
.map(StringNode::expectShapeId)
@@ -118,13 +121,15 @@ data class KotlinSettings(
118121
val sdkId = config.getStringMemberOrDefault(SDK_ID, serviceId.name)
119122
val build = config.getObjectMember(BUILD_SETTINGS)
120123
val api = config.getObjectMember(API_SETTINGS)
124+
val serviceStub = config.getObjectMember(SERVICE_STUB_SETTINGS)
121125
val debug = config.getBooleanMemberOrDefault("debug", false)
122126
return KotlinSettings(
123127
serviceId,
124128
PackageSettings(packageName, version, desc),
125129
sdkId,
126130
BuildSettings.fromNode(build),
127131
ApiSettings.fromNode(api),
132+
ServiceStubSettings.fromNode(serviceStub),
128133
debug,
129134
)
130135
}
@@ -185,31 +190,35 @@ fun Model.inferService(): ShapeId {
185190
* @param optInAnnotations Kotlin opt-in annotations. See:
186191
* https://kotlinlang.org/docs/reference/opt-in-requirements.html
187192
* @param generateMultiplatformProject Flag indicating to generate a Kotlin multiplatform or JVM project
193+
* @param generateServiceProject Flag indicating to generate a Kotlin service project
188194
*/
189195
data class BuildSettings(
190196
val generateFullProject: Boolean = false,
191197
val generateDefaultBuildFiles: Boolean = true,
192198
val optInAnnotations: List<String>? = null,
193199
val generateMultiplatformProject: Boolean = false,
200+
val generateServiceProject: Boolean = false,
194201
) {
195202
companion object {
196203
const val ROOT_PROJECT = "rootProject"
197204
const val GENERATE_DEFAULT_BUILD_FILES = "generateDefaultBuildFiles"
198205
const val ANNOTATIONS = "optInAnnotations"
199206
const val GENERATE_MULTIPLATFORM_MODULE = "multiplatform"
207+
const val GENERATE_SERVICE_PROJECT = "generateServiceProject"
200208

201209
fun fromNode(node: Optional<ObjectNode>): BuildSettings = node.map {
202210
val generateFullProject = node.get().getBooleanMemberOrDefault(ROOT_PROJECT, false)
203211
val generateBuildFiles = node.get().getBooleanMemberOrDefault(GENERATE_DEFAULT_BUILD_FILES, true)
204212
val generateMultiplatformProject = node.get().getBooleanMemberOrDefault(GENERATE_MULTIPLATFORM_MODULE, false)
213+
val generateServiceProject = node.get().getBooleanMemberOrDefault(GENERATE_SERVICE_PROJECT, false)
205214
val annotations = node.get().getArrayMember(ANNOTATIONS).map {
206215
it.elements.mapNotNull { node ->
207216
node.asStringNode().map { stringNode ->
208217
stringNode.value
209218
}.orNull()
210219
}
211220
}.orNull()
212-
BuildSettings(generateFullProject, generateBuildFiles, annotations, generateMultiplatformProject)
221+
BuildSettings(generateFullProject, generateBuildFiles, annotations, generateMultiplatformProject, generateServiceProject)
213222
}.orElse(Default)
214223

215224
/**
@@ -335,3 +344,29 @@ data class ApiSettings(
335344
val Default: ApiSettings = ApiSettings()
336345
}
337346
}
347+
348+
/**
349+
* Contains configurations settings for a Kotlin service project
350+
* @param framework Enum representing the server framework of the service.
351+
*/
352+
data class ServiceStubSettings(
353+
val framework: ServiceFramework = ServiceFramework.KTOR,
354+
) {
355+
companion object {
356+
const val SERVER_FRAMEWORK = "serverFramework"
357+
358+
fun fromNode(node: Optional<ObjectNode>): ServiceStubSettings = node.map {
359+
val serverFramework = node.get()
360+
.getStringMember(SERVER_FRAMEWORK)
361+
.map { ServiceFramework.fromValue(it.value) }
362+
.getOrNull() ?: ServiceFramework.KTOR
363+
364+
ServiceStubSettings(serverFramework)
365+
}.orElse(Default)
366+
367+
/**
368+
* Default service stub settings
369+
*/
370+
val Default: ServiceStubSettings = ServiceStubSettings()
371+
}
372+
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ private fun getDefaultRuntimeVersion(): String {
3838
const val RUNTIME_GROUP: String = "aws.smithy.kotlin"
3939
val RUNTIME_VERSION: String = System.getProperty("smithy.kotlin.codegen.clientRuntimeVersion", getDefaultRuntimeVersion())
4040
val KOTLIN_COMPILER_VERSION: String = System.getProperty("smithy.kotlin.codegen.kotlinCompilerVersion", "2.2.0")
41+
val KTOR_VERSION: String = System.getProperty("smithy.kotlin.codegen.ktorVersion", "3.1.3")
42+
val SERIALIZATION_PLUGIN: String = System.getProperty("smithy.kotlin.codegen.SerializationPlugin", "2.0.20")
43+
val KOTLINX_VERSION: String = System.getProperty("smithy.kotlin.codegen.ktorKotlinxVersion", "1.9.0")
44+
val KTOR_LOGGING_BACKEND_VERSION: String = System.getProperty("smithy.kotlin.codegen.ktorLoggingBackendVersion", "1.4.14")
4145

4246
enum class SourceSet {
4347
CommonMain,
@@ -135,6 +139,19 @@ data class KotlinDependency(
135139
val KOTLIN_STDLIB = KotlinDependency(GradleConfiguration.Implementation, "kotlin", "org.jetbrains.kotlin", "kotlin-stdlib", KOTLIN_COMPILER_VERSION)
136140
val KOTLIN_TEST = KotlinDependency(GradleConfiguration.TestImplementation, "kotlin.test", "org.jetbrains.kotlin", "kotlin-test", KOTLIN_COMPILER_VERSION)
137141
val KOTLIN_TEST_IMPL = KOTLIN_TEST.copy(config = GradleConfiguration.Implementation)
142+
143+
// Ktor server dependencies
144+
// FIXME: version numbers should not be hardcoded, they should be setting dynamically based on the Gradle library versions
145+
val KTOR_SERVER_CORE = KotlinDependency(GradleConfiguration.Implementation, "io.ktor.server", "io.ktor", "ktor-server-core", KTOR_VERSION)
146+
val KTOR_SERVER_NETTY = KotlinDependency(GradleConfiguration.Implementation, "io.ktor.server.netty", "io.ktor", "ktor-server-netty", KTOR_VERSION)
147+
val KTOR_SERVER_HTTP = KotlinDependency(GradleConfiguration.Implementation, "io.ktor.http", "io.ktor", "ktor-http-jvm", KTOR_VERSION)
148+
val KTOR_SERVER_LOGGING = KotlinDependency(GradleConfiguration.Implementation, "io.ktor.server.plugins.calllogging", "io.ktor", "ktor-server-call-logging", KTOR_VERSION)
149+
val KTOR_LOGGING_SLF4J = KotlinDependency(GradleConfiguration.Implementation, "org.slf4j", "ch.qos.logback", "logback-classic", KTOR_LOGGING_BACKEND_VERSION)
150+
val KTOR_LOGGING_LOGBACK = KotlinDependency(GradleConfiguration.Implementation, "ch.qos.logback", "ch.qos.logback", "logback-classic", KTOR_LOGGING_BACKEND_VERSION)
151+
val KTOR_SERVER_STATUS_PAGE = KotlinDependency(GradleConfiguration.Implementation, "io.ktor.server.plugins.statuspages", "io.ktor", "ktor-server-status-pages-jvm", KTOR_VERSION)
152+
val KOTLINX_CBOR_SERDE = KotlinDependency(GradleConfiguration.Implementation, "kotlinx.serialization", "org.jetbrains.kotlinx", "kotlinx-serialization-cbor", KOTLINX_VERSION)
153+
val KOTLINX_JSON_SERDE = KotlinDependency(GradleConfiguration.Implementation, "kotlinx.serialization.json", "org.jetbrains.kotlinx", "kotlinx-serialization-json", KOTLINX_VERSION)
154+
val KTOR_SERVER_AUTH = KotlinDependency(GradleConfiguration.Implementation, "io.ktor.server.auth", "io.ktor", "ktor-server-auth", KTOR_VERSION)
138155
}
139156

140157
override fun getDependencies(): List<SymbolDependency> {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package software.amazon.smithy.kotlin.codegen.core
66

77
import software.amazon.smithy.codegen.core.*
88
import software.amazon.smithy.kotlin.codegen.KotlinSettings
9+
import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes
910
import software.amazon.smithy.kotlin.codegen.lang.kotlinReservedWords
1011
import software.amazon.smithy.kotlin.codegen.model.*
1112
import software.amazon.smithy.kotlin.codegen.utils.dq
@@ -332,6 +333,10 @@ class KotlinSymbolProvider(private val model: Model, private val settings: Kotli
332333
}
333334

334335
override fun serviceShape(shape: ServiceShape): Symbol {
336+
if (settings.build.generateServiceProject) {
337+
// Intentionally not generating a *client symbol* for the service
338+
return KotlinTypes.Nothing
339+
}
335340
val serviceName = clientName(settings.sdkId)
336341
return createSymbolBuilder(shape, "${serviceName}Client")
337342
.namespace(rootNamespace, ".")

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

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,4 +492,94 @@ object RuntimeTypes {
492492

493493
val sign = symbol("sign")
494494
}
495+
496+
object KtorServerCore : RuntimeTypePackage(KotlinDependency.KTOR_SERVER_CORE) {
497+
val embeddedServer = symbol("embeddedServer", "engine")
498+
val EmbeddedServerType = symbol("EmbeddedServer", "engine")
499+
val ApplicationEngineFactory = symbol("ApplicationEngineFactory", "engine")
500+
501+
val Application = symbol("Application", "application")
502+
val ApplicationCallClass = symbol("ApplicationCall", "application")
503+
val ApplicationStopping = symbol("ApplicationStopping", "application")
504+
val ApplicationStopped = symbol("ApplicationStopped", "application")
505+
val ApplicationCreateRouteScopedPlugin = symbol("createRouteScopedPlugin", "application")
506+
val ApplicationRouteScopedPlugin = symbol("RouteScopedPlugin", "application")
507+
val applicationCall = symbol("call", "application")
508+
val install = symbol("install", "application")
509+
510+
val BadRequestException = symbol("BadRequestException", "plugins")
511+
}
512+
513+
object KtorServerRouting : RuntimeTypePackage(KotlinDependency.KTOR_SERVER_CORE) {
514+
val routing = symbol("routing", "routing")
515+
val route = symbol("route", "routing")
516+
val get = symbol("get", "routing")
517+
val post = symbol("post", "routing")
518+
val put = symbol("put", "routing")
519+
val delete = symbol("delete", "routing")
520+
val patch = symbol("patch", "routing")
521+
val head = symbol("head", "routing")
522+
val options = symbol("options", "routing")
523+
524+
val requestReceive = symbol("receive", "request")
525+
val requestUri = symbol("uri", "request")
526+
val requestHttpMethod = symbol("httpMethod", "request")
527+
val requestApplicationRequest = symbol("ApplicationRequest", "request")
528+
val requestContentLength = symbol("contentLength", "request")
529+
val requestContentType = symbol("contentType", "request")
530+
val requestacceptItems = symbol("acceptItems", "request")
531+
532+
val responseText = symbol("respondText", "response")
533+
val responseRespond = symbol("respond", "response")
534+
val responseRespondBytes = symbol("respondBytes", "response")
535+
}
536+
537+
object KtorServerNetty : RuntimeTypePackage(KotlinDependency.KTOR_SERVER_NETTY) {
538+
val Netty = symbol("Netty")
539+
}
540+
541+
object KtorServerHttp : RuntimeTypePackage(KotlinDependency.KTOR_SERVER_HTTP) {
542+
val ContentType = symbol("ContentType")
543+
val HttpStatusCode = symbol("HttpStatusCode")
544+
val HttpHeaders = symbol("HttpHeaders")
545+
val Cbor = symbol("Cbor", "ContentType.Application")
546+
val Json = symbol("Json", "ContentType.Application")
547+
}
548+
549+
object KtorServerLogging : RuntimeTypePackage(KotlinDependency.KTOR_SERVER_LOGGING) {
550+
val CallLogging = symbol("CallLogging")
551+
}
552+
553+
object KtorLoggingSlf4j : RuntimeTypePackage(KotlinDependency.KTOR_LOGGING_SLF4J) {
554+
val Level = symbol("Level", "event")
555+
val LoggerFactory = symbol("LoggerFactory")
556+
val ROOT_LOGGER_NAME = symbol("ROOT_LOGGER_NAME", "Logger")
557+
}
558+
559+
object KtorLoggingLogback : RuntimeTypePackage(KotlinDependency.KTOR_LOGGING_LOGBACK) {
560+
val Level = symbol("Level", "classic")
561+
val LoggerContext = symbol("LoggerContext", "classic")
562+
}
563+
564+
object KtorServerStatusPage : RuntimeTypePackage(KotlinDependency.KTOR_SERVER_STATUS_PAGE) {
565+
val StatusPages = symbol("StatusPages")
566+
val exception = symbol("exception")
567+
}
568+
569+
object KtorServerAuth : RuntimeTypePackage(KotlinDependency.KTOR_SERVER_AUTH) {
570+
val Authentication = symbol("Authentication")
571+
val authenticate = symbol("authenticate")
572+
val Principal = symbol("Principal")
573+
val bearer = symbol("bearer")
574+
}
575+
576+
object KotlinxCborSerde : RuntimeTypePackage(KotlinDependency.KOTLINX_CBOR_SERDE) {
577+
val Serializable = symbol("Serializable")
578+
val Cbor = symbol("Cbor", "cbor")
579+
val encodeToByteArray = symbol("encodeToByteArray")
580+
}
581+
582+
object KotlinxJsonSerde : RuntimeTypePackage(KotlinDependency.KOTLINX_JSON_SERDE) {
583+
val Json = symbol("Json")
584+
}
495585
}

0 commit comments

Comments
 (0)