Ktor server upgrade (#9088)

* Update to new ktor version #9087

* Update doc #9087

* Cleanup #9087
This commit is contained in:
Rustam
2021-04-28 15:57:15 +02:00
committed by GitHub
parent 73564bc046
commit 53398a0850
31 changed files with 481 additions and 410 deletions

View File

@@ -16,6 +16,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|featureCompression|Adds ability to compress outgoing content using gzip, deflate or custom encoder and thus reduce size of the response.| |true|
|featureConditionalHeaders|Avoid sending content if client already has same content, by checking ETag or LastModified properties.| |false|
|featureHSTS|Avoid sending content if client already has same content, by checking ETag or LastModified properties.| |true|
|featureLocations|Generates routes in a typed way, for both: constructing URLs and reading the parameters.| |true|
|groupId|Generated artifact package's organization (i.e. maven groupId).| |org.openapitools|
|library|library template (sub-template)|<dl><dt>**ktor**</dt><dd>ktor framework</dd></dl>|ktor|
|modelMutable|Create mutable models| |false|

View File

@@ -42,15 +42,17 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
private Boolean hstsFeatureEnabled = true;
private Boolean corsFeatureEnabled = false;
private Boolean compressionFeatureEnabled = true;
private Boolean locationsFeatureEnabled = true;
// This is here to potentially warn the user when an option is not supoprted by the target framework.
// This is here to potentially warn the user when an option is not supported by the target framework.
private Map<String, List<String>> optionsSupportedPerFramework = new ImmutableMap.Builder<String, List<String>>()
.put(Constants.KTOR, Arrays.asList(
Constants.AUTOMATIC_HEAD_REQUESTS,
Constants.CONDITIONAL_HEADERS,
Constants.HSTS,
Constants.CORS,
Constants.COMPRESSION
Constants.COMPRESSION,
Constants.LOCATIONS
))
.build();
@@ -85,6 +87,8 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
artifactId = "kotlin-server";
packageName = "org.openapitools.server";
typeMapping.put("array", "kotlin.collections.List");
// cliOptions default redefinition need to be updated
updateOption(CodegenConstants.ARTIFACT_ID, this.artifactId);
updateOption(CodegenConstants.PACKAGE_NAME, this.packageName);
@@ -110,6 +114,7 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
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());
}
public Boolean getAutoHeadFeatureEnabled() {
@@ -156,6 +161,14 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
this.hstsFeatureEnabled = hstsFeatureEnabled;
}
public Boolean getLocationsFeatureEnabled() {
return locationsFeatureEnabled;
}
public void setLocationsFeatureEnabled(Boolean locationsFeatureEnabled) {
this.locationsFeatureEnabled = locationsFeatureEnabled;
}
public String getName() {
return "kotlin-server";
}
@@ -209,6 +222,12 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
additionalProperties.put(Constants.COMPRESSION, getCompressionFeatureEnabled());
}
if (additionalProperties.containsKey(Constants.LOCATIONS)) {
setLocationsFeatureEnabled(convertPropertyToBooleanAndWriteBack(Constants.LOCATIONS));
} else {
additionalProperties.put(Constants.LOCATIONS, getLocationsFeatureEnabled());
}
boolean generateApis = additionalProperties.containsKey(CodegenConstants.GENERATE_APIS) && (Boolean) additionalProperties.get(CodegenConstants.GENERATE_APIS);
String packageFolder = (sourceFolder + File.separator + packageName).replace(".", File.separator);
String resourcesFolder = "src/main/resources"; // not sure this can be user configurable.
@@ -223,7 +242,7 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
supportingFiles.add(new SupportingFile("AppMain.kt.mustache", packageFolder, "AppMain.kt"));
supportingFiles.add(new SupportingFile("Configuration.kt.mustache", packageFolder, "Configuration.kt"));
if (generateApis) {
if (generateApis && locationsFeatureEnabled) {
supportingFiles.add(new SupportingFile("Paths.kt.mustache", packageFolder, "Paths.kt"));
}
@@ -247,6 +266,8 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
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.";
}
@Override

View File

@@ -2,8 +2,8 @@
## Requires
* Kotlin 1.1.2
* Gradle 3.3
* Kotlin 1.4.31
* Gradle 6.8.2
## Build

View File

@@ -15,7 +15,7 @@ import java.io.Serializable
{{#parcelizeModels}}
@Parcelize
{{/parcelizeModels}}
data class {{classname}} (
data class {{classname}}(
{{#requiredVars}}
{{>data_class_req_var}}{{^-last}},
{{/-last}}{{/requiredVars}}{{#hasRequired}}{{#hasOptional}},

View File

@@ -2,7 +2,7 @@
* {{{description}}}
* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}}
*/
enum class {{classname}}(val value: {{dataType}}){
enum class {{classname}}(val value: {{dataType}}) {
{{#allowableValues}}{{#enumVars}}
{{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
{{/enumVars}}{{/allowableValues}}

View File

@@ -1,85 +1,105 @@
package {{packageName}}.infrastructure
import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.auth.Authentication
import io.ktor.auth.AuthenticationFailedCause
import io.ktor.auth.AuthenticationPipeline
import io.ktor.auth.AuthenticationProvider
import io.ktor.auth.Credential
import io.ktor.auth.Principal
import io.ktor.auth.UnauthorizedResponse
import io.ktor.http.auth.HeaderValueEncoding
import io.ktor.http.auth.HttpAuthHeader
import io.ktor.request.ApplicationRequest
import io.ktor.response.respond
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.http.auth.*
import io.ktor.request.*
import io.ktor.response.*
enum class ApiKeyLocation(val location: String) {
QUERY("query"),
HEADER("header")
}
data class ApiKeyCredential(val value: String): Credential
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
*/
class ApiKeyAuthenticationProvider(name: String?) : AuthenticationProvider(name) {
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = { null }
class ApiKeyAuthenticationProvider(configuration: Configuration) : AuthenticationProvider(configuration) {
var apiKeyName: String = "";
private val authenticationFunction = configuration.authenticationFunction
var apiKeyLocation: ApiKeyLocation = ApiKeyLocation.QUERY;
private val apiKeyName: String = configuration.apiKeyName
/**
* Sets a validation function that will check given [ApiKeyCredential] instance and return [Principal],
* or null if credential does not correspond to an authenticated principal
*/
fun validate(body: suspend ApplicationCall.(ApiKeyCredential) -> Principal?) {
authenticationFunction = body
}
}
private val apiKeyLocation: ApiKeyLocation = configuration.apiKeyLocation
fun Authentication.Configuration.apiKeyAuth(name: String? = null, configure: ApiKeyAuthenticationProvider.() -> Unit) {
val provider = ApiKeyAuthenticationProvider(name).apply(configure)
val apiKeyName = provider.apiKeyName
val apiKeyLocation = provider.apiKeyLocation
val authenticate = provider.authenticationFunction
internal fun install() {
pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
val principal = credentials?.let { authenticationFunction(call, it) }
provider.pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
val principal = credentials?.let { authenticate(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
)
)
)
it.complete()
}
}
if (cause != null) {
context.challenge(apiKeyName, cause) {
// TODO: Verify correct response structure here.
call.respond(UnauthorizedResponse(HttpAuthHeader.Parameterized("API_KEY", mapOf("key" to apiKeyName), HeaderValueEncoding.QUOTED_ALWAYS)))
it.complete()
if (principal != null) {
context.principal(principal)
}
}
}
if (principal != null) {
context.principal(principal)
class Configuration internal constructor(name: String?) : AuthenticationProvider.Configuration(name) {
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = {
throw NotImplementedError(
"Api Key auth validate function is not specified. Use apiKeyAuth { validate { ... } } to fix."
)
}
var apiKeyName: String = ""
var apiKeyLocation: ApiKeyLocation = ApiKeyLocation.QUERY
/**
* Sets a validation function that will check given [ApiKeyCredential] instance and return [Principal],
* or null if credential does not correspond to an authenticated principal
*/
fun validate(body: suspend ApplicationCall.(ApiKeyCredential) -> Principal?) {
authenticationFunction = body
}
}
}
fun ApplicationRequest.apiKeyAuthenticationCredentials(apiKeyName: String, apiKeyLocation: ApiKeyLocation): ApiKeyCredential? {
val value: String? = when(apiKeyLocation) {
fun Authentication.Configuration.apiKeyAuth(
name: String? = null,
configure: ApiKeyAuthenticationProvider.Configuration.() -> Unit
) {
val configuration = ApiKeyAuthenticationProvider.Configuration(name).apply(configure)
val provider = ApiKeyAuthenticationProvider(configuration)
provider.install()
register(provider)
}
fun ApplicationRequest.apiKeyAuthenticationCredentials(
apiKeyName: String,
apiKeyLocation: ApiKeyLocation
): ApiKeyCredential? {
val value: String? = when (apiKeyLocation) {
ApiKeyLocation.QUERY -> this.queryParameters[apiKeyName]
ApiKeyLocation.HEADER -> this.headers[apiKeyName]
}
when (value) {
null -> return null
else -> return ApiKeyCredential(value)
return when (value) {
null -> null
else -> ApiKeyCredential(value)
}
}

View File

@@ -28,15 +28,16 @@ import io.ktor.features.HSTS
{{/featureHSTS}}
import io.ktor.gson.GsonConverter
import io.ktor.http.ContentType
{{#featureLocations}}
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Locations
import io.ktor.metrics.Metrics
{{/featureLocations}}
import io.ktor.routing.Routing
import java.util.concurrent.TimeUnit
import io.ktor.util.KtorExperimentalAPI
{{#hasAuthMethods}}
import io.ktor.auth.Authentication
import io.ktor.auth.oauth
import io.ktor.metrics.dropwizard.DropwizardMetrics
import org.openapitools.server.infrastructure.ApiKeyCredential
import org.openapitools.server.infrastructure.ApiPrincipal
import org.openapitools.server.infrastructure.apiKeyAuth
@@ -44,23 +45,23 @@ import org.openapitools.server.infrastructure.apiKeyAuth
{{#generateApis}}{{#apiInfo}}{{#apis}}import {{apiPackage}}.{{classname}}
{{/apis}}{{/apiInfo}}{{/generateApis}}
@KtorExperimentalAPI
internal val settings = HoconApplicationConfig(ConfigFactory.defaultApplication(HTTP::class.java.classLoader))
object HTTP {
val client = HttpClient(Apache)
}
@KtorExperimentalAPI
{{#featureLocations}}
@KtorExperimentalLocationsAPI
{{/featureLocations}}
fun Application.main() {
install(DefaultHeaders)
install(Metrics) {
install(DropwizardMetrics) {
val reporter = Slf4jReporter.forRegistry(registry)
.outputTo(log)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build()
.outputTo(log)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build()
reporter.start(10, TimeUnit.SECONDS)
}
{{#generateApis}}
@@ -82,7 +83,9 @@ fun Application.main() {
{{#featureCompression}}
install(Compression, ApplicationCompressionConfiguration()) // see http://ktor.io/features/compression.html
{{/featureCompression}}
{{#featureLocations}}
install(Locations) // see http://ktor.io/features/locations.html
{{/featureLocations}}
{{#hasAuthMethods}}
install(Authentication) {
{{#authMethods}}
@@ -117,8 +120,8 @@ fun Application.main() {
client = HttpClient(Apache)
providerLookup = { ApplicationAuthProviders["{{{name}}}"] }
urlProvider = { _ ->
// TODO: define a callback url here.
"/"
// TODO: define a callback url here.
"/"
}
}
{{/bodyAllowed}}
@@ -138,8 +141,7 @@ fun Application.main() {
{{/generateApis}}
environment.monitor.subscribe(ApplicationStopping)
{
environment.monitor.subscribe(ApplicationStopping) {
HTTP.client.close()
}
}

View File

@@ -8,11 +8,7 @@ import io.ktor.features.deflate
import io.ktor.features.gzip
import io.ktor.features.minimumSize
import io.ktor.http.HttpMethod
import io.ktor.util.KtorExperimentalAPI
import java.time.Duration
import java.util.concurrent.Executors
import {{packageName}}.settings
import java.util.concurrent.TimeUnit
{{#featureCORS}}
/**
@@ -49,7 +45,7 @@ internal fun ApplicationCORSConfiguration(): CORS.Configuration.() -> Unit {
*/
internal fun ApplicationHstsConfiguration(): HSTS.Configuration.() -> Unit {
return {
maxAge = Duration.ofDays(365)
maxAgeInSeconds = TimeUnit.DAYS.toSeconds(365)
includeSubDomains = true
preload = false
@@ -82,7 +78,6 @@ internal fun ApplicationCompressionConfiguration(): Compression.Configuration.()
{{/featureCompression}}
// Defines authentication mechanisms used throughout the application.
@KtorExperimentalAPI
val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthServerSettings>(
{{#authMethods}}
{{#isOAuth}}
@@ -109,6 +104,3 @@ val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthSer
// defaultScopes = listOf("public_profile")
// )
).associateBy { it.name }
// Provides an application-level fixed thread pool on which to execute coroutines (mainly)
internal val ApplicationExecutors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 4)

View File

@@ -3,6 +3,7 @@ package {{packageName}}
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import {{packageName}}.models.*
{{#imports}}import {{import}}
{{/imports}}
@@ -11,7 +12,6 @@ object Paths {
{{#apis}}
{{#operations}}
{{#operation}}
{{^bodyAllowed}}
/**
* {{summary}}
* {{#unescapedNotes}}{{.}}{{/unescapedNotes}}
@@ -20,7 +20,6 @@ object Paths {
@KtorExperimentalLocationsAPI
@Location("{{path}}") class {{operationId}}({{#allParams}}val {{paramName}}: {{{dataType}}}{{^required}}? = null{{/required}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
{{/bodyAllowed}}
{{/operation}}
{{/operations}}
{{/apis}}

View File

@@ -8,8 +8,8 @@ Generated by OpenAPI Generator {{generatorVersion}}{{^hideGenerationTimestamp}}
## Requires
* Kotlin 1.3.21
* Gradle 4.9
* Kotlin 1.4.31
* Gradle 6.8.2
## Build

View File

@@ -1,17 +1,13 @@
{{#hasAuthMethods}}
{{>libraries/ktor/_principal}}
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
{{#examples}}
{{#-first}}
{{#lambda.indented}}{{>_response}}{{/lambda.indented}}
{{/-first}}
{{/examples}}
{{^examples}}
call.respond(HttpStatusCode.NotImplemented)
{{/examples}}
}
{{#examples}}
{{#-first}}
{{#lambda.indented}}{{>_response}}{{/lambda.indented}}
{{/-first}}
{{/examples}}
{{^examples}}
call.respond(HttpStatusCode.NotImplemented)
{{/examples}}
{{/hasAuthMethods}}
{{^hasAuthMethods}}
{{#examples}}

View File

@@ -1,11 +1,11 @@
{{#authMethods}}
{{#isBasic}}
val principal = call.authentication.principal<UserIdPrincipal>()
val principal = call.authentication.principal<UserIdPrincipal>()!!
{{/isBasic}}
{{#isApiKey}}
val principal = call.authentication.principal<ApiPrincipal>()
val principal = call.authentication.principal<ApiPrincipal>()!!
{{/isApiKey}}
{{#isOAuth}}
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
val principal = call.authentication.principal<OAuthAccessTokenResponse>()!!
{{/isOAuth}}
{{/authMethods}}

View File

@@ -1,7 +1,7 @@
val exampleContentType = "{{{contentType}}}"
val exampleContentString = """{{&example}}"""
when(exampleContentType) {
when (exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)

View File

@@ -10,17 +10,29 @@ import io.ktor.auth.OAuthAccessTokenResponse
import io.ktor.auth.OAuthServerSettings
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.delete
import io.ktor.locations.get
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.Route
{{#featureLocations}}
import {{packageName}}.Paths
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.delete
import io.ktor.locations.get
import io.ktor.locations.post
import io.ktor.locations.put
import io.ktor.locations.options
import io.ktor.locations.head
{{/featureLocations}}
{{^featureLocations}}
import io.ktor.routing.delete
import io.ktor.routing.get
import io.ktor.routing.post
import io.ktor.routing.put
import io.ktor.routing.options
import io.ktor.routing.head
import io.ktor.routing.route
{{/featureLocations}}
import {{packageName}}.Paths
import {{packageName}}.infrastructure.ApiPrincipal
@@ -28,34 +40,33 @@ import {{packageName}}.infrastructure.ApiPrincipal
{{/imports}}
{{#operations}}
{{#featureLocations}}
@KtorExperimentalLocationsAPI
{{/featureLocations}}
fun Route.{{classname}}() {
val gson = Gson()
val empty = mutableMapOf<String, Any?>()
{{#operation}}
{{#bodyAllowed}}
{{#hasAuthMethods}}
{{#authMethods}}
authenticate("{{{name}}}") {
{{/authMethods}}
{{/hasAuthMethods}}
{{^featureLocations}}
route("{{path}}") {
{{#hasAuthMethods}}
{{#authMethods}}
authenticate("{{{name}}}") {
{{/authMethods}}
{{/hasAuthMethods}}
{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}} {
{{#lambda.indented_12}}{{>libraries/ktor/_api_body}}{{/lambda.indented_12}}
}
{{#hasAuthMethods}}
}
{{/hasAuthMethods}}
}
{{/bodyAllowed}}
{{^bodyAllowed}}
{{! NOTE: Locations can be used on routes without body parameters.}}
{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}<Paths.{{operationId}}> { _: Paths.{{operationId}} ->
{{/featureLocations}}
{{#featureLocations}}
{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}<Paths.{{operationId}}> {
{{#lambda.indented_8}}{{>libraries/ktor/_api_body}}{{/lambda.indented_8}}
}
{{/bodyAllowed}}
{{/featureLocations}}
{{#hasAuthMethods}}
}
{{/hasAuthMethods}}
{{/operation}}
}

View File

@@ -2,13 +2,13 @@ group '{{groupId}}'
version '{{artifactVersion}}'
wrapper {
gradleVersion = '4.9'
gradleVersion = '6.8.2'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.3.21'
ext.ktor_version = '1.1.3'
ext.kotlin_version = '1.4.31'
ext.ktor_version = '1.5.2'
ext.shadow_version = '2.0.3'
repositories {
@@ -29,7 +29,7 @@ apply plugin: 'application'
mainClassName = "io.ktor.server.netty.DevelopmentEngine"
// Initialization order with shadow 2.0.1 and Gradle 4.3 is weird.
// Initialization order with shadow 2.0.1 and Gradle 6.8.2 is weird.
// See https://github.com/johnrengelman/shadow/issues/336#issuecomment-355402508
apply plugin: 'com.github.johnrengelman.shadow'
@@ -59,7 +59,10 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "io.ktor:ktor-metrics:$ktor_version"
compile "io.ktor:ktor-auth:$ktor_version"
{{#featureLocations}}
compile "io.ktor:ktor-locations:$ktor_version"
{{/featureLocations}}
compile "io.ktor:ktor-gson:$ktor_version"
compile "io.ktor:ktor-client-core:$ktor_version"
compile "io.ktor:ktor-client-apache:$ktor_version"

View File

@@ -1,11 +1,11 @@
/**
* {{{appName}}}
* {{{appDescription}}}
*
* {{#version}}The version of the OpenAPI document: {{{version}}}{{/version}}
* {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}}
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
* {{{appName}}}
* {{{appDescription}}}
*
* {{#version}}The version of the OpenAPI document: {{{version}}}{{/version}}
* {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}}
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@@ -6,8 +6,8 @@ Generated by OpenAPI Generator 5.2.0-SNAPSHOT.
## Requires
* Kotlin 1.3.21
* Gradle 4.9
* Kotlin 1.4.31
* Gradle 6.8.2
## Build

View File

@@ -2,13 +2,13 @@ group 'org.openapitools'
version '1.0.0'
wrapper {
gradleVersion = '4.9'
gradleVersion = '6.8.2'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.3.21'
ext.ktor_version = '1.1.3'
ext.kotlin_version = '1.4.31'
ext.ktor_version = '1.5.2'
ext.shadow_version = '2.0.3'
repositories {
@@ -29,7 +29,7 @@ apply plugin: 'application'
mainClassName = "io.ktor.server.netty.DevelopmentEngine"
// Initialization order with shadow 2.0.1 and Gradle 4.3 is weird.
// Initialization order with shadow 2.0.1 and Gradle 6.8.2 is weird.
// See https://github.com/johnrengelman/shadow/issues/336#issuecomment-355402508
apply plugin: 'com.github.johnrengelman.shadow'
@@ -59,6 +59,7 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "io.ktor:ktor-metrics:$ktor_version"
compile "io.ktor:ktor-auth:$ktor_version"
compile "io.ktor:ktor-locations:$ktor_version"
compile "io.ktor:ktor-gson:$ktor_version"
compile "io.ktor:ktor-client-core:$ktor_version"

View File

@@ -18,12 +18,11 @@ import io.ktor.gson.GsonConverter
import io.ktor.http.ContentType
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Locations
import io.ktor.metrics.Metrics
import io.ktor.routing.Routing
import java.util.concurrent.TimeUnit
import io.ktor.util.KtorExperimentalAPI
import io.ktor.auth.Authentication
import io.ktor.auth.oauth
import io.ktor.metrics.dropwizard.DropwizardMetrics
import org.openapitools.server.infrastructure.ApiKeyCredential
import org.openapitools.server.infrastructure.ApiPrincipal
import org.openapitools.server.infrastructure.apiKeyAuth
@@ -32,23 +31,21 @@ import org.openapitools.server.apis.StoreApi
import org.openapitools.server.apis.UserApi
@KtorExperimentalAPI
internal val settings = HoconApplicationConfig(ConfigFactory.defaultApplication(HTTP::class.java.classLoader))
object HTTP {
val client = HttpClient(Apache)
}
@KtorExperimentalAPI
@KtorExperimentalLocationsAPI
fun Application.main() {
install(DefaultHeaders)
install(Metrics) {
install(DropwizardMetrics) {
val reporter = Slf4jReporter.forRegistry(registry)
.outputTo(log)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build()
.outputTo(log)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build()
reporter.start(10, TimeUnit.SECONDS)
}
install(ContentNegotiation) {
@@ -72,8 +69,8 @@ fun Application.main() {
client = HttpClient(Apache)
providerLookup = { ApplicationAuthProviders["petstore_auth"] }
urlProvider = { _ ->
// TODO: define a callback url here.
"/"
// TODO: define a callback url here.
"/"
}
}
}
@@ -84,8 +81,7 @@ fun Application.main() {
}
environment.monitor.subscribe(ApplicationStopping)
{
environment.monitor.subscribe(ApplicationStopping) {
HTTP.client.close()
}
}

View File

@@ -8,11 +8,7 @@ import io.ktor.features.deflate
import io.ktor.features.gzip
import io.ktor.features.minimumSize
import io.ktor.http.HttpMethod
import io.ktor.util.KtorExperimentalAPI
import java.time.Duration
import java.util.concurrent.Executors
import org.openapitools.server.settings
import java.util.concurrent.TimeUnit
/**
@@ -25,7 +21,7 @@ import org.openapitools.server.settings
*/
internal fun ApplicationHstsConfiguration(): HSTS.Configuration.() -> Unit {
return {
maxAge = Duration.ofDays(365)
maxAgeInSeconds = TimeUnit.DAYS.toSeconds(365)
includeSubDomains = true
preload = false
@@ -55,7 +51,6 @@ internal fun ApplicationCompressionConfiguration(): Compression.Configuration.()
}
// Defines authentication mechanisms used throughout the application.
@KtorExperimentalAPI
val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthServerSettings>(
OAuthServerSettings.OAuth2ServerSettings(
name = "petstore_auth",
@@ -77,6 +72,3 @@ val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthSer
// defaultScopes = listOf("public_profile")
// )
).associateBy { it.name }
// Provides an application-level fixed thread pool on which to execute coroutines (mainly)
internal val ApplicationExecutors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 4)

View File

@@ -13,8 +13,17 @@ package org.openapitools.server
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import org.openapitools.server.models.*
object Paths {
/**
* Add a new pet to the store
*
* @param body Pet object that needs to be added to the store
*/
@KtorExperimentalLocationsAPI
@Location("/pet") class addPet(val body: Pet)
/**
* Deletes a pet
*
@@ -30,7 +39,7 @@ object Paths {
* @param status Status values that need to be considered for filter
*/
@KtorExperimentalLocationsAPI
@Location("/pet/findByStatus") class findPetsByStatus(val status: kotlin.Array<kotlin.String>)
@Location("/pet/findByStatus") class findPetsByStatus(val status: kotlin.collections.List<kotlin.String>)
/**
* Finds Pets by tags
@@ -38,7 +47,7 @@ object Paths {
* @param tags Tags to filter by
*/
@KtorExperimentalLocationsAPI
@Location("/pet/findByTags") class findPetsByTags(val tags: kotlin.Array<kotlin.String>)
@Location("/pet/findByTags") class findPetsByTags(val tags: kotlin.collections.List<kotlin.String>)
/**
* Find pet by ID
@@ -48,6 +57,34 @@ object Paths {
@KtorExperimentalLocationsAPI
@Location("/pet/{petId}") class getPetById(val petId: kotlin.Long)
/**
* Update an existing pet
*
* @param body Pet object that needs to be added to the store
*/
@KtorExperimentalLocationsAPI
@Location("/pet") class updatePet(val body: Pet)
/**
* Updates a pet in the store with form data
*
* @param petId ID of pet that needs to be updated
* @param name Updated name of the pet (optional)
* @param status Updated status of the pet (optional)
*/
@KtorExperimentalLocationsAPI
@Location("/pet/{petId}") class updatePetWithForm(val petId: kotlin.Long, val name: kotlin.String? = null, val status: kotlin.String? = null)
/**
* uploads an image
*
* @param petId ID of pet to update
* @param additionalMetadata Additional data to pass to server (optional)
* @param file file to upload (optional)
*/
@KtorExperimentalLocationsAPI
@Location("/pet/{petId}/uploadImage") class uploadFile(val petId: kotlin.Long, val additionalMetadata: kotlin.String? = null, val file: java.io.File? = null)
/**
* Delete purchase order by ID
* For valid response try integer IDs with value &lt; 1000. Anything above 1000 or nonintegers will generate API errors
@@ -71,6 +108,38 @@ object Paths {
@KtorExperimentalLocationsAPI
@Location("/store/order/{orderId}") class getOrderById(val orderId: kotlin.Long)
/**
* Place an order for a pet
*
* @param body order placed for purchasing the pet
*/
@KtorExperimentalLocationsAPI
@Location("/store/order") class placeOrder(val body: Order)
/**
* Create user
* This can only be done by the logged in user.
* @param body Created user object
*/
@KtorExperimentalLocationsAPI
@Location("/user") class createUser(val body: User)
/**
* Creates list of users with given input array
*
* @param body List of user object
*/
@KtorExperimentalLocationsAPI
@Location("/user/createWithArray") class createUsersWithArrayInput(val body: kotlin.collections.List<User>)
/**
* Creates list of users with given input array
*
* @param body List of user object
*/
@KtorExperimentalLocationsAPI
@Location("/user/createWithList") class createUsersWithListInput(val body: kotlin.collections.List<User>)
/**
* Delete user
* This can only be done by the logged in user.
@@ -103,4 +172,13 @@ object Paths {
@KtorExperimentalLocationsAPI
@Location("/user/logout") class logoutUser()
/**
* Updated user
* This can only be done by the logged in user.
* @param username name that need to be deleted
* @param body Updated user object
*/
@KtorExperimentalLocationsAPI
@Location("/user/{username}") class updateUser(val username: kotlin.String, val body: User)
}

View File

@@ -20,17 +20,18 @@ import io.ktor.auth.OAuthAccessTokenResponse
import io.ktor.auth.OAuthServerSettings
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.delete
import io.ktor.locations.get
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.Route
import io.ktor.routing.post
import io.ktor.routing.put
import io.ktor.routing.route
import org.openapitools.server.Paths
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.delete
import io.ktor.locations.get
import io.ktor.locations.post
import io.ktor.locations.put
import io.ktor.locations.options
import io.ktor.locations.head
import org.openapitools.server.infrastructure.ApiPrincipal
@@ -41,40 +42,27 @@ import org.openapitools.server.models.Pet
fun Route.PetApi() {
val gson = Gson()
val empty = mutableMapOf<String, Any?>()
route("/pet") {
authenticate("petstore_auth") {
post {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
call.respond(HttpStatusCode.NotImplemented)
}
}
}
authenticate("petstore_auth") {
post<Paths.addPet> {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()!!
call.respond(HttpStatusCode.NotImplemented)
}
}
delete<Paths.deletePet> { _: Paths.deletePet ->
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
authenticate("petstore_auth") {
delete<Paths.deletePet> {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()!!
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
call.respond(HttpStatusCode.NotImplemented)
}
call.respond(HttpStatusCode.NotImplemented)
}
}
get<Paths.findPetsByStatus> { _: Paths.findPetsByStatus ->
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
authenticate("petstore_auth") {
get<Paths.findPetsByStatus> {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()!!
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
val exampleContentType = "application/json"
val exampleContentType = "application/json"
val exampleContentString = """{
"photoUrls" : [ "photoUrls", "photoUrls" ],
"name" : "doggie",
@@ -93,22 +81,19 @@ fun Route.PetApi() {
"status" : "available"
}"""
when(exampleContentType) {
when (exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
}
}
get<Paths.findPetsByTags> { _: Paths.findPetsByTags ->
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
authenticate("petstore_auth") {
get<Paths.findPetsByTags> {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()!!
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
val exampleContentType = "application/json"
val exampleContentType = "application/json"
val exampleContentString = """{
"photoUrls" : [ "photoUrls", "photoUrls" ],
"name" : "doggie",
@@ -127,22 +112,19 @@ fun Route.PetApi() {
"status" : "available"
}"""
when(exampleContentType) {
when (exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
}
}
get<Paths.getPetById> { _: Paths.getPetById ->
val principal = call.authentication.principal<ApiPrincipal>()
authenticate("api_key") {
get<Paths.getPetById> {
val principal = call.authentication.principal<ApiPrincipal>()!!
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
val exampleContentType = "application/json"
val exampleContentType = "application/json"
val exampleContentString = """{
"photoUrls" : [ "photoUrls", "photoUrls" ],
"name" : "doggie",
@@ -161,68 +143,47 @@ fun Route.PetApi() {
"status" : "available"
}"""
when(exampleContentType) {
when (exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
}
}
route("/pet") {
authenticate("petstore_auth") {
put {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
call.respond(HttpStatusCode.NotImplemented)
}
}
}
authenticate("petstore_auth") {
put<Paths.updatePet> {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()!!
call.respond(HttpStatusCode.NotImplemented)
}
}
route("/pet/{petId}") {
authenticate("petstore_auth") {
post {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
call.respond(HttpStatusCode.NotImplemented)
}
}
}
authenticate("petstore_auth") {
post<Paths.updatePetWithForm> {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()!!
call.respond(HttpStatusCode.NotImplemented)
}
}
route("/pet/{petId}/uploadImage") {
authenticate("petstore_auth") {
post {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
authenticate("petstore_auth") {
post<Paths.uploadFile> {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()!!
val exampleContentType = "application/json"
val exampleContentString = """{
"code" : 0,
"type" : "type",
"message" : "message"
}"""
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
val exampleContentType = "application/json"
val exampleContentString = """{
"code" : 0,
"type" : "type",
"message" : "message"
}"""
when(exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
}
when (exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
}
}

View File

@@ -20,17 +20,18 @@ import io.ktor.auth.OAuthAccessTokenResponse
import io.ktor.auth.OAuthServerSettings
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.delete
import io.ktor.locations.get
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.Route
import io.ktor.routing.post
import io.ktor.routing.put
import io.ktor.routing.route
import org.openapitools.server.Paths
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.delete
import io.ktor.locations.get
import io.ktor.locations.post
import io.ktor.locations.put
import io.ktor.locations.options
import io.ktor.locations.head
import org.openapitools.server.infrastructure.ApiPrincipal
@@ -40,24 +41,19 @@ import org.openapitools.server.models.Order
fun Route.StoreApi() {
val gson = Gson()
val empty = mutableMapOf<String, Any?>()
delete<Paths.deleteOrder> { _: Paths.deleteOrder ->
delete<Paths.deleteOrder> {
call.respond(HttpStatusCode.NotImplemented)
}
get<Paths.getInventory> { _: Paths.getInventory ->
val principal = call.authentication.principal<ApiPrincipal>()
authenticate("api_key") {
get<Paths.getInventory> {
val principal = call.authentication.principal<ApiPrincipal>()!!
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
call.respond(HttpStatusCode.NotImplemented)
}
call.respond(HttpStatusCode.NotImplemented)
}
}
get<Paths.getOrderById> { _: Paths.getOrderById ->
get<Paths.getOrderById> {
val exampleContentType = "application/json"
val exampleContentString = """{
"petId" : 6,
@@ -68,31 +64,28 @@ fun Route.StoreApi() {
"status" : "placed"
}"""
when(exampleContentType) {
when (exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
route("/store/order") {
post {
val exampleContentType = "application/json"
val exampleContentString = """{
"petId" : 6,
"quantity" : 1,
"id" : 0,
"shipDate" : "2000-01-23T04:56:07.000+00:00",
"complete" : false,
"status" : "placed"
}"""
when(exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
post<Paths.placeOrder> {
val exampleContentType = "application/json"
val exampleContentString = """{
"petId" : 6,
"quantity" : 1,
"id" : 0,
"shipDate" : "2000-01-23T04:56:07.000+00:00",
"complete" : false,
"status" : "placed"
}"""
when (exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}

View File

@@ -20,17 +20,18 @@ import io.ktor.auth.OAuthAccessTokenResponse
import io.ktor.auth.OAuthServerSettings
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.delete
import io.ktor.locations.get
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.Route
import io.ktor.routing.post
import io.ktor.routing.put
import io.ktor.routing.route
import org.openapitools.server.Paths
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.delete
import io.ktor.locations.get
import io.ktor.locations.post
import io.ktor.locations.put
import io.ktor.locations.options
import io.ktor.locations.head
import org.openapitools.server.infrastructure.ApiPrincipal
@@ -40,34 +41,23 @@ import org.openapitools.server.models.User
fun Route.UserApi() {
val gson = Gson()
val empty = mutableMapOf<String, Any?>()
route("/user") {
post {
call.respond(HttpStatusCode.NotImplemented)
}
}
route("/user/createWithArray") {
post {
call.respond(HttpStatusCode.NotImplemented)
}
}
route("/user/createWithList") {
post {
call.respond(HttpStatusCode.NotImplemented)
}
}
delete<Paths.deleteUser> { _: Paths.deleteUser ->
post<Paths.createUser> {
call.respond(HttpStatusCode.NotImplemented)
}
post<Paths.createUsersWithArrayInput> {
call.respond(HttpStatusCode.NotImplemented)
}
get<Paths.getUserByName> { _: Paths.getUserByName ->
post<Paths.createUsersWithListInput> {
call.respond(HttpStatusCode.NotImplemented)
}
delete<Paths.deleteUser> {
call.respond(HttpStatusCode.NotImplemented)
}
get<Paths.getUserByName> {
val exampleContentType = "application/json"
val exampleContentString = """{
"firstName" : "firstName",
@@ -80,28 +70,23 @@ fun Route.UserApi() {
"username" : "username"
}"""
when(exampleContentType) {
when (exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
get<Paths.loginUser> { _: Paths.loginUser ->
get<Paths.loginUser> {
call.respond(HttpStatusCode.NotImplemented)
}
get<Paths.logoutUser> { _: Paths.logoutUser ->
get<Paths.logoutUser> {
call.respond(HttpStatusCode.NotImplemented)
}
route("/user/{username}") {
put {
call.respond(HttpStatusCode.NotImplemented)
}
put<Paths.updateUser> {
call.respond(HttpStatusCode.NotImplemented)
}
}

View File

@@ -1,85 +1,105 @@
package org.openapitools.server.infrastructure
import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.auth.Authentication
import io.ktor.auth.AuthenticationFailedCause
import io.ktor.auth.AuthenticationPipeline
import io.ktor.auth.AuthenticationProvider
import io.ktor.auth.Credential
import io.ktor.auth.Principal
import io.ktor.auth.UnauthorizedResponse
import io.ktor.http.auth.HeaderValueEncoding
import io.ktor.http.auth.HttpAuthHeader
import io.ktor.request.ApplicationRequest
import io.ktor.response.respond
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.http.auth.*
import io.ktor.request.*
import io.ktor.response.*
enum class ApiKeyLocation(val location: String) {
QUERY("query"),
HEADER("header")
}
data class ApiKeyCredential(val value: String): Credential
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
*/
class ApiKeyAuthenticationProvider(name: String?) : AuthenticationProvider(name) {
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = { null }
class ApiKeyAuthenticationProvider(configuration: Configuration) : AuthenticationProvider(configuration) {
var apiKeyName: String = "";
private val authenticationFunction = configuration.authenticationFunction
var apiKeyLocation: ApiKeyLocation = ApiKeyLocation.QUERY;
private val apiKeyName: String = configuration.apiKeyName
/**
* Sets a validation function that will check given [ApiKeyCredential] instance and return [Principal],
* or null if credential does not correspond to an authenticated principal
*/
fun validate(body: suspend ApplicationCall.(ApiKeyCredential) -> Principal?) {
authenticationFunction = body
}
}
private val apiKeyLocation: ApiKeyLocation = configuration.apiKeyLocation
fun Authentication.Configuration.apiKeyAuth(name: String? = null, configure: ApiKeyAuthenticationProvider.() -> Unit) {
val provider = ApiKeyAuthenticationProvider(name).apply(configure)
val apiKeyName = provider.apiKeyName
val apiKeyLocation = provider.apiKeyLocation
val authenticate = provider.authenticationFunction
internal fun install() {
pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
val principal = credentials?.let { authenticationFunction(call, it) }
provider.pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
val principal = credentials?.let { authenticate(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
)
)
)
it.complete()
}
}
if (cause != null) {
context.challenge(apiKeyName, cause) {
// TODO: Verify correct response structure here.
call.respond(UnauthorizedResponse(HttpAuthHeader.Parameterized("API_KEY", mapOf("key" to apiKeyName), HeaderValueEncoding.QUOTED_ALWAYS)))
it.complete()
if (principal != null) {
context.principal(principal)
}
}
}
if (principal != null) {
context.principal(principal)
class Configuration internal constructor(name: String?) : AuthenticationProvider.Configuration(name) {
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = {
throw NotImplementedError(
"Api Key auth validate function is not specified. Use apiKeyAuth { validate { ... } } to fix."
)
}
var apiKeyName: String = ""
var apiKeyLocation: ApiKeyLocation = ApiKeyLocation.QUERY
/**
* Sets a validation function that will check given [ApiKeyCredential] instance and return [Principal],
* or null if credential does not correspond to an authenticated principal
*/
fun validate(body: suspend ApplicationCall.(ApiKeyCredential) -> Principal?) {
authenticationFunction = body
}
}
}
fun ApplicationRequest.apiKeyAuthenticationCredentials(apiKeyName: String, apiKeyLocation: ApiKeyLocation): ApiKeyCredential? {
val value: String? = when(apiKeyLocation) {
fun Authentication.Configuration.apiKeyAuth(
name: String? = null,
configure: ApiKeyAuthenticationProvider.Configuration.() -> Unit
) {
val configuration = ApiKeyAuthenticationProvider.Configuration(name).apply(configure)
val provider = ApiKeyAuthenticationProvider(configuration)
provider.install()
register(provider)
}
fun ApplicationRequest.apiKeyAuthenticationCredentials(
apiKeyName: String,
apiKeyLocation: ApiKeyLocation
): ApiKeyCredential? {
val value: String? = when (apiKeyLocation) {
ApiKeyLocation.QUERY -> this.queryParameters[apiKeyName]
ApiKeyLocation.HEADER -> this.headers[apiKeyName]
}
when (value) {
null -> return null
else -> return ApiKeyCredential(value)
return when (value) {
null -> null
else -> ApiKeyCredential(value)
}
}

View File

@@ -19,7 +19,7 @@ import java.io.Serializable
* @param type
* @param message
*/
data class ApiResponse (
data class ApiResponse(
val code: kotlin.Int? = null,
val type: kotlin.String? = null,
val message: kotlin.String? = null

View File

@@ -18,7 +18,7 @@ import java.io.Serializable
* @param id
* @param name
*/
data class Category (
data class Category(
val id: kotlin.Long? = null,
val name: kotlin.String? = null
) : Serializable

View File

@@ -22,7 +22,7 @@ import java.io.Serializable
* @param status Order Status
* @param complete
*/
data class Order (
data class Order(
val id: kotlin.Long? = null,
val petId: kotlin.Long? = null,
val quantity: kotlin.Int? = null,

View File

@@ -24,12 +24,12 @@ import java.io.Serializable
* @param tags
* @param status pet status in the store
*/
data class Pet (
data class Pet(
val name: kotlin.String,
val photoUrls: kotlin.Array<kotlin.String>,
val photoUrls: kotlin.collections.List<kotlin.String>,
val id: kotlin.Long? = null,
val category: Category? = null,
val tags: kotlin.Array<Tag>? = null,
val tags: kotlin.collections.List<Tag>? = null,
/* pet status in the store */
val status: Pet.Status? = null
) : Serializable

View File

@@ -18,7 +18,7 @@ import java.io.Serializable
* @param id
* @param name
*/
data class Tag (
data class Tag(
val id: kotlin.Long? = null,
val name: kotlin.String? = null
) : Serializable

View File

@@ -24,7 +24,7 @@ import java.io.Serializable
* @param phone
* @param userStatus User Status
*/
data class User (
data class User(
val id: kotlin.Long? = null,
val username: kotlin.String? = null,
val firstName: kotlin.String? = null,