Skip to content

Commit

Permalink
[Ktor] Update generator to latest Ktor version OpenAPITools#14061
Browse files Browse the repository at this point in the history
  • Loading branch information
rsinukov committed Dec 20, 2022
1 parent 82971cc commit 5c39bab
Show file tree
Hide file tree
Showing 26 changed files with 1,074 additions and 311 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen implements BeanVa
private Boolean hstsFeatureEnabled = true;
private Boolean corsFeatureEnabled = false;
private Boolean compressionFeatureEnabled = true;
private Boolean locationsFeatureEnabled = true;
private Boolean resourcesFeatureEnabled = true;
private Boolean metricsFeatureEnabled = true;
private boolean interfaceOnly = false;
private boolean useBeanValidation = false;
Expand All @@ -62,7 +62,7 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen implements BeanVa
Constants.HSTS,
Constants.CORS,
Constants.COMPRESSION,
Constants.LOCATIONS,
Constants.RESOURCES,
Constants.METRICS
))
.build();
Expand Down Expand Up @@ -126,7 +126,7 @@ public KotlinServerCodegen() {
addSwitch(Constants.HSTS, Constants.HSTS_DESC, getHstsFeatureEnabled());
addSwitch(Constants.CORS, Constants.CORS_DESC, getCorsFeatureEnabled());
addSwitch(Constants.COMPRESSION, Constants.COMPRESSION_DESC, getCompressionFeatureEnabled());
addSwitch(Constants.LOCATIONS, Constants.LOCATIONS_DESC, getLocationsFeatureEnabled());
addSwitch(Constants.RESOURCES, Constants.RESOURCES_DESC, getResourcesFeatureEnabled());
addSwitch(Constants.METRICS, Constants.METRICS_DESC, getMetricsFeatureEnabled());

cliOptions.add(CliOption.newBoolean(INTERFACE_ONLY, "Whether to generate only API interface stubs without the server files. This option is currently supported only when using jaxrs-spec library.").defaultValue(String.valueOf(interfaceOnly)));
Expand Down Expand Up @@ -179,12 +179,12 @@ public void setHstsFeatureEnabled(Boolean hstsFeatureEnabled) {
this.hstsFeatureEnabled = hstsFeatureEnabled;
}

public Boolean getLocationsFeatureEnabled() {
return locationsFeatureEnabled;
public Boolean getResourcesFeatureEnabled() {
return resourcesFeatureEnabled;
}

public void setLocationsFeatureEnabled(Boolean locationsFeatureEnabled) {
this.locationsFeatureEnabled = locationsFeatureEnabled;
public void setResourcesFeatureEnabled(Boolean resourcesFeatureEnabled) {
this.resourcesFeatureEnabled = resourcesFeatureEnabled;
}

public Boolean getMetricsFeatureEnabled() {
Expand Down Expand Up @@ -279,10 +279,10 @@ public void processOpts() {
additionalProperties.put(Constants.COMPRESSION, getCompressionFeatureEnabled());
}

if (additionalProperties.containsKey(Constants.LOCATIONS)) {
setLocationsFeatureEnabled(convertPropertyToBooleanAndWriteBack(Constants.LOCATIONS));
if (additionalProperties.containsKey(Constants.RESOURCES)) {
setResourcesFeatureEnabled(convertPropertyToBooleanAndWriteBack(Constants.RESOURCES));
} else {
additionalProperties.put(Constants.LOCATIONS, getLocationsFeatureEnabled());
additionalProperties.put(Constants.RESOURCES, getResourcesFeatureEnabled());
}

if (additionalProperties.containsKey(Constants.METRICS)) {
Expand All @@ -308,7 +308,7 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("AppMain.kt.mustache", packageFolder, "AppMain.kt"));
supportingFiles.add(new SupportingFile("Configuration.kt.mustache", packageFolder, "Configuration.kt"));

if (generateApis && locationsFeatureEnabled) {
if (generateApis && resourcesFeatureEnabled) {
supportingFiles.add(new SupportingFile("Paths.kt.mustache", packageFolder, "Paths.kt"));
}

Expand Down Expand Up @@ -339,8 +339,8 @@ public static class Constants {
public final static String CORS_DESC = "Ktor by default provides an interceptor for implementing proper support for Cross-Origin Resource Sharing (CORS). See enable-cors.org.";
public final static String COMPRESSION = "featureCompression";
public final static String COMPRESSION_DESC = "Adds ability to compress outgoing content using gzip, deflate or custom encoder and thus reduce size of the response.";
public final static String LOCATIONS = "featureLocations";
public final static String LOCATIONS_DESC = "Generates routes in a typed way, for both: constructing URLs and reading the parameters.";
public final static String RESOURCES = "featureResources";
public final static String RESOURCES_DESC = "Generates routes in a typed way, for both: constructing URLs and reading the parameters.";
public final static String METRICS = "featureMetrics";
public final static String METRICS_DESC = "Enables metrics feature.";
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package {{packageName}}.infrastructure
package org.openapitools.server.infrastructure

import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.http.auth.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.server.response.*

enum class ApiKeyLocation(val location: String) {
QUERY("query"),
Expand All @@ -15,8 +15,7 @@ data class ApiKeyCredential(val value: String) : Credential
data class ApiPrincipal(val apiKeyCredential: ApiKeyCredential?) : Principal

/**
* Represents a Api Key authentication provider
* @param name is the name of the provider, or `null` for a default provider
* Represents an Api Key authentication provider
*/
class ApiKeyAuthenticationProvider(configuration: Configuration) : AuthenticationProvider(configuration) {
Expand All @@ -26,39 +25,38 @@ class ApiKeyAuthenticationProvider(configuration: Configuration) : Authenticatio
private val apiKeyLocation: ApiKeyLocation = configuration.apiKeyLocation
internal fun install() {
pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
val principal = credentials?.let { authenticationFunction(call, it) }
override suspend fun onAuthenticate(context: AuthenticationContext) {
val call = context.call
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
val principal = credentials?.let { authenticationFunction.invoke(call, it) }

val cause = when {
credentials == null -> AuthenticationFailedCause.NoCredentials
principal == null -> AuthenticationFailedCause.InvalidCredentials
else -> null
}
val cause = when {
credentials == null -> AuthenticationFailedCause.NoCredentials
principal == null -> AuthenticationFailedCause.InvalidCredentials
else -> null
}

if (cause != null) {
context.challenge(apiKeyName, cause) {
call.respond(
UnauthorizedResponse(
HttpAuthHeader.Parameterized(
"API_KEY",
mapOf("key" to apiKeyName),
HeaderValueEncoding.QUOTED_ALWAYS
)
if (cause != null) {
context.challenge(apiKeyName, cause) { challenge, call ->
call.respond(
UnauthorizedResponse(
HttpAuthHeader.Parameterized(
"API_KEY",
mapOf("key" to apiKeyName),
HeaderValueEncoding.QUOTED_ALWAYS
)
)
it.complete()
}
)
challenge.complete()
}
}

if (principal != null) {
context.principal(principal)
}
if (principal != null) {
context.principal(principal)
}
}

class Configuration internal constructor(name: String?) : AuthenticationProvider.Configuration(name) {
class Configuration internal constructor(name: String?) : Config(name) {
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = {
throw NotImplementedError(
Expand All @@ -80,13 +78,12 @@ class ApiKeyAuthenticationProvider(configuration: Configuration) : Authenticatio
}
}

fun Authentication.Configuration.apiKeyAuth(
fun AuthenticationConfig.apiKeyAuth(
name: String? = null,
configure: ApiKeyAuthenticationProvider.Configuration.() -> Unit
) {
val configuration = ApiKeyAuthenticationProvider.Configuration(name).apply(configure)
val provider = ApiKeyAuthenticationProvider(configuration)
provider.install()
register(provider)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,40 @@
package {{packageName}}

{{#featureMetrics}}
import com.codahale.metrics.Slf4jReporter
{{/featureMetrics}}
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.gson.*
import io.ktor.server.application.*
import io.ktor.serialization.gson.*
import io.ktor.http.*
{{#featureLocations}}
import io.ktor.locations.*
{{/featureLocations}}
{{#featureResources}}
import io.ktor.server.resources.*
{{/featureResources}}
{{#featureCORS}}
import io.ktor.server.plugins.cors.routing.*
{{/featureCORS}}
{{#featureAutoHead}}
import io.ktor.server.plugins.autohead.*
{{/featureAutoHead}}
{{#featureConditionalHeaders}}
import io.ktor.server.plugins.conditionalheaders.*
{{/featureConditionalHeaders}}
{{#featureCompression}}
import io.ktor.server.plugins.compression.*
{{/featureCompression}}
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.defaultheaders.*
{{#featureHSTS}}
import io.ktor.server.plugins.hsts.*
{{/featureHSTS}}
{{#featureMetrics}}
import io.ktor.metrics.dropwizard.*
import com.codahale.metrics.Slf4jReporter
import io.ktor.server.metrics.dropwizard.*
import java.util.concurrent.TimeUnit
{{/featureMetrics}}
import io.ktor.routing.*
import io.ktor.util.*
import io.ktor.server.routing.*
{{#hasAuthMethods}}
import com.typesafe.config.ConfigFactory
import io.ktor.auth.*
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache.Apache
import io.ktor.config.HoconApplicationConfig
import io.ktor.server.config.HoconApplicationConfig
import io.ktor.server.auth.*
import org.openapitools.server.infrastructure.*
{{/hasAuthMethods}}
{{#generateApis}}{{#apiInfo}}{{#apis}}import {{apiPackage}}.{{classname}}
Expand All @@ -35,16 +48,12 @@ object HTTP {
}
{{/hasAuthMethods}}

@KtorExperimentalAPI
{{#featureLocations}}
@KtorExperimentalLocationsAPI
{{/featureLocations}}
fun Application.main() {
install(DefaultHeaders)
{{#featureMetrics}}
install(DropwizardMetrics) {
val reporter = Slf4jReporter.forRegistry(registry)
.outputTo(log)
.outputTo(this@main.log)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build()
Expand All @@ -70,9 +79,9 @@ fun Application.main() {
{{#featureHSTS}}
install(HSTS, ApplicationHstsConfiguration()) // see https://ktor.io/docs/hsts.html
{{/featureHSTS}}
{{#featureLocations}}
install(Locations) // see https://ktor.io/docs/features-locations.html
{{/featureLocations}}
{{#featureResources}}
install(Resources)
{{/featureResources}}
{{#hasAuthMethods}}
install(Authentication) {
{{#authMethods}}
Expand Down Expand Up @@ -105,7 +114,7 @@ fun Application.main() {
{{^bodyAllowed}}
oauth("{{name}}") {
client = HttpClient(Apache)
providerLookup = { ApplicationAuthProviders["{{{name}}}"] }
providerLookup = { applicationAuthProvider([email protected]) }
urlProvider = { _ ->
// TODO: define a callback url here.
"/"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
package {{packageName}}

// Use this file to hold package-level internal functions that return receiver object passed to the `install` method.
import io.ktor.auth.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.server.auth.*
import io.ktor.server.config.*
import io.ktor.util.*
import java.time.Duration
import java.util.concurrent.TimeUnit
{{#featureCORS}}
import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.plugins.cors.*
{{/featureCORS}}
{{#featureCompression}}
import io.ktor.server.plugins.compression.*
{{/featureCompression}}
{{#featureHSTS}}
import io.ktor.server.plugins.hsts.*
{{/featureHSTS}}

{{#featureCORS}}
/**
* Application block for [CORS] configuration.
*
* This file may be excluded in .openapi-generator-ignore,
* and application specific configuration can be applied in this function.
* and application-specific configuration can be applied in this function.
*
* See http://ktor.io/features/cors.html
*/
internal fun ApplicationCORSConfiguration(): CORS.Configuration.() -> Unit {
internal fun ApplicationCORSConfiguration(): CORSConfig.() -> Unit {
return {
// method(HttpMethod.Options)
// header(HttpHeaders.XForwardedProto)
Expand All @@ -37,11 +47,11 @@ internal fun ApplicationCORSConfiguration(): CORS.Configuration.() -> Unit {
* Application block for [HSTS] configuration.
*
* This file may be excluded in .openapi-generator-ignore,
* and application specific configuration can be applied in this function.
* and application-specific configuration can be applied in this function.
*
* See http://ktor.io/features/hsts.html
*/
internal fun ApplicationHstsConfiguration(): HSTS.Configuration.() -> Unit {
internal fun ApplicationHstsConfiguration(): HSTSConfig.() -> Unit {
return {
maxAgeInSeconds = TimeUnit.DAYS.toSeconds(365)
includeSubDomains = true
Expand All @@ -58,11 +68,11 @@ internal fun ApplicationHstsConfiguration(): HSTS.Configuration.() -> Unit {
* Application block for [Compression] configuration.
*
* This file may be excluded in .openapi-generator-ignore,
* and application specific configuration can be applied in this function.
* and application-specific configuration can be applied in this function.
*
* See http://ktor.io/features/compression.html
*/
internal fun ApplicationCompressionConfiguration(): Compression.Configuration.() -> Unit {
internal fun ApplicationCompressionConfiguration(): CompressionConfig.() -> Unit {
return {
gzip {
priority = 1.0
Expand All @@ -76,29 +86,13 @@ internal fun ApplicationCompressionConfiguration(): Compression.Configuration.()
{{/featureCompression}}

// Defines authentication mechanisms used throughout the application.
val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthServerSettings>(
{{#authMethods}}
{{#isOAuth}}
OAuthServerSettings.OAuth2ServerSettings(
name = "{{name}}",
authorizeUrl = "{{authorizationUrl}}",
accessTokenUrl = "{{tokenUrl}}",
requestMethod = HttpMethod.Get,
{{! TODO: flow, doesn't seem to be supported yet by ktor }}
clientId = settings.property("auth.oauth.{{name}}.clientId").getString(),
clientSecret = settings.property("auth.oauth.{{name}}.clientSecret").getString(),
defaultScopes = listOf({{#scopes}}"{{scope}}"{{^-last}}, {{/-last}}{{/scopes}})
){{^-last}},{{/-last}}
{{/isOAuth}}
{{/authMethods}}
// OAuthServerSettings.OAuth2ServerSettings(
// name = "facebook",
// authorizeUrl = "https://graph.facebook.com/oauth/authorize",
// accessTokenUrl = "https://graph.facebook.com/oauth/access_token",
// requestMethod = HttpMethod.Post,
//
// clientId = settings.property("auth.oauth.facebook.clientId").getString(),
// clientSecret = settings.property("auth.oauth.facebook.clientSecret").getString(),
// defaultScopes = listOf("public_profile")
// )
).associateBy { it.name }
fun applicationAuthProvider(config: ApplicationConfig): OAuthServerSettings =
OAuthServerSettings.OAuth2ServerSettings(
name = "petstore_auth",
authorizeUrl = "http://petstore.swagger.io/api/oauth/dialog",
accessTokenUrl = "",
requestMethod = HttpMethod.Get,
clientId = config.property("auth.oauth.petstore_auth.clientId").getString(),
clientSecret = config.property("auth.oauth.petstore_auth.clientSecret").getString(),
defaultScopes = listOf("write:pets", "read:pets")
)
Loading

0 comments on commit 5c39bab

Please sign in to comment.