diff --git a/.github/workflows/samples-kotlin-server-jdk17.yaml b/.github/workflows/samples-kotlin-server-jdk17.yaml index b66723da4b1d..3cda9e23224a 100644 --- a/.github/workflows/samples-kotlin-server-jdk17.yaml +++ b/.github/workflows/samples-kotlin-server-jdk17.yaml @@ -41,6 +41,7 @@ jobs: - samples/server/petstore/kotlin-server/javalin - samples/server/petstore/kotlin-server/javalin-6 - samples/server/petstore/kotlin-server/ktor + - samples/server/petstore/kotlin-server/ktor2 # comment out due to gradle build failure # - samples/server/petstore/kotlin-spring-default/ steps: diff --git a/.github/workflows/samples-kotlin-server-jdk21.yaml b/.github/workflows/samples-kotlin-server-jdk21.yaml index 0de80197cb7c..cb9134a95b9d 100644 --- a/.github/workflows/samples-kotlin-server-jdk21.yaml +++ b/.github/workflows/samples-kotlin-server-jdk21.yaml @@ -23,6 +23,7 @@ jobs: sample: - samples/server/petstore/kotlin-server/javalin-6 - samples/server/petstore/kotlin-server/ktor + - samples/server/petstore/kotlin-server/ktor2 - samples/server/petstore/kotlin-server-required-and-nullable-properties steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/samples-kotlin-server.yaml b/.github/workflows/samples-kotlin-server.yaml index f46719f46b4f..7721fbd44a36 100644 --- a/.github/workflows/samples-kotlin-server.yaml +++ b/.github/workflows/samples-kotlin-server.yaml @@ -38,6 +38,7 @@ jobs: - samples/server/petstore/kotlin-springboot-source-swagger2 - samples/server/petstore/kotlin-springboot-springfox - samples/server/petstore/kotlin-server/ktor + - samples/server/petstore/kotlin-server/ktor2 - samples/server/petstore/kotlin-server/jaxrs-spec - samples/server/petstore/kotlin-server/jaxrs-spec-mutiny - samples/server/petstore/kotlin-server-modelMutable diff --git a/bin/configs/kotlin-server-ktor2.yaml b/bin/configs/kotlin-server-ktor2.yaml new file mode 100644 index 000000000000..240bb5bc6d9a --- /dev/null +++ b/bin/configs/kotlin-server-ktor2.yaml @@ -0,0 +1,8 @@ +generatorName: kotlin-server +outputDir: samples/server/petstore/kotlin-server/ktor2 +library: ktor2 +inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml +templateDir: modules/openapi-generator/src/main/resources/kotlin-server +additionalProperties: + hideGenerationTimestamp: "true" + serializableModel: "true" diff --git a/docs/generators/kotlin-server.md b/docs/generators/kotlin-server.md index 9e8c7c2291df..733e819e6ce3 100644 --- a/docs/generators/kotlin-server.md +++ b/docs/generators/kotlin-server.md @@ -32,7 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |featureResources|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| |interfaceOnly|Whether to generate only API interface stubs without the server files. This option is currently supported only when using jaxrs-spec library.| |false| -|library|library template (sub-template)|
**ktor**
ktor framework
**jaxrs-spec**
JAX-RS spec only
**javalin5**
Javalin 5
**javalin6**
Javalin 6
|ktor| +|library|library template (sub-template)|
**ktor**
ktor framework
**ktor2**
ktor (2.x) framework
**jaxrs-spec**
JAX-RS spec only
**javalin5**
Javalin 5
**javalin6**
Javalin 6
|ktor| |modelMutable|Create mutable models| |false| |omitGradleWrapper|Whether to omit Gradle wrapper for creating a sub project.| |false| |packageName|Generated artifact package name.| |org.openapitools.server| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinServerCodegen.java index 2fc9871b9f59..65a49682025d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinServerCodegen.java @@ -73,6 +73,16 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen implements BeanVa Constants.METRICS, Constants.OMIT_GRADLE_WRAPPER )) + .put(Constants.KTOR2, Arrays.asList( + Constants.AUTOMATIC_HEAD_REQUESTS, + Constants.CONDITIONAL_HEADERS, + Constants.HSTS, + Constants.CORS, + Constants.COMPRESSION, + Constants.RESOURCES, + Constants.METRICS, + Constants.OMIT_GRADLE_WRAPPER + )) .put(Constants.JAXRS_SPEC, Arrays.asList( USE_BEANVALIDATION, Constants.USE_COROUTINES, @@ -127,6 +137,7 @@ public KotlinServerCodegen() { modelPackage = packageName + ".models"; supportedLibraries.put(Constants.KTOR, "ktor framework"); + supportedLibraries.put(Constants.KTOR2, "ktor (2.x) framework"); supportedLibraries.put(Constants.JAXRS_SPEC, "JAX-RS spec only"); supportedLibraries.put(Constants.JAVALIN5, "Javalin 5"); supportedLibraries.put(Constants.JAVALIN6, "Javalin 6"); @@ -323,6 +334,7 @@ public void setUseBeanValidation(boolean useBeanValidation) { public static class Constants { public final static String KTOR = "ktor"; + public final static String KTOR2 = "ktor2"; public final static String JAXRS_SPEC = "jaxrs-spec"; public final static String JAVALIN5 = "javalin5"; @@ -419,6 +431,6 @@ private boolean isJavalin() { } private boolean isKtor() { - return Constants.KTOR.equals(library); + return Constants.KTOR.equals(library) || Constants.KTOR2.equals(library); } } diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/ApiKeyAuth.kt.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/ApiKeyAuth.kt.mustache new file mode 100644 index 000000000000..c2cad7fe7a7f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/ApiKeyAuth.kt.mustache @@ -0,0 +1,102 @@ +package org.openapitools.server.infrastructure + +import io.ktor.http.auth.* +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"), + HEADER("header") +} + +data class ApiKeyCredential(val value: String) : Credential +data class ApiPrincipal(val apiKeyCredential: ApiKeyCredential?) : Principal + +/** +* Represents an Api Key authentication provider +*/ +class ApiKeyAuthenticationProvider(configuration: Configuration) : AuthenticationProvider(configuration) { + + private val authenticationFunction = configuration.authenticationFunction + + private val apiKeyName: String = configuration.apiKeyName + + private val apiKeyLocation: ApiKeyLocation = configuration.apiKeyLocation + + 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 + } + + if (cause != null) { + context.challenge(apiKeyName, cause) { challenge, call -> + call.respond( + UnauthorizedResponse( + HttpAuthHeader.Parameterized( + "API_KEY", + mapOf("key" to apiKeyName), + HeaderValueEncoding.QUOTED_ALWAYS + ) + ) + ) + challenge.complete() + } + } + + if (principal != null) { + context.principal(principal) + } + } + + class Configuration internal constructor(name: String?) : Config(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 AuthenticationConfig.apiKeyAuth( + name: String? = null, + configure: ApiKeyAuthenticationProvider.Configuration.() -> Unit +) { + val configuration = ApiKeyAuthenticationProvider.Configuration(name).apply(configure) + val provider = ApiKeyAuthenticationProvider(configuration) + 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] + } + return when (value) { + null -> null + else -> ApiKeyCredential(value) + } +} diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/AppMain.kt.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/AppMain.kt.mustache new file mode 100644 index 000000000000..5ba3775c92cf --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/AppMain.kt.mustache @@ -0,0 +1,139 @@ +package {{packageName}} + +import io.ktor.server.application.* +import io.ktor.serialization.gson.* +import io.ktor.http.* +{{#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 com.codahale.metrics.Slf4jReporter +import io.ktor.server.metrics.dropwizard.* +import java.util.concurrent.TimeUnit +{{/featureMetrics}} +import io.ktor.server.routing.* +{{#hasAuthMethods}} +import com.typesafe.config.ConfigFactory +import io.ktor.client.HttpClient +import io.ktor.client.engine.apache.Apache +import io.ktor.server.config.HoconApplicationConfig +import io.ktor.server.auth.* +import org.openapitools.server.infrastructure.* +{{/hasAuthMethods}} +{{#generateApis}}{{#apiInfo}}{{#apis}}import {{apiPackage}}.{{classname}} +{{/apis}}{{/apiInfo}}{{/generateApis}} + +{{#hasAuthMethods}} +internal val settings = HoconApplicationConfig(ConfigFactory.defaultApplication(HTTP::class.java.classLoader)) + +object HTTP { + val client = HttpClient(Apache) +} +{{/hasAuthMethods}} + +fun Application.main() { + install(DefaultHeaders) + {{#featureMetrics}} + install(DropwizardMetrics) { + val reporter = Slf4jReporter.forRegistry(registry) + .outputTo(this@main.log) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .build() + reporter.start(10, TimeUnit.SECONDS) + } + {{/featureMetrics}} +{{#generateApis}} + install(ContentNegotiation) { + register(ContentType.Application.Json, GsonConverter()) + } + {{#featureAutoHead}} + install(AutoHeadResponse) // see https://ktor.io/docs/autoheadresponse.html + {{/featureAutoHead}} + {{#featureConditionalHeaders}} + install(ConditionalHeaders) // see https://ktor.io/docs/conditional-headers.html + {{/featureConditionalHeaders}} + {{#featureCompression}} + install(Compression, ApplicationCompressionConfiguration()) // see https://ktor.io/docs/compression.html + {{/featureCompression}} + {{#featureCORS}} + install(CORS, ApplicationCORSConfiguration()) // see https://ktor.io/docs/cors.html + {{/featureCORS}} + {{#featureHSTS}} + install(HSTS, ApplicationHstsConfiguration()) // see https://ktor.io/docs/hsts.html + {{/featureHSTS}} + {{#featureResources}} + install(Resources) + {{/featureResources}} + {{#hasAuthMethods}} + install(Authentication) { + {{#authMethods}} + {{#isBasicBasic}} + basic("{{{name}}}") { + validate { credentials -> + // TODO: "Apply your basic authentication functionality." + // Accessible in-method via call.principal() + if (credentials.name == "Swagger" && "Codegen" == credentials.password) { + UserIdPrincipal(credentials.name) + } else { + null + } + } + {{/isBasicBasic}} + {{#isApiKey}} + // "Implement API key auth ({{{name}}}) for parameter name '{{{keyParamName}}}'." + apiKeyAuth("{{{name}}}") { + validate { apikeyCredential: ApiKeyCredential -> + when { + apikeyCredential.value == "keyboardcat" -> ApiPrincipal(apikeyCredential) + else -> null + } + } + } + {{/isApiKey}} + {{#isOAuth}} + {{#bodyAllowed}} + {{/bodyAllowed}} + {{^bodyAllowed}} + oauth("{{name}}") { + client = HttpClient(Apache) + providerLookup = { applicationAuthProvider(this@main.environment.config) } + urlProvider = { _ -> + // TODO: define a callback url here. + "/" + } + } + {{/bodyAllowed}} + {{/isOAuth}} + {{/authMethods}} + } + {{/hasAuthMethods}} + install(Routing) { + {{#apiInfo}} + {{#apis}} + {{#operations}} + {{classname}}() + {{/operations}} + {{/apis}} + {{/apiInfo}} + } + +{{/generateApis}} +} diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/Configuration.kt.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/Configuration.kt.mustache new file mode 100644 index 000000000000..0c81bee193ef --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/Configuration.kt.mustache @@ -0,0 +1,98 @@ +package {{packageName}} + +// Use this file to hold package-level internal functions that return receiver object passed to the `install` method. +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. + * + * See http://ktor.io/features/cors.html + */ +internal fun ApplicationCORSConfiguration(): CORSConfig.() -> Unit { + return { + // method(HttpMethod.Options) + // header(HttpHeaders.XForwardedProto) + // anyHost() + // host("my-host") + // host("my-host:80") + // host("my-host", subDomains = listOf("www")) + // host("my-host", schemes = listOf("http", "https")) + // allowCredentials = true + // maxAge = Duration.ofDays(1) + } +} +{{/featureCORS}} +{{#featureHSTS}} + +/** + * Application block for [HSTS] configuration. + * + * This file may be excluded in .openapi-generator-ignore, + * and application-specific configuration can be applied in this function. + * + * See http://ktor.io/features/hsts.html + */ +internal fun ApplicationHstsConfiguration(): HSTSConfig.() -> Unit { + return { + maxAgeInSeconds = TimeUnit.DAYS.toSeconds(365) + includeSubDomains = true + preload = false + + // You may also apply any custom directives supported by specific user-agent. For example: + // customDirectives.put("redirectHttpToHttps", "false") + } +} +{{/featureHSTS}} +{{#featureCompression}} + +/** + * Application block for [Compression] configuration. + * + * This file may be excluded in .openapi-generator-ignore, + * and application-specific configuration can be applied in this function. + * + * See http://ktor.io/features/compression.html + */ +internal fun ApplicationCompressionConfiguration(): CompressionConfig.() -> Unit { + return { + gzip { + priority = 1.0 + } + deflate { + priority = 10.0 + minimumSize(1024) // condition + } + } +} +{{/featureCompression}} + +// Defines authentication mechanisms used throughout the application. +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") + ) diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/Dockerfile.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/Dockerfile.mustache new file mode 100644 index 000000000000..b9158ac26e39 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/Dockerfile.mustache @@ -0,0 +1,7 @@ +FROM openjdk:8-jre-alpine + +COPY ./build/libs/{{artifactId}}.jar /root/{{artifactId}}.jar + +WORKDIR /root + +CMD ["java", "-server", "-Xms4g", "-Xmx4g", "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=100", "-XX:+UseStringDeduplication", "-jar", "{{artifactId}}.jar"] \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/Paths.kt.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/Paths.kt.mustache new file mode 100644 index 000000000000..a575ea678772 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/Paths.kt.mustache @@ -0,0 +1,31 @@ +{{>licenseInfo}} +package {{packageName}} + +import io.ktor.resources.* +import kotlinx.serialization.* +import {{packageName}}.models.* +{{#imports}}import {{import}} +{{/imports}} + +{{#apiInfo}} +object Paths { +{{#apis}} +{{#operations}} + {{#operation}} + /**{{#summary}} + * {{.}}{{/summary}} + * {{unescapedNotes}} + {{#allParams}}* @param {{paramName}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + {{/allParams}}*/ + {{#hasParams}} + @Serializable @Resource("{{{path}}}") class {{operationId}}({{#allParams}}val {{paramName}}: {{{dataType}}}{{^required}}? = null{{/required}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) + {{/hasParams}} + {{^hasParams}} + @Serializable @Resource("{{{path}}}") class {{operationId}} + {{/hasParams}} + + {{/operation}} +{{/operations}} +{{/apis}} +} +{{/apiInfo}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/README.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/README.mustache new file mode 100644 index 000000000000..24333d99ccbd --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/README.mustache @@ -0,0 +1,99 @@ +# {{packageName}} - Kotlin Server library for {{appName}} + +{{#unescapedAppDescription}} +{{.}} +{{/unescapedAppDescription}} + +Generated by OpenAPI Generator {{generatorVersion}}{{^hideGenerationTimestamp}} ({{generatedDate}}){{/hideGenerationTimestamp}}. + +## Requires + +* Kotlin 2.0.20 +* Gradle 8.10.2 + +## Build + +First, create the gradle wrapper script: + +``` +gradle wrapper +``` + +Then, run: + +``` +./gradlew check assemble +``` + +This runs all tests and packages the library. + +## Running + +The server builds as a fat jar with a main entrypoint. To start the service, run `java -jar ./build/libs/{{artifactId}}.jar`. + +You may also run in docker: + +``` +docker build -t {{artifactId}} . +docker run -p 8080:8080 {{artifactId}} +``` + +## Features/Implementation Notes + +* Supports JSON inputs/outputs, File inputs, and Form inputs (see ktor documentation for more info). +* ~Supports collection formats for query parameters: csv, tsv, ssv, pipes.~ +* Some Kotlin and Java types are fully qualified to avoid conflicts with types defined in OpenAPI definitions. + +{{#generateApiDocs}} + +## Documentation for API Endpoints + +All URIs are relative to *{{{basePath}}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{{summary}}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +{{/generateApiDocs}} + +{{#generateModelDocs}} + +## Documentation for Models + +{{#modelPackage}} +{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) +{{/model}}{{/models}} +{{/modelPackage}} +{{^modelPackage}} +No model defined in this package +{{/modelPackage}} +{{/generateModelDocs}} + + +## Documentation for Authorization + +{{^authMethods}}Endpoints do not require authorization.{{/authMethods}} +{{#hasAuthMethods}}Authentication schemes defined for the API:{{/hasAuthMethods}} +{{#authMethods}} + +### {{name}} + +{{#isApiKey}}- **Type**: API key +- **API key parameter name**: {{keyParamName}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} +{{/isApiKey}} +{{#isBasicBasic}}- **Type**: HTTP basic authentication +{{/isBasicBasic}} +{{#isBasicBearer}}- **Type**: HTTP Bearer Token authentication{{#bearerFormat}} ({{{.}}}){{/bearerFormat}} +{{/isBasicBearer}} +{{#isHttpSignature}}- **Type**: HTTP signature authentication +{{/isHttpSignature}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - {{scope}}: {{description}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/_api_body.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/_api_body.mustache new file mode 100644 index 000000000000..b29afdb0adf2 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/_api_body.mustache @@ -0,0 +1,21 @@ +{{#hasAuthMethods}} +{{>libraries/ktor/_principal}} +{{#examples}} +{{#-first}} +{{#lambda.indented}}{{>_response}}{{/lambda.indented}} +{{/-first}} +{{/examples}} +{{^examples}} +call.respond(HttpStatusCode.NotImplemented) +{{/examples}} +{{/hasAuthMethods}} +{{^hasAuthMethods}} +{{#examples}} +{{#-first}} +{{>libraries/ktor/_response}} +{{/-first}} +{{/examples}} +{{^examples}} +call.respond(HttpStatusCode.NotImplemented) +{{/examples}} +{{/hasAuthMethods}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/_principal.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/_principal.mustache new file mode 100644 index 000000000000..17c32a4c40a2 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/_principal.mustache @@ -0,0 +1,11 @@ +{{#authMethods}} +{{#isBasicBasic}} +val principal = call.authentication.principal()!! +{{/isBasicBasic}}{{^isBasicBasic}}{{#isApiKey}} +val principal = call.authentication.principal()!! +{{/isApiKey}}{{^isApiKey}}{{#isOAuth}} +val principal = call.authentication.principal()!! +{{/isOAuth}}{{^isOAuth}} +val principal = null!! +{{/isOAuth}}{{/isApiKey}}{{/isBasicBasic}} +{{/authMethods}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/_response.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/_response.mustache new file mode 100644 index 000000000000..931dad98f5e1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/_response.mustache @@ -0,0 +1,8 @@ +val exampleContentType = "{{{contentType}}}" +val exampleContentString = """{{&example}}""" + +when (exampleContentType) { + "application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java)) + "application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml) + else -> call.respondText(exampleContentString) +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/api.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/api.mustache new file mode 100644 index 000000000000..eb1da3bd527d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/api.mustache @@ -0,0 +1,53 @@ +{{>licenseInfo}} +package {{apiPackage}} + +import com.google.gson.Gson +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.auth.* +import io.ktor.server.response.* +{{#featureResources}} +import {{packageName}}.Paths +import io.ktor.server.resources.options +import io.ktor.server.resources.get +import io.ktor.server.resources.post +import io.ktor.server.resources.put +import io.ktor.server.resources.delete +import io.ktor.server.resources.head +import io.ktor.server.resources.patch +{{/featureResources}} +import io.ktor.server.routing.* +import {{packageName}}.infrastructure.ApiPrincipal +{{#imports}}import {{import}} +{{/imports}} + +{{#operations}} +fun Route.{{classname}}() { + val gson = Gson() + val empty = mutableMapOf() + +{{#operation}} + {{#hasAuthMethods}} + {{#authMethods}} + authenticate("{{{name}}}") { + {{/authMethods}} + {{/hasAuthMethods}} + {{^featureResources}} + route("{{path}}") { + {{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}} { + {{#lambda.indented_12}}{{>libraries/ktor/_api_body}}{{/lambda.indented_12}} + } + } + {{/featureResources}} + {{#featureResources}} + {{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}} { + {{#lambda.indented_8}}{{>libraries/ktor/_api_body}}{{/lambda.indented_8}} + } + {{/featureResources}} + {{#hasAuthMethods}} + } + {{/hasAuthMethods}} + +{{/operation}} +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/application.conf.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/application.conf.mustache new file mode 100644 index 000000000000..032be42fa11d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/application.conf.mustache @@ -0,0 +1,27 @@ +ktor { + deployment { + environment = development + port = 8080 + autoreload = true + watch = [ {{packageName}} ] + } + + application { + modules = [ {{packageName}}.AppMainKt.main ] + } +} + +# Typesafe config allows multiple ways to provide configuration values without hard-coding them here. +# Please see https://github.com/lightbend/config for details. +auth { + oauth { +{{#authMethods}} +{{#isOAuth}} + {{name}} { + clientId = "" + clientSecret = "" + } +{{/isOAuth}} +{{/authMethods}} + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/build.gradle.kts.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/build.gradle.kts.mustache new file mode 100644 index 000000000000..3dc3dd38f043 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/build.gradle.kts.mustache @@ -0,0 +1,61 @@ + +val kotlin_version: String by project +val logback_version: String by project + +group = "{{groupId}}" +version = "{{artifactVersion}}" + +plugins { + kotlin("jvm") version "2.0.20" + id("io.ktor.plugin") version "2.3.12" +} + +application { + mainClass.set("io.ktor.server.netty.EngineMain") + + val isDevelopment: Boolean = project.ext.has("development") + applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("ch.qos.logback:logback-classic:$logback_version") +{{#hasAuthMethods}} + implementation("com.typesafe:config:1.4.1") +{{/hasAuthMethods}} + implementation("io.ktor:ktor-server-auth") +{{#hasAuthMethods}} + implementation("io.ktor:ktor-client-apache") +{{/hasAuthMethods}} +{{#featureAutoHead}} + implementation("io.ktor:ktor-server-auto-head-response") +{{/featureAutoHead}} + implementation("io.ktor:ktor-server-default-headers") + implementation("io.ktor:ktor-server-content-negotiation") + implementation("io.ktor:ktor-serialization-gson") +{{#featureResources}} + implementation("io.ktor:ktor-server-resources") +{{/featureResources}} +{{#featureHSTS}} + implementation("io.ktor:ktor-server-hsts") +{{/featureHSTS}} +{{#featureCORS}} + implementation("io.ktor:ktor-server-cors") +{{/featureCORS}} +{{#featureConditionalHeaders}} + implementation("io.ktor:ktor-server-conditional-headers") +{{/featureConditionalHeaders}} +{{#featureCompression}} + implementation("io.ktor:ktor-server-compression") +{{/featureCompression}} +{{#featureMetrics}} + implementation("io.dropwizard.metrics:metrics-core:4.1.18") + implementation("io.ktor:ktor-server-metrics") +{{/featureMetrics}} + implementation("io.ktor:ktor-server-netty") + + testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") +} diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/gradle-wrapper.properties b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/gradle-wrapper.properties new file mode 100644 index 000000000000..18330fcba804 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/gradle.properties b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/gradle.properties new file mode 100644 index 000000000000..d507b58c1c87 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/gradle.properties @@ -0,0 +1,4 @@ +kotlin.code.style=official +ktor_version=2.3.12 +kotlin_version=2.0.20 +logback_version=1.4.14 \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/licenseInfo.mustache new file mode 100644 index 000000000000..8d5259173a1b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/licenseInfo.mustache @@ -0,0 +1,11 @@ +/** +* {{{appName}}} +* {{{appDescription}}} +* +* {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}} +* {{#infoEmail}}Contact: {{{.}}}{{/infoEmail}} +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/logback.xml b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/logback.xml new file mode 100644 index 000000000000..d0eaba8debd6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-server/libraries/ktor2/logback.xml @@ -0,0 +1,15 @@ + + + + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + diff --git a/samples/server/petstore/kotlin-server/ktor2/.openapi-generator-ignore b/samples/server/petstore/kotlin-server/ktor2/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/kotlin-server/ktor2/.openapi-generator/FILES b/samples/server/petstore/kotlin-server/ktor2/.openapi-generator/FILES new file mode 100644 index 000000000000..52c6d592e8f2 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/.openapi-generator/FILES @@ -0,0 +1,21 @@ +Dockerfile +README.md +build.gradle.kts +gradle.properties +gradle/wrapper/gradle-wrapper.properties +settings.gradle +src/main/kotlin/org/openapitools/server/AppMain.kt +src/main/kotlin/org/openapitools/server/Configuration.kt +src/main/kotlin/org/openapitools/server/Paths.kt +src/main/kotlin/org/openapitools/server/apis/PetApi.kt +src/main/kotlin/org/openapitools/server/apis/StoreApi.kt +src/main/kotlin/org/openapitools/server/apis/UserApi.kt +src/main/kotlin/org/openapitools/server/infrastructure/ApiKeyAuth.kt +src/main/kotlin/org/openapitools/server/models/Category.kt +src/main/kotlin/org/openapitools/server/models/ModelApiResponse.kt +src/main/kotlin/org/openapitools/server/models/Order.kt +src/main/kotlin/org/openapitools/server/models/Pet.kt +src/main/kotlin/org/openapitools/server/models/Tag.kt +src/main/kotlin/org/openapitools/server/models/User.kt +src/main/resources/application.conf +src/main/resources/logback.xml diff --git a/samples/server/petstore/kotlin-server/ktor2/.openapi-generator/VERSION b/samples/server/petstore/kotlin-server/ktor2/.openapi-generator/VERSION new file mode 100644 index 000000000000..884119126398 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.11.0-SNAPSHOT diff --git a/samples/server/petstore/kotlin-server/ktor2/Dockerfile b/samples/server/petstore/kotlin-server/ktor2/Dockerfile new file mode 100644 index 000000000000..0c1f942d32ae --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/Dockerfile @@ -0,0 +1,7 @@ +FROM openjdk:8-jre-alpine + +COPY ./build/libs/kotlin-server.jar /root/kotlin-server.jar + +WORKDIR /root + +CMD ["java", "-server", "-Xms4g", "-Xmx4g", "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=100", "-XX:+UseStringDeduplication", "-jar", "kotlin-server.jar"] \ No newline at end of file diff --git a/samples/server/petstore/kotlin-server/ktor2/README.md b/samples/server/petstore/kotlin-server/ktor2/README.md new file mode 100644 index 000000000000..505272a7df8a --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/README.md @@ -0,0 +1,106 @@ +# org.openapitools.server - Kotlin Server library for OpenAPI Petstore + +This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + +Generated by OpenAPI Generator 7.11.0-SNAPSHOT. + +## Requires + +* Kotlin 2.0.20 +* Gradle 8.10.2 + +## Build + +First, create the gradle wrapper script: + +``` +gradle wrapper +``` + +Then, run: + +``` +./gradlew check assemble +``` + +This runs all tests and packages the library. + +## Running + +The server builds as a fat jar with a main entrypoint. To start the service, run `java -jar ./build/libs/kotlin-server.jar`. + +You may also run in docker: + +``` +docker build -t kotlin-server . +docker run -p 8080:8080 kotlin-server +``` + +## Features/Implementation Notes + +* Supports JSON inputs/outputs, File inputs, and Form inputs (see ktor documentation for more info). +* ~Supports collection formats for query parameters: csv, tsv, ssv, pipes.~ +* Some Kotlin and Java types are fully qualified to avoid conflicts with types defined in OpenAPI definitions. + + +## Documentation for API Endpoints + +All URIs are relative to *http://petstore.swagger.io/v2* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*PetApi* | [**addPet**](docs/PetApi.md#addpet) | **POST** /pet | Add a new pet to the store +*PetApi* | [**deletePet**](docs/PetApi.md#deletepet) | **DELETE** /pet/{petId} | Deletes a pet +*PetApi* | [**findPetsByStatus**](docs/PetApi.md#findpetsbystatus) | **GET** /pet/findByStatus | Finds Pets by status +*PetApi* | [**findPetsByTags**](docs/PetApi.md#findpetsbytags) | **GET** /pet/findByTags | Finds Pets by tags +*PetApi* | [**getPetById**](docs/PetApi.md#getpetbyid) | **GET** /pet/{petId} | Find pet by ID +*PetApi* | [**updatePet**](docs/PetApi.md#updatepet) | **PUT** /pet | Update an existing pet +*PetApi* | [**updatePetWithForm**](docs/PetApi.md#updatepetwithform) | **POST** /pet/{petId} | Updates a pet in the store with form data +*PetApi* | [**uploadFile**](docs/PetApi.md#uploadfile) | **POST** /pet/{petId}/uploadImage | uploads an image +*StoreApi* | [**deleteOrder**](docs/StoreApi.md#deleteorder) | **DELETE** /store/order/{orderId} | Delete purchase order by ID +*StoreApi* | [**getInventory**](docs/StoreApi.md#getinventory) | **GET** /store/inventory | Returns pet inventories by status +*StoreApi* | [**getOrderById**](docs/StoreApi.md#getorderbyid) | **GET** /store/order/{orderId} | Find purchase order by ID +*StoreApi* | [**placeOrder**](docs/StoreApi.md#placeorder) | **POST** /store/order | Place an order for a pet +*UserApi* | [**createUser**](docs/UserApi.md#createuser) | **POST** /user | Create user +*UserApi* | [**createUsersWithArrayInput**](docs/UserApi.md#createuserswitharrayinput) | **POST** /user/createWithArray | Creates list of users with given input array +*UserApi* | [**createUsersWithListInput**](docs/UserApi.md#createuserswithlistinput) | **POST** /user/createWithList | Creates list of users with given input array +*UserApi* | [**deleteUser**](docs/UserApi.md#deleteuser) | **DELETE** /user/{username} | Delete user +*UserApi* | [**getUserByName**](docs/UserApi.md#getuserbyname) | **GET** /user/{username} | Get user by user name +*UserApi* | [**loginUser**](docs/UserApi.md#loginuser) | **GET** /user/login | Logs user into the system +*UserApi* | [**logoutUser**](docs/UserApi.md#logoutuser) | **GET** /user/logout | Logs out current logged in user session +*UserApi* | [**updateUser**](docs/UserApi.md#updateuser) | **PUT** /user/{username} | Updated user + + + +## Documentation for Models + + - [org.openapitools.server.models.Category](docs/Category.md) + - [org.openapitools.server.models.ModelApiResponse](docs/ModelApiResponse.md) + - [org.openapitools.server.models.Order](docs/Order.md) + - [org.openapitools.server.models.Pet](docs/Pet.md) + - [org.openapitools.server.models.Tag](docs/Tag.md) + - [org.openapitools.server.models.User](docs/User.md) + + + +## Documentation for Authorization + + +Authentication schemes defined for the API: + +### petstore_auth + +- **Type**: OAuth +- **Flow**: implicit +- **Authorization URL**: http://petstore.swagger.io/api/oauth/dialog +- **Scopes**: + - write:pets: modify pets in your account + - read:pets: read your pets + + +### api_key + +- **Type**: API key +- **API key parameter name**: api_key +- **Location**: HTTP header + diff --git a/samples/server/petstore/kotlin-server/ktor2/build.gradle.kts b/samples/server/petstore/kotlin-server/ktor2/build.gradle.kts new file mode 100644 index 000000000000..fb35b77164a8 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/build.gradle.kts @@ -0,0 +1,41 @@ + +val kotlin_version: String by project +val logback_version: String by project + +group = "org.openapitools" +version = "1.0.0" + +plugins { + kotlin("jvm") version "2.0.20" + id("io.ktor.plugin") version "2.3.12" +} + +application { + mainClass.set("io.ktor.server.netty.EngineMain") + + val isDevelopment: Boolean = project.ext.has("development") + applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("ch.qos.logback:logback-classic:$logback_version") + implementation("com.typesafe:config:1.4.1") + implementation("io.ktor:ktor-server-auth") + implementation("io.ktor:ktor-client-apache") + implementation("io.ktor:ktor-server-auto-head-response") + implementation("io.ktor:ktor-server-default-headers") + implementation("io.ktor:ktor-server-content-negotiation") + implementation("io.ktor:ktor-serialization-gson") + implementation("io.ktor:ktor-server-resources") + implementation("io.ktor:ktor-server-hsts") + implementation("io.ktor:ktor-server-compression") + implementation("io.dropwizard.metrics:metrics-core:4.1.18") + implementation("io.ktor:ktor-server-metrics") + implementation("io.ktor:ktor-server-netty") + + testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") +} diff --git a/samples/server/petstore/kotlin-server/ktor2/gradle.properties b/samples/server/petstore/kotlin-server/ktor2/gradle.properties new file mode 100644 index 000000000000..d507b58c1c87 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/gradle.properties @@ -0,0 +1,4 @@ +kotlin.code.style=official +ktor_version=2.3.12 +kotlin_version=2.0.20 +logback_version=1.4.14 \ No newline at end of file diff --git a/samples/server/petstore/kotlin-server/ktor2/gradle/wrapper/gradle-wrapper.properties b/samples/server/petstore/kotlin-server/ktor2/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..18330fcba804 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/samples/server/petstore/kotlin-server/ktor2/settings.gradle b/samples/server/petstore/kotlin-server/ktor2/settings.gradle new file mode 100644 index 000000000000..a09a58efab11 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'kotlin-server' \ No newline at end of file diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/AppMain.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/AppMain.kt new file mode 100644 index 000000000000..2de9d58ce937 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/AppMain.kt @@ -0,0 +1,75 @@ +package org.openapitools.server + +import io.ktor.server.application.* +import io.ktor.serialization.gson.* +import io.ktor.http.* +import io.ktor.server.resources.* +import io.ktor.server.plugins.autohead.* +import io.ktor.server.plugins.compression.* +import io.ktor.server.plugins.contentnegotiation.* +import io.ktor.server.plugins.defaultheaders.* +import io.ktor.server.plugins.hsts.* +import com.codahale.metrics.Slf4jReporter +import io.ktor.server.metrics.dropwizard.* +import java.util.concurrent.TimeUnit +import io.ktor.server.routing.* +import com.typesafe.config.ConfigFactory +import io.ktor.client.HttpClient +import io.ktor.client.engine.apache.Apache +import io.ktor.server.config.HoconApplicationConfig +import io.ktor.server.auth.* +import org.openapitools.server.infrastructure.* +import org.openapitools.server.apis.PetApi +import org.openapitools.server.apis.StoreApi +import org.openapitools.server.apis.UserApi + + +internal val settings = HoconApplicationConfig(ConfigFactory.defaultApplication(HTTP::class.java.classLoader)) + +object HTTP { + val client = HttpClient(Apache) +} + +fun Application.main() { + install(DefaultHeaders) + install(DropwizardMetrics) { + val reporter = Slf4jReporter.forRegistry(registry) + .outputTo(this@main.log) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .build() + reporter.start(10, TimeUnit.SECONDS) + } + install(ContentNegotiation) { + register(ContentType.Application.Json, GsonConverter()) + } + install(AutoHeadResponse) // see https://ktor.io/docs/autoheadresponse.html + install(Compression, ApplicationCompressionConfiguration()) // see https://ktor.io/docs/compression.html + install(HSTS, ApplicationHstsConfiguration()) // see https://ktor.io/docs/hsts.html + install(Resources) + install(Authentication) { + oauth("petstore_auth") { + client = HttpClient(Apache) + providerLookup = { applicationAuthProvider(this@main.environment.config) } + urlProvider = { _ -> + // TODO: define a callback url here. + "/" + } + } + // "Implement API key auth (api_key) for parameter name 'api_key'." + apiKeyAuth("api_key") { + validate { apikeyCredential: ApiKeyCredential -> + when { + apikeyCredential.value == "keyboardcat" -> ApiPrincipal(apikeyCredential) + else -> null + } + } + } + } + install(Routing) { + PetApi() + StoreApi() + UserApi() + } + +} diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/Configuration.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/Configuration.kt new file mode 100644 index 000000000000..f78654c60f0f --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/Configuration.kt @@ -0,0 +1,63 @@ +package org.openapitools.server + +// Use this file to hold package-level internal functions that return receiver object passed to the `install` method. +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 +import io.ktor.server.plugins.compression.* +import io.ktor.server.plugins.hsts.* + + +/** + * Application block for [HSTS] configuration. + * + * This file may be excluded in .openapi-generator-ignore, + * and application-specific configuration can be applied in this function. + * + * See http://ktor.io/features/hsts.html + */ +internal fun ApplicationHstsConfiguration(): HSTSConfig.() -> Unit { + return { + maxAgeInSeconds = TimeUnit.DAYS.toSeconds(365) + includeSubDomains = true + preload = false + + // You may also apply any custom directives supported by specific user-agent. For example: + // customDirectives.put("redirectHttpToHttps", "false") + } +} + +/** + * Application block for [Compression] configuration. + * + * This file may be excluded in .openapi-generator-ignore, + * and application-specific configuration can be applied in this function. + * + * See http://ktor.io/features/compression.html + */ +internal fun ApplicationCompressionConfiguration(): CompressionConfig.() -> Unit { + return { + gzip { + priority = 1.0 + } + deflate { + priority = 10.0 + minimumSize(1024) // condition + } + } +} + +// Defines authentication mechanisms used throughout the application. +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") + ) diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/Paths.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/Paths.kt new file mode 100644 index 000000000000..58a0096fe4ee --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/Paths.kt @@ -0,0 +1,164 @@ +/** +* OpenAPI Petstore +* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +* +* The version of the OpenAPI document: 1.0.0 +* +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package org.openapitools.server + +import io.ktor.resources.* +import kotlinx.serialization.* +import org.openapitools.server.models.* + +object Paths { + /** + * Add a new pet to the store + * + * @param pet Pet object that needs to be added to the store + */ + @Serializable @Resource("/pet") class addPet(val pet: Pet) + + /** + * Deletes a pet + * + * @param petId Pet id to delete + * @param apiKey (optional) + */ + @Serializable @Resource("/pet/{petId}") class deletePet(val petId: kotlin.Long, val apiKey: kotlin.String? = null) + + /** + * Finds Pets by status + * Multiple status values can be provided with comma separated strings + * @param status Status values that need to be considered for filter + */ + @Serializable @Resource("/pet/findByStatus") class findPetsByStatus(val status: kotlin.collections.List) + + /** + * Finds Pets by tags + * Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + * @param tags Tags to filter by + */ + @Serializable @Resource("/pet/findByTags") class findPetsByTags(val tags: kotlin.collections.List) + + /** + * Find pet by ID + * Returns a single pet + * @param petId ID of pet to return + */ + @Serializable @Resource("/pet/{petId}") class getPetById(val petId: kotlin.Long) + + /** + * Update an existing pet + * + * @param pet Pet object that needs to be added to the store + */ + @Serializable @Resource("/pet") class updatePet(val pet: 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) + */ + @Serializable @Resource("/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) + */ + @Serializable @Resource("/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 < 1000. Anything above 1000 or nonintegers will generate API errors + * @param orderId ID of the order that needs to be deleted + */ + @Serializable @Resource("/store/order/{orderId}") class deleteOrder(val orderId: kotlin.String) + + /** + * Returns pet inventories by status + * Returns a map of status codes to quantities + */ + @Serializable @Resource("/store/inventory") class getInventory + + /** + * Find purchase order by ID + * For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions + * @param orderId ID of pet that needs to be fetched + */ + @Serializable @Resource("/store/order/{orderId}") class getOrderById(val orderId: kotlin.Long) + + /** + * Place an order for a pet + * + * @param order order placed for purchasing the pet + */ + @Serializable @Resource("/store/order") class placeOrder(val order: Order) + + /** + * Create user + * This can only be done by the logged in user. + * @param user Created user object + */ + @Serializable @Resource("/user") class createUser(val user: User) + + /** + * Creates list of users with given input array + * + * @param user List of user object + */ + @Serializable @Resource("/user/createWithArray") class createUsersWithArrayInput(val user: kotlin.collections.List) + + /** + * Creates list of users with given input array + * + * @param user List of user object + */ + @Serializable @Resource("/user/createWithList") class createUsersWithListInput(val user: kotlin.collections.List) + + /** + * Delete user + * This can only be done by the logged in user. + * @param username The name that needs to be deleted + */ + @Serializable @Resource("/user/{username}") class deleteUser(val username: kotlin.String) + + /** + * Get user by user name + * + * @param username The name that needs to be fetched. Use user1 for testing. + */ + @Serializable @Resource("/user/{username}") class getUserByName(val username: kotlin.String) + + /** + * Logs user into the system + * + * @param username The user name for login + * @param password The password for login in clear text + */ + @Serializable @Resource("/user/login") class loginUser(val username: kotlin.String, val password: kotlin.String) + + /** + * Logs out current logged in user session + * + */ + @Serializable @Resource("/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 user Updated user object + */ + @Serializable @Resource("/user/{username}") class updateUser(val username: kotlin.String, val user: User) + +} diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/apis/PetApi.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/apis/PetApi.kt new file mode 100644 index 000000000000..7b0482f3d322 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/apis/PetApi.kt @@ -0,0 +1,282 @@ +/** +* OpenAPI Petstore +* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +* +* The version of the OpenAPI document: 1.0.0 +* +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package org.openapitools.server.apis + +import com.google.gson.Gson +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.auth.* +import io.ktor.server.response.* +import org.openapitools.server.Paths +import io.ktor.server.resources.options +import io.ktor.server.resources.get +import io.ktor.server.resources.post +import io.ktor.server.resources.put +import io.ktor.server.resources.delete +import io.ktor.server.resources.head +import io.ktor.server.resources.patch +import io.ktor.server.routing.* +import org.openapitools.server.infrastructure.ApiPrincipal +import org.openapitools.server.models.ModelApiResponse +import org.openapitools.server.models.Pet + +fun Route.PetApi() { + val gson = Gson() + val empty = mutableMapOf() + + authenticate("petstore_auth") { + post { + + val principal = call.authentication.principal()!! + + + val exampleContentType = "application/json" + val exampleContentString = """{ + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" + }""" + + when (exampleContentType) { + "application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java)) + "application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml) + else -> call.respondText(exampleContentString) + } + + } + } + + authenticate("petstore_auth") { + delete { + + val principal = call.authentication.principal()!! + + + call.respond(HttpStatusCode.NotImplemented) + + } + } + + authenticate("petstore_auth") { + get { + + val principal = call.authentication.principal()!! + + + val exampleContentType = "application/json" + val exampleContentString = """[ { + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" + }, { + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" + } ]""" + + when (exampleContentType) { + "application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java)) + "application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml) + else -> call.respondText(exampleContentString) + } + + } + } + + authenticate("petstore_auth") { + get { + + val principal = call.authentication.principal()!! + + + val exampleContentType = "application/json" + val exampleContentString = """[ { + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" + }, { + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" + } ]""" + + when (exampleContentType) { + "application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java)) + "application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml) + else -> call.respondText(exampleContentString) + } + + } + } + + authenticate("api_key") { + get { + + val principal = call.authentication.principal()!! + + + val exampleContentType = "application/json" + val exampleContentString = """{ + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" + }""" + + when (exampleContentType) { + "application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java)) + "application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml) + else -> call.respondText(exampleContentString) + } + + } + } + + authenticate("petstore_auth") { + put { + + val principal = call.authentication.principal()!! + + + val exampleContentType = "application/json" + val exampleContentString = """{ + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" + }""" + + when (exampleContentType) { + "application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java)) + "application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml) + else -> call.respondText(exampleContentString) + } + + } + } + + authenticate("petstore_auth") { + post { + + val principal = call.authentication.principal()!! + + + call.respond(HttpStatusCode.NotImplemented) + + } + } + + authenticate("petstore_auth") { + post { + + val principal = call.authentication.principal()!! + + + 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) + } + + } + } + +} diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/apis/StoreApi.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/apis/StoreApi.kt new file mode 100644 index 000000000000..fe780f7feb46 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/apis/StoreApi.kt @@ -0,0 +1,89 @@ +/** +* OpenAPI Petstore +* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +* +* The version of the OpenAPI document: 1.0.0 +* +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package org.openapitools.server.apis + +import com.google.gson.Gson +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.auth.* +import io.ktor.server.response.* +import org.openapitools.server.Paths +import io.ktor.server.resources.options +import io.ktor.server.resources.get +import io.ktor.server.resources.post +import io.ktor.server.resources.put +import io.ktor.server.resources.delete +import io.ktor.server.resources.head +import io.ktor.server.resources.patch +import io.ktor.server.routing.* +import org.openapitools.server.infrastructure.ApiPrincipal +import org.openapitools.server.models.Order + +fun Route.StoreApi() { + val gson = Gson() + val empty = mutableMapOf() + + delete { + call.respond(HttpStatusCode.NotImplemented) + + } + + authenticate("api_key") { + get { + + val principal = call.authentication.principal()!! + + + call.respond(HttpStatusCode.NotImplemented) + + } + } + + get { + 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 { + 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) + } + + } + +} diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/apis/UserApi.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/apis/UserApi.kt new file mode 100644 index 000000000000..f3b7a76c39a0 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/apis/UserApi.kt @@ -0,0 +1,127 @@ +/** +* OpenAPI Petstore +* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +* +* The version of the OpenAPI document: 1.0.0 +* +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package org.openapitools.server.apis + +import com.google.gson.Gson +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.auth.* +import io.ktor.server.response.* +import org.openapitools.server.Paths +import io.ktor.server.resources.options +import io.ktor.server.resources.get +import io.ktor.server.resources.post +import io.ktor.server.resources.put +import io.ktor.server.resources.delete +import io.ktor.server.resources.head +import io.ktor.server.resources.patch +import io.ktor.server.routing.* +import org.openapitools.server.infrastructure.ApiPrincipal +import org.openapitools.server.models.User + +fun Route.UserApi() { + val gson = Gson() + val empty = mutableMapOf() + + authenticate("api_key") { + post { + + val principal = call.authentication.principal()!! + + + call.respond(HttpStatusCode.NotImplemented) + + } + } + + authenticate("api_key") { + post { + + val principal = call.authentication.principal()!! + + + call.respond(HttpStatusCode.NotImplemented) + + } + } + + authenticate("api_key") { + post { + + val principal = call.authentication.principal()!! + + + call.respond(HttpStatusCode.NotImplemented) + + } + } + + authenticate("api_key") { + delete { + + val principal = call.authentication.principal()!! + + + call.respond(HttpStatusCode.NotImplemented) + + } + } + + get { + val exampleContentType = "application/json" + val exampleContentString = """{ + "firstName" : "firstName", + "lastName" : "lastName", + "password" : "password", + "userStatus" : 6, + "phone" : "phone", + "id" : 0, + "email" : "email", + "username" : "username" + }""" + + 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 { + call.respond(HttpStatusCode.NotImplemented) + + } + + authenticate("api_key") { + get { + + val principal = call.authentication.principal()!! + + + call.respond(HttpStatusCode.NotImplemented) + + } + } + + authenticate("api_key") { + put { + + val principal = call.authentication.principal()!! + + + call.respond(HttpStatusCode.NotImplemented) + + } + } + +} diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/infrastructure/ApiKeyAuth.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/infrastructure/ApiKeyAuth.kt new file mode 100644 index 000000000000..c2cad7fe7a7f --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/infrastructure/ApiKeyAuth.kt @@ -0,0 +1,102 @@ +package org.openapitools.server.infrastructure + +import io.ktor.http.auth.* +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"), + HEADER("header") +} + +data class ApiKeyCredential(val value: String) : Credential +data class ApiPrincipal(val apiKeyCredential: ApiKeyCredential?) : Principal + +/** +* Represents an Api Key authentication provider +*/ +class ApiKeyAuthenticationProvider(configuration: Configuration) : AuthenticationProvider(configuration) { + + private val authenticationFunction = configuration.authenticationFunction + + private val apiKeyName: String = configuration.apiKeyName + + private val apiKeyLocation: ApiKeyLocation = configuration.apiKeyLocation + + 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 + } + + if (cause != null) { + context.challenge(apiKeyName, cause) { challenge, call -> + call.respond( + UnauthorizedResponse( + HttpAuthHeader.Parameterized( + "API_KEY", + mapOf("key" to apiKeyName), + HeaderValueEncoding.QUOTED_ALWAYS + ) + ) + ) + challenge.complete() + } + } + + if (principal != null) { + context.principal(principal) + } + } + + class Configuration internal constructor(name: String?) : Config(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 AuthenticationConfig.apiKeyAuth( + name: String? = null, + configure: ApiKeyAuthenticationProvider.Configuration.() -> Unit +) { + val configuration = ApiKeyAuthenticationProvider.Configuration(name).apply(configure) + val provider = ApiKeyAuthenticationProvider(configuration) + 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] + } + return when (value) { + null -> null + else -> ApiKeyCredential(value) + } +} diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Category.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Category.kt new file mode 100644 index 000000000000..9d8571921384 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Category.kt @@ -0,0 +1,30 @@ +/** +* OpenAPI Petstore +* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +* +* The version of the OpenAPI document: 1.0.0 +* +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package org.openapitools.server.models + + +import java.io.Serializable +/** + * A category for a pet + * @param id + * @param name + */ +data class Category( + val id: kotlin.Long? = null, + val name: kotlin.String? = null +) : Serializable +{ + companion object { + private const val serialVersionUID: Long = 123 + } +} + diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/ModelApiResponse.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/ModelApiResponse.kt new file mode 100644 index 000000000000..5548175d9048 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/ModelApiResponse.kt @@ -0,0 +1,32 @@ +/** +* OpenAPI Petstore +* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +* +* The version of the OpenAPI document: 1.0.0 +* +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package org.openapitools.server.models + + +import java.io.Serializable +/** + * Describes the result of uploading an image resource + * @param code + * @param type + * @param message + */ +data class ModelApiResponse( + val code: kotlin.Int? = null, + val type: kotlin.String? = null, + val message: kotlin.String? = null +) : Serializable +{ + companion object { + private const val serialVersionUID: Long = 123 + } +} + diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Order.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Order.kt new file mode 100644 index 000000000000..79dce95e1e47 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Order.kt @@ -0,0 +1,48 @@ +/** +* OpenAPI Petstore +* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +* +* The version of the OpenAPI document: 1.0.0 +* +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package org.openapitools.server.models + + +import java.io.Serializable +/** + * An order for a pets from the pet store + * @param id + * @param petId + * @param quantity + * @param shipDate + * @param status Order Status + * @param complete + */ +data class Order( + val id: kotlin.Long? = null, + val petId: kotlin.Long? = null, + val quantity: kotlin.Int? = null, + val shipDate: java.time.OffsetDateTime? = null, + /* Order Status */ + val status: Order.Status? = null, + val complete: kotlin.Boolean? = false +) : Serializable +{ + companion object { + private const val serialVersionUID: Long = 123 + } + /** + * Order Status + * Values: placed,approved,delivered + */ + enum class Status(val value: kotlin.String){ + placed("placed"), + approved("approved"), + delivered("delivered"); + } +} + diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Pet.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Pet.kt new file mode 100644 index 000000000000..8b98ce0469c5 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Pet.kt @@ -0,0 +1,50 @@ +/** +* OpenAPI Petstore +* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +* +* The version of the OpenAPI document: 1.0.0 +* +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package org.openapitools.server.models + +import org.openapitools.server.models.Category +import org.openapitools.server.models.Tag + +import java.io.Serializable +/** + * A pet for sale in the pet store + * @param name + * @param photoUrls + * @param id + * @param category + * @param tags + * @param status pet status in the store + */ +data class Pet( + val name: kotlin.String, + val photoUrls: kotlin.collections.List, + val id: kotlin.Long? = null, + val category: Category? = null, + val tags: kotlin.collections.List? = null, + /* pet status in the store */ + val status: Pet.Status? = null +) : Serializable +{ + companion object { + private const val serialVersionUID: Long = 123 + } + /** + * pet status in the store + * Values: available,pending,sold + */ + enum class Status(val value: kotlin.String){ + available("available"), + pending("pending"), + sold("sold"); + } +} + diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Tag.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Tag.kt new file mode 100644 index 000000000000..e9626b25c6d1 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/Tag.kt @@ -0,0 +1,30 @@ +/** +* OpenAPI Petstore +* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +* +* The version of the OpenAPI document: 1.0.0 +* +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package org.openapitools.server.models + + +import java.io.Serializable +/** + * A tag for a pet + * @param id + * @param name + */ +data class Tag( + val id: kotlin.Long? = null, + val name: kotlin.String? = null +) : Serializable +{ + companion object { + private const val serialVersionUID: Long = 123 + } +} + diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/User.kt b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/User.kt new file mode 100644 index 000000000000..fdbdb4e1cff4 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/kotlin/org/openapitools/server/models/User.kt @@ -0,0 +1,43 @@ +/** +* OpenAPI Petstore +* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +* +* The version of the OpenAPI document: 1.0.0 +* +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package org.openapitools.server.models + + +import java.io.Serializable +/** + * A User who is purchasing from the pet store + * @param id + * @param username + * @param firstName + * @param lastName + * @param email + * @param password + * @param phone + * @param userStatus User Status + */ +data class User( + val id: kotlin.Long? = null, + val username: kotlin.String? = null, + val firstName: kotlin.String? = null, + val lastName: kotlin.String? = null, + val email: kotlin.String? = null, + val password: kotlin.String? = null, + val phone: kotlin.String? = null, + /* User Status */ + val userStatus: kotlin.Int? = null +) : Serializable +{ + companion object { + private const val serialVersionUID: Long = 123 + } +} + diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/resources/application.conf b/samples/server/petstore/kotlin-server/ktor2/src/main/resources/application.conf new file mode 100644 index 000000000000..d33fe93f9937 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/resources/application.conf @@ -0,0 +1,23 @@ +ktor { + deployment { + environment = development + port = 8080 + autoreload = true + watch = [ org.openapitools.server ] + } + + application { + modules = [ org.openapitools.server.AppMainKt.main ] + } +} + +# Typesafe config allows multiple ways to provide configuration values without hard-coding them here. +# Please see https://github.com/lightbend/config for details. +auth { + oauth { + petstore_auth { + clientId = "" + clientSecret = "" + } + } +} \ No newline at end of file diff --git a/samples/server/petstore/kotlin-server/ktor2/src/main/resources/logback.xml b/samples/server/petstore/kotlin-server/ktor2/src/main/resources/logback.xml new file mode 100644 index 000000000000..d0eaba8debd6 --- /dev/null +++ b/samples/server/petstore/kotlin-server/ktor2/src/main/resources/logback.xml @@ -0,0 +1,15 @@ + + + + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + +