Skip to content

Commit ed5dec2

Browse files
committed
updated kotlin-server generator and template
1 parent fa0b752 commit ed5dec2

File tree

11 files changed

+223
-173
lines changed

11 files changed

+223
-173
lines changed

modules/swagger-codegen/src/main/resources/kotlin-server/data_class.mustache

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,33 @@
55
{{/vars}}
66
*/
77
data class {{classname}} (
8-
{{#requiredVars}}
9-
{{>data_class_req_var}}{{^-last}},
10-
{{/-last}}{{/requiredVars}}{{#hasRequired}}{{#hasOptional}},
11-
{{/hasOptional}}{{/hasRequired}}{{#optionalVars}}{{>data_class_opt_var}}{{^-last}},
12-
{{/-last}}{{/optionalVars}}
8+
{{#vars}}
9+
{{#required}}
10+
{{>data_class_req_var}}{{^@last}},{{/@last}}
11+
{{/required}}
12+
{{^required}}
13+
{{>data_class_opt_var}}{{^@last}},{{/@last}}
14+
{{/required}}
15+
{{/vars}}
1316
) {
14-
{{#hasEnums}}{{#vars}}{{#isEnum}}
17+
{{#hasEnums}}{{#vars}}
18+
19+
{{#isEnum}}
1520
/**
1621
* {{{description}}}
1722
* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}}
1823
*/
19-
enum class {{nameInCamelCase}}(val value: {{dataType}}){
24+
{{#items}}
25+
enum class {{{datatypeWithEnum}}}(val value: {{{datatype}}}{{#isNullable}}?{{/isNullable}}){
26+
{{/items}}
27+
{{^items}}
28+
enum class {{nameInCamelCase}}(val value: {{{datatype}}}){
29+
{{/items}}
2030
{{#allowableValues}}{{#enumVars}}
2131
{{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
2232
{{/enumVars}}{{/allowableValues}}
2333
}
24-
{{/isEnum}}{{/vars}}{{/hasEnums}}
34+
35+
{{/isEnum}}
36+
{{/vars}}{{/hasEnums}}
2537
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{{#description}}
22
/* {{{description}}} */
33
{{/description}}
4-
val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{/isEnum}}? = {{#defaultvalue}}{{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}null{{/defaultvalue}}
4+
val {{{name}}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{/isEnum}}? = {{#defaultvalue}}{{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}null{{/defaultvalue}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{{#description}}
22
/* {{{description}}} */
33
{{/description}}
4-
val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{/isEnum}}
4+
val {{{name}}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{/isEnum}}

modules/swagger-codegen/src/main/resources/kotlin-server/enum_class.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
* {{{description}}}
33
* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}}
44
*/
5-
enum class {{classname}}(val value: {{dataType}}){
5+
enum class {{classname}}(val value: {{dataType}}{{#isNullable}}?{{/isNullable}}){
66
{{#allowableValues}}{{#enumVars}}
77
{{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
88
{{/enumVars}}{{/allowableValues}}
9-
}
9+
}

modules/swagger-codegen/src/main/resources/kotlin-server/libraries/ktor/ApiKeyAuth.kt.mustache

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,60 @@ package {{packageName}}.infrastructure
22

33
import io.ktor.application.ApplicationCall
44
import io.ktor.application.call
5-
import io.ktor.auth.*
5+
import io.ktor.auth.Authentication
6+
import io.ktor.auth.AuthenticationFailedCause
7+
import io.ktor.auth.AuthenticationPipeline
8+
import io.ktor.auth.AuthenticationProvider
9+
import io.ktor.auth.Credential
10+
import io.ktor.auth.Principal
11+
import io.ktor.auth.UnauthorizedResponse
12+
import io.ktor.http.auth.HeaderValueEncoding
13+
import io.ktor.http.auth.HttpAuthHeader
614
import io.ktor.request.ApplicationRequest
715
import io.ktor.response.respond
816

9-
10-
import io.ktor.application.*
11-
import io.ktor.pipeline.*
12-
import io.ktor.request.*
13-
import io.ktor.response.*
14-
import java.util.*
15-
1617
enum class ApiKeyLocation(val location: String) {
1718
QUERY("query"),
1819
HEADER("header")
1920
}
20-
data class ApiKey(val value: String): Credential
21-
data class ApiPrincipal(val apiKey: ApiKey?) : Principal
22-
fun ApplicationCall.apiKey(key: String, keyLocation: ApiKeyLocation = ApiKeyLocation.valueOf("header")): ApiKey? = request.apiKey(key, keyLocation)
23-
fun ApplicationRequest.apiKey(key: String, keyLocation: ApiKeyLocation = ApiKeyLocation.valueOf("header")): ApiKey? {
24-
val value: String? = when(keyLocation) {
25-
ApiKeyLocation.QUERY -> this.queryParameters[key]
26-
ApiKeyLocation.HEADER -> this.headers[key]
27-
}
28-
when (value) {
29-
null -> return null
30-
else -> return ApiKey(value)
21+
data class ApiKeyCredential(val value: String): Credential
22+
data class ApiPrincipal(val apiKeyCredential: ApiKeyCredential?) : Principal
23+
24+
25+
26+
/**
27+
* Represents a Api Key authentication provider
28+
* @param name is the name of the provider, or `null` for a default provider
29+
*/
30+
class ApiKeyAuthenticationProvider(config: Configuration) : AuthenticationProvider(config) {
31+
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = { null }
32+
33+
var apiKeyName: String = "";
34+
35+
var apiKeyLocation: ApiKeyLocation = ApiKeyLocation.QUERY;
36+
37+
/**
38+
* Sets a validation function that will check given [ApiKeyCredential] instance and return [Principal],
39+
* or null if credential does not correspond to an authenticated principal
40+
*/
41+
fun validate(body: suspend ApplicationCall.(ApiKeyCredential) -> Principal?) {
42+
authenticationFunction = body
3143
}
3244
}
3345

34-
fun AuthenticationPipeline.apiKeyAuth(apiKeyName: String, authLocation: String, validate: suspend (ApiKey) -> ApiPrincipal?) {
35-
intercept(AuthenticationPipeline.RequestAuthentication) { context ->
36-
val credentials = call.request.apiKey(apiKeyName, ApiKeyLocation.values().first { it.location == authLocation })
37-
val principal = credentials?.let { validate(it) }
46+
class ApiKeyConfiguration(name: String?) : AuthenticationProvider.Configuration(name) {
47+
//todo
48+
}
49+
50+
fun Authentication.Configuration.apiKeyAuth(name: String? = null, configure: ApiKeyAuthenticationProvider.() -> Unit) {
51+
val provider = ApiKeyAuthenticationProvider(ApiKeyConfiguration(name)).apply(configure)
52+
val apiKeyName = provider.apiKeyName
53+
val apiKeyLocation = provider.apiKeyLocation
54+
val authenticate = provider.authenticationFunction
55+
56+
provider.pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
57+
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
58+
val principal = credentials?.let { authenticate(call, it) }
3859

3960
val cause = when {
4061
credentials == null -> AuthenticationFailedCause.NoCredentials
@@ -49,9 +70,20 @@ fun AuthenticationPipeline.apiKeyAuth(apiKeyName: String, authLocation: String,
4970
it.complete()
5071
}
5172
}
73+
5274
if (principal != null) {
5375
context.principal(principal)
5476
}
5577
}
5678
}
5779

80+
fun ApplicationRequest.apiKeyAuthenticationCredentials(apiKeyName: String, apiKeyLocation: ApiKeyLocation): ApiKeyCredential? {
81+
val value: String? = when(apiKeyLocation) {
82+
ApiKeyLocation.QUERY -> this.queryParameters[apiKeyName]
83+
ApiKeyLocation.HEADER -> this.headers[apiKeyName]
84+
}
85+
when (value) {
86+
null -> return null
87+
else -> return ApiKeyCredential(value)
88+
}
89+
}

modules/swagger-codegen/src/main/resources/kotlin-server/libraries/ktor/AppMain.kt.mustache

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,63 @@
11
package {{packageName}}
22

3-
import com.codahale.metrics.*
3+
import com.codahale.metrics.Slf4jReporter
44
import com.typesafe.config.ConfigFactory
5-
import io.ktor.application.*
5+
import io.ktor.application.Application
6+
import io.ktor.application.ApplicationStopping
7+
import io.ktor.application.install
8+
import io.ktor.application.log
69
import io.ktor.client.HttpClient
710
import io.ktor.client.engine.apache.Apache
811
import io.ktor.config.HoconApplicationConfig
9-
import io.ktor.features.*
12+
{{#featureAutoHead}}
13+
import io.ktor.features.AutoHeadResponse
14+
{{/featureAutoHead}}
15+
{{#featureCompression}}
16+
import io.ktor.features.Compression
17+
{{/featureCompression}}
18+
{{#featureCORS}}
19+
import io.ktor.features.CORS
20+
{{/featureCORS}}
21+
{{#featureConditionalHeaders}}
22+
import io.ktor.features.ConditionalHeaders
23+
{{/featureConditionalHeaders}}
24+
import io.ktor.features.ContentNegotiation
25+
import io.ktor.features.DefaultHeaders
26+
{{#featureHSTS}}
27+
import io.ktor.features.HSTS
28+
{{/featureHSTS}}
1029
import io.ktor.gson.GsonConverter
1130
import io.ktor.http.ContentType
12-
import io.ktor.locations.*
13-
import io.ktor.metrics.*
14-
import io.ktor.routing.*
15-
import java.util.concurrent.*
16-
{{#generateApis}}
17-
import {{apiPackage}}.*
18-
{{/generateApis}}
19-
20-
{{#imports}}import {{import}}
21-
{{/imports}}
31+
import io.ktor.locations.KtorExperimentalLocationsAPI
32+
import io.ktor.locations.Locations
33+
import io.ktor.routing.Routing
34+
import java.util.concurrent.TimeUnit
35+
import io.ktor.util.KtorExperimentalAPI
36+
{{#hasAuthMethods}}
37+
import io.ktor.auth.Authentication
38+
import io.ktor.auth.oauth
39+
import io.ktor.auth.basic
40+
import io.ktor.auth.UserIdPrincipal
41+
import io.ktor.metrics.dropwizard.DropwizardMetrics
42+
import io.swagger.server.infrastructure.ApiKeyCredential
43+
import io.swagger.server.infrastructure.ApiPrincipal
44+
import io.swagger.server.infrastructure.apiKeyAuth
45+
{{/hasAuthMethods}}
46+
{{#generateApis}}{{#apiInfo}}{{#apis}}import {{apiPackage}}.{{classname}}
47+
{{/apis}}{{/apiInfo}}{{/generateApis}}
2248

49+
@KtorExperimentalAPI
2350
internal val settings = HoconApplicationConfig(ConfigFactory.defaultApplication(HTTP::class.java.classLoader))
2451

2552
object HTTP {
2653
val client = HttpClient(Apache)
2754
}
2855

56+
@KtorExperimentalAPI
57+
@KtorExperimentalLocationsAPI
2958
fun Application.main() {
3059
install(DefaultHeaders)
31-
install(Metrics) {
60+
install(DropwizardMetrics) {
3261
val reporter = Slf4jReporter.forRegistry(registry)
3362
.outputTo(log)
3463
.convertRatesTo(TimeUnit.SECONDS)
@@ -56,6 +85,50 @@ fun Application.main() {
5685
install(Compression, ApplicationCompressionConfiguration()) // see http://ktor.io/features/compression.html
5786
{{/featureCompression}}
5887
install(Locations) // see http://ktor.io/features/locations.html
88+
{{#hasAuthMethods}}
89+
install(Authentication) {
90+
{{#authMethods}}
91+
{{#isBasic}}
92+
basic("{{{name}}}") {
93+
validate { credentials ->
94+
// TODO: "Apply your basic authentication functionality."
95+
// Accessible in-method via call.principal<UserIdPrincipal>()
96+
if (credentials.name == "Swagger" && "Codegen" == credentials.password) {
97+
UserIdPrincipal(credentials.name)
98+
} else {
99+
null
100+
}
101+
}
102+
}
103+
{{/isBasic}}
104+
{{#isApiKey}}
105+
// "Implement API key auth ({{{name}}}) for parameter name '{{{keyParamName}}}'."
106+
apiKeyAuth("{{{name}}}") {
107+
validate { apikeyCredential: ApiKeyCredential ->
108+
when {
109+
apikeyCredential.value == "keyboardcat" -> ApiPrincipal(apikeyCredential)
110+
else -> null
111+
}
112+
}
113+
}
114+
{{/isApiKey}}
115+
{{#isOAuth}}
116+
{{#bodyAllowed}}
117+
{{/bodyAllowed}}
118+
{{^bodyAllowed}}
119+
oauth("{{name}}") {
120+
client = HttpClient(Apache)
121+
providerLookup = { ApplicationAuthProviders["{{{name}}}"] }
122+
urlProvider = { _ ->
123+
// TODO: define a callback url here.
124+
"/"
125+
}
126+
}
127+
{{/bodyAllowed}}
128+
{{/isOAuth}}
129+
{{/authMethods}}
130+
}
131+
{{/hasAuthMethods}}
59132
install(Routing) {
60133
{{#apiInfo}}
61134
{{#apis}}
@@ -65,10 +138,11 @@ fun Application.main() {
65138
{{/apis}}
66139
{{/apiInfo}}
67140
}
141+
68142
{{/generateApis}}
69143

70144
environment.monitor.subscribe(ApplicationStopping)
71145
{
72146
HTTP.client.close()
73147
}
74-
}
148+
}

modules/swagger-codegen/src/main/resources/kotlin-server/libraries/ktor/Configuration.kt.mustache

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ package {{packageName}}
22

33
// Use this file to hold package-level internal functions that return receiver object passed to the `install` method.
44
import io.ktor.auth.OAuthServerSettings
5-
import io.ktor.features.*
6-
import io.ktor.http.*
5+
import io.ktor.features.Compression
6+
import io.ktor.features.HSTS
7+
import io.ktor.features.deflate
8+
import io.ktor.features.gzip
9+
import io.ktor.features.maxAge
10+
import io.ktor.features.minimumSize
11+
import io.ktor.http.HttpMethod
12+
import io.ktor.util.KtorExperimentalAPI
713
import java.time.Duration
814
import java.util.concurrent.Executors
915

@@ -13,7 +19,7 @@ import {{packageName}}.settings
1319
/**
1420
* Application block for [CORS] configuration.
1521
*
16-
* This file may be excluded in .swagger-codegen-ignore,
22+
* This file may be excluded in .openapi-generator-ignore,
1723
* and application specific configuration can be applied in this function.
1824
*
1925
* See http://ktor.io/features/cors.html
@@ -37,7 +43,7 @@ internal fun ApplicationCORSConfiguration(): CORS.Configuration.() -> Unit {
3743
/**
3844
* Application block for [HSTS] configuration.
3945
*
40-
* This file may be excluded in .swagger-codegen-ignore,
46+
* This file may be excluded in .openapi-generator-ignore,
4147
* and application specific configuration can be applied in this function.
4248
*
4349
* See http://ktor.io/features/hsts.html
@@ -58,7 +64,7 @@ internal fun ApplicationHstsConfiguration(): HSTS.Configuration.() -> Unit {
5864
/**
5965
* Application block for [Compression] configuration.
6066
*
61-
* This file may be excluded in .swagger-codegen-ignore,
67+
* This file may be excluded in .openapi-generator-ignore,
6268
* and application specific configuration can be applied in this function.
6369
*
6470
* See http://ktor.io/features/compression.html
@@ -77,6 +83,7 @@ internal fun ApplicationCompressionConfiguration(): Compression.Configuration.()
7783
{{/featureCompression}}
7884

7985
// Defines authentication mechanisms used throughout the application.
86+
@KtorExperimentalAPI
8087
val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthServerSettings>(
8188
{{#authMethods}}
8289
{{#isOAuth}}
@@ -88,8 +95,8 @@ val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthSer
8895
{{! TODO: flow, doesn't seem to be supported yet by ktor }}
8996
clientId = settings.property("auth.oauth.{{name}}.clientId").getString(),
9097
clientSecret = settings.property("auth.oauth.{{name}}.clientSecret").getString(),
91-
defaultScopes = listOf({{#scopes}}"{{scope}}"{{#hasMore}}, {{/hasMore}}{{/scopes}})
92-
){{#hasMore}},{{/hasMore}}
98+
defaultScopes = listOf({{#scopes}}"{{scope}}"{{^-last}}, {{/-last}}{{/scopes}})
99+
){{^-last}},{{/-last}}
93100
{{/isOAuth}}
94101
{{/authMethods}}
95102
// OAuthServerSettings.OAuth2ServerSettings(

0 commit comments

Comments
 (0)