[kotlin-server] --library=ktor (barebones implementation) (#7412)

* [tools] Make sed in new.sh more cross-platform

The -r option passed to sed is a GNU sed option for extended regex
evaluation. The -E option evaluates the same option, and is part of the
POSIX standard, meaning this option is available in GNU sed as well as
Apple's BSD variant.

This commit removes the need for users to install gnu-sed on Mac.

* [ktor] Initial ktor (kotlin-server)

This adds a very barebones implementation for a ktor server generator.

This supports metrics and typed locations. All endpoins are stubbed to
return HTTP/1.1 501 Not Implemented.

* [ktor] Initial sample

* [ktor] Adding options for select feature installs

Options available:

* featureAutoHead
* featureConditionalHeaders
* featureHSTS
* featureCORS
* featureCompression

* [ktor] Start of auth functionality

* [ktor] API key auth placeholder

* Add basic support for oauth2 configurations

ktor doesn't seem to explicitly accept oauth flow properties in its
configuration object. This may be a blocker for 'implicit' flow
definitions.

* Added example response objects

* [ktor] Route for apis with bodies, some cleanup

ktor locations are only supported for routes with path/query parameters.
Routes with body or file parameters must be declared with traditional
route api.

This commit also includes lambdas for simplifying processing in
library-based server generator code. As an example, ktor requires
lowercase http methods while spring (a potential future generator)
would require an uppercase such as HttpMethod.GET. It doesn't make sense
to modify these in the operations post-process method because that
format wouldn't be universally desirable.

The lambdas included in the KotlinServerCodegen:
* lowercase: converts all text to lowercase
* uppercase: converts all text to UPPERCASE
* titlecase: converts words (with configurable delim) to Title Case
* indented|indented_8|indented_12|indented_16: these helpers apply the
  same desired indent to all lines of an included fragment's text.

* Fix some javadoc issues in lambda classes

* Update kotlin-server-petstore.bat

Change `kotlin` to `kotlin-server`

* Fix javadoc error messages in CI
This commit is contained in:
Jim Schubert
2018-01-27 04:43:46 -05:00
committed by William Cheng
parent a2410b210c
commit 7cad47dd39
72 changed files with 3644 additions and 496 deletions

View File

@@ -0,0 +1,4 @@
build/
gradle/
gradlew
gradlew.bat

View File

@@ -0,0 +1,23 @@
# Swagger Codegen Ignore
# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen
# 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 Swagger Codgen 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

View File

@@ -0,0 +1 @@
2.3.0

View File

@@ -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"]

View File

@@ -0,0 +1,104 @@
# io.swagger.server - Kotlin Server library for Swagger Petstore
This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.
Generated by Swagger Codegen 2.3.0 (2018-01-21T22:11:17.518-05:00).
## Requires
* Kotlin 1.2.10
* Gradle 4.3
## 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 Swagger definitions.
<a name="documentation-for-api-endpoints"></a>
## 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
<a name="documentation-for-models"></a>
## Documentation for Models
- [io.swagger.server.models.ApiResponse](docs/ApiResponse.md)
- [io.swagger.server.models.Category](docs/Category.md)
- [io.swagger.server.models.Order](docs/Order.md)
- [io.swagger.server.models.Pet](docs/Pet.md)
- [io.swagger.server.models.Tag](docs/Tag.md)
- [io.swagger.server.models.User](docs/User.md)
<a name="documentation-for-authorization"></a>
## Documentation for Authorization
<a name="api_key"></a>
### api_key
- **Type**: API key
- **API key parameter name**: api_key
- **Location**: HTTP header
<a name="petstore_auth"></a>
### 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

View File

@@ -0,0 +1,74 @@
group 'io.swagger'
version '1.0.0'
task wrapper(type: Wrapper) {
gradleVersion = '4.3'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.2.10'
ext.ktor_version = '0.9.1-alpha-9'
ext.shadow_version = '2.0.2'
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.github.jengelman.gradle.plugins:shadow:$shadow_version"
}
}
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName = "io.ktor.server.netty.DevelopmentEngine"
// Initialization order with shadow 2.0.1 and Gradle 4.3 is weird.
// See https://github.com/johnrengelman/shadow/issues/336#issuecomment-355402508
apply plugin: 'com.github.johnrengelman.shadow'
sourceCompatibility = 1.8
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
kotlin {
experimental {
coroutines "enable"
}
}
shadowJar {
baseName = 'kotlin-server'
classifier = null
version = null
}
repositories {
mavenCentral()
maven { url "http://dl.bintray.com/kotlin/ktor" }
maven { url "https://dl.bintray.com/kotlin/kotlinx" }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "io.ktor:ktor-metrics:$ktor_version"
compile "io.ktor:ktor-locations:$ktor_version"
compile "io.ktor:ktor-gson:$ktor_version"
compile "io.ktor:ktor-client-core:$ktor_version"
compile "io.ktor:ktor-client-apache:$ktor_version"
compile "ch.qos.logback:logback-classic:1.2.1"
testCompile group: 'junit', name: 'junit', version: '4.12'
}

View File

@@ -0,0 +1 @@
org.gradle.caching=true

View File

@@ -0,0 +1 @@
rootProject.name = 'kotlin-server'

View File

@@ -0,0 +1,52 @@
package io.swagger.server
import com.codahale.metrics.*
import com.typesafe.config.ConfigFactory
import io.ktor.application.*
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache.Apache
import io.ktor.config.HoconApplicationConfig
import io.ktor.features.*
import io.ktor.gson.GsonConverter
import io.ktor.http.ContentType
import io.ktor.locations.*
import io.ktor.metrics.*
import io.ktor.routing.*
import java.util.concurrent.*
import io.swagger.server.apis.*
internal val settings = HoconApplicationConfig(ConfigFactory.defaultApplication(HTTP::class.java.classLoader))
object HTTP {
val client = HttpClient(Apache)
}
fun Application.main() {
install(DefaultHeaders)
install(Metrics) {
val reporter = Slf4jReporter.forRegistry(registry)
.outputTo(log)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build()
reporter.start(10, TimeUnit.SECONDS)
}
install(ContentNegotiation) {
register(ContentType.Application.Json, GsonConverter())
}
install(AutoHeadResponse) // see http://ktor.io/features/autoheadresponse.html
install(HSTS, ApplicationHstsConfiguration()) // see http://ktor.io/features/hsts.html
install(Compression, ApplicationCompressionConfiguration()) // see http://ktor.io/features/compression.html
install(Locations) // see http://ktor.io/features/locations.html
install(Routing) {
PetApi()
StoreApi()
UserApi()
}
environment.monitor.subscribe(ApplicationStopping)
{
HTTP.client.close()
}
}

View File

@@ -0,0 +1,76 @@
package io.swagger.server
// Use this file to hold package-level internal functions that return receiver object passed to the `install` method.
import io.ktor.auth.OAuthServerSettings
import io.ktor.features.*
import io.ktor.http.*
import java.time.Duration
import java.util.concurrent.Executors
import io.swagger.server.settings
/**
* Application block for [HSTS] configuration.
*
* This file may be excluded in .swagger-codegen-ignore,
* and application specific configuration can be applied in this function.
*
* See http://ktor.io/features/hsts.html
*/
internal fun ApplicationHstsConfiguration(): HSTS.Configuration.() -> Unit {
return {
maxAge = Duration.ofDays(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 .swagger-codegen-ignore,
* and application specific configuration can be applied in this function.
*
* See http://ktor.io/features/compression.html
*/
internal fun ApplicationCompressionConfiguration(): Compression.Configuration.() -> Unit {
return {
gzip {
priority = 1.0
}
deflate {
priority = 10.0
minimumSize(1024) // condition
}
}
}
// Defines authentication mechanisms used throughout the application.
val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthServerSettings>(
OAuthServerSettings.OAuth2ServerSettings(
name = "petstore_auth",
authorizeUrl = "http://petstore.swagger.io/api/oauth/dialog",
accessTokenUrl = "",
requestMethod = HttpMethod.Get,
clientId = settings.property("auth.oauth.petstore_auth.clientId").getString(),
clientSecret = settings.property("auth.oauth.petstore_auth.clientSecret").getString(),
defaultScopes = listOf("write:pets", "read:pets")
)
// OAuthServerSettings.OAuth2ServerSettings(
// name = "facebook",
// authorizeUrl = "https://graph.facebook.com/oauth/authorize",
// accessTokenUrl = "https://graph.facebook.com/oauth/access_token",
// requestMethod = HttpMethod.Post,
//
// clientId = "settings.property("auth.oauth.facebook.clientId").getString()",
// clientSecret = "settings.property("auth.oauth.facebook.clientSecret").getString()",
// defaultScopes = listOf("public_profile")
// )
).associateBy { it.name }
// Provides an application-level fixed thread pool on which to execute coroutines (mainly)
internal val ApplicationExecutors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 4)

View File

@@ -0,0 +1,110 @@
/**
* Swagger Petstore
* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.server
import io.ktor.application.ApplicationCall
import io.ktor.http.HttpMethod
import io.ktor.locations.*
import io.ktor.pipeline.PipelineContext
import io.ktor.routing.Route
import io.ktor.routing.method
import io.swagger.server.models.*
// NOTE: ktor-location@0.9.0 is missing extension for Route.delete. This includes it.
inline fun <reified T : Any> Route.delete(noinline body: suspend PipelineContext<Unit, ApplicationCall>.(T) -> Unit): Route {
return location(T::class) {
method(HttpMethod.Delete) {
handle(body)
}
}
}
object Paths {
/**
* Deletes a pet
*
* @param petId Pet id to delete
* @param apiKey (optional)
*/
@Location("/pet/{petId}") class deletePet(val petId: kotlin.Long, val apiKey: kotlin.String)
/**
* 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
*/
@Location("/pet/findByStatus") class findPetsByStatus(val status: kotlin.Array<kotlin.String>)
/**
* 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
*/
@Location("/pet/findByTags") class findPetsByTags(val tags: kotlin.Array<kotlin.String>)
/**
* Find pet by ID
* Returns a single pet
* @param petId ID of pet to return
*/
@Location("/pet/{petId}") class getPetById(val petId: kotlin.Long)
/**
* Delete purchase order by ID
* For valid response try integer IDs with value &lt; 1000. Anything above 1000 or nonintegers will generate API errors
* @param orderId ID of the order that needs to be deleted
*/
@Location("/store/order/{orderId}") class deleteOrder(val orderId: kotlin.String)
/**
* Returns pet inventories by status
* Returns a map of status codes to quantities
*/
@Location("/store/inventory") class getInventory()
/**
* Find purchase order by ID
* For valid response try integer IDs with value &lt;&#x3D; 5 or &gt; 10. Other values will generated exceptions
* @param orderId ID of pet that needs to be fetched
*/
@Location("/store/order/{orderId}") class getOrderById(val orderId: kotlin.Long)
/**
* Delete user
* This can only be done by the logged in user.
* @param username The name that needs to be deleted
*/
@Location("/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.
*/
@Location("/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
*/
@Location("/user/login") class loginUser(val username: kotlin.String, val password: kotlin.String)
/**
* Logs out current logged in user session
*
*/
@Location("/user/logout") class logoutUser()
}

View File

@@ -0,0 +1,334 @@
/**
* Swagger Petstore
* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.server.apis
import com.google.gson.Gson
import io.ktor.application.call
import io.ktor.auth.UserIdPrincipal
import io.ktor.auth.authentication
import io.ktor.auth.basicAuthentication
import io.ktor.auth.oauth
import io.ktor.auth.OAuthAccessTokenResponse
import io.ktor.auth.OAuthServerSettings
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.locations.*
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.*
import kotlinx.coroutines.experimental.asCoroutineDispatcher
import io.swagger.server.ApplicationAuthProviders
import io.swagger.server.Paths
import io.swagger.server.ApplicationExecutors
import io.swagger.server.HTTP.client
import io.swagger.server.infrastructure.ApiPrincipal
import io.swagger.server.infrastructure.apiKeyAuth
// ktor 0.9.x is missing io.ktor.locations.DELETE, this adds it.
// see https://github.com/ktorio/ktor/issues/288
import io.swagger.server.delete
import io.swagger.server.models.ApiResponse
import io.swagger.server.models.Pet
fun Route.PetApi() {
val gson = Gson()
val empty = mutableMapOf<String, Any?>()
route("/pet") {
post {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
call.respond(HttpStatusCode.NotImplemented)
}
}
}
.apply {
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
try {
authentication {
oauth(client, ApplicationExecutors.asCoroutineDispatcher(), { ApplicationAuthProviders["petstore_auth"] }, {
// TODO: define a callback url here.
"/"
})
}
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
application.environment.log.warn("authentication block for '/pet' is duplicated in code. " +
"Generated endpoints may need to be merged under a 'route' entry.")
}
}
delete<Paths.deletePet> { it: Paths.deletePet ->
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
call.respond(HttpStatusCode.NotImplemented)
}
}
.apply {
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
try {
authentication {
oauthAtLocation<Paths.deletePet>(client, ApplicationExecutors.asCoroutineDispatcher(),
providerLookup = { ApplicationAuthProviders["petstore_auth"] },
urlProvider = { currentLocation, provider ->
// TODO: define a callback url here.
"/"
})
}
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
application.environment.log.warn("authentication block for '/pet/{petId}' is duplicated in code. " +
"Generated endpoints may need to be merged under a 'route' entry.")
}
}
get<Paths.findPetsByStatus> { it: Paths.findPetsByStatus ->
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
val exampleContentType = "application/xml"
val exampleContentString = """<Pet>
<id>123456789</id>
<name>doggie</name>
<photoUrls>
<photoUrls>aeiou</photoUrls>
</photoUrls>
<tags>
</tags>
<status>aeiou</status>
</Pet>"""
when(exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
}
.apply {
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
try {
authentication {
oauthAtLocation<Paths.findPetsByStatus>(client, ApplicationExecutors.asCoroutineDispatcher(),
providerLookup = { ApplicationAuthProviders["petstore_auth"] },
urlProvider = { currentLocation, provider ->
// TODO: define a callback url here.
"/"
})
}
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
application.environment.log.warn("authentication block for '/pet/findByStatus' is duplicated in code. " +
"Generated endpoints may need to be merged under a 'route' entry.")
}
}
get<Paths.findPetsByTags> { it: Paths.findPetsByTags ->
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
val exampleContentType = "application/xml"
val exampleContentString = """<Pet>
<id>123456789</id>
<name>doggie</name>
<photoUrls>
<photoUrls>aeiou</photoUrls>
</photoUrls>
<tags>
</tags>
<status>aeiou</status>
</Pet>"""
when(exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
}
.apply {
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
try {
authentication {
oauthAtLocation<Paths.findPetsByTags>(client, ApplicationExecutors.asCoroutineDispatcher(),
providerLookup = { ApplicationAuthProviders["petstore_auth"] },
urlProvider = { currentLocation, provider ->
// TODO: define a callback url here.
"/"
})
}
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
application.environment.log.warn("authentication block for '/pet/findByTags' is duplicated in code. " +
"Generated endpoints may need to be merged under a 'route' entry.")
}
}
get<Paths.getPetById> { it: Paths.getPetById ->
val principal = call.authentication.principal<ApiPrincipal>()
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
val exampleContentType = "application/xml"
val exampleContentString = """<Pet>
<id>123456789</id>
<name>doggie</name>
<photoUrls>
<photoUrls>aeiou</photoUrls>
</photoUrls>
<tags>
</tags>
<status>aeiou</status>
</Pet>"""
when(exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
}
.apply {
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
try {
authentication {
// "Implement API key auth (api_key) for parameter name 'api_key'."
apiKeyAuth("api_key", "header") {
// TODO: "Verify key here , accessible as it.value"
if (it.value == "keyboardcat") {
ApiPrincipal(it)
} else {
null
}
}
}
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
application.environment.log.warn("authentication block for '/pet/{petId}' is duplicated in code. " +
"Generated endpoints may need to be merged under a 'route' entry.")
}
}
route("/pet") {
put {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
call.respond(HttpStatusCode.NotImplemented)
}
}
}
.apply {
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
try {
authentication {
oauth(client, ApplicationExecutors.asCoroutineDispatcher(), { ApplicationAuthProviders["petstore_auth"] }, {
// TODO: define a callback url here.
"/"
})
}
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
application.environment.log.warn("authentication block for '/pet' is duplicated in code. " +
"Generated endpoints may need to be merged under a 'route' entry.")
}
}
route("/pet/{petId}") {
post {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
call.respond(HttpStatusCode.NotImplemented)
}
}
}
.apply {
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
try {
authentication {
oauth(client, ApplicationExecutors.asCoroutineDispatcher(), { ApplicationAuthProviders["petstore_auth"] }, {
// TODO: define a callback url here.
"/"
})
}
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
application.environment.log.warn("authentication block for '/pet/{petId}' is duplicated in code. " +
"Generated endpoints may need to be merged under a 'route' entry.")
}
}
route("/pet/{petId}/uploadImage") {
post {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
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)
}
}
}
}
.apply {
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
try {
authentication {
oauth(client, ApplicationExecutors.asCoroutineDispatcher(), { ApplicationAuthProviders["petstore_auth"] }, {
// TODO: define a callback url here.
"/"
})
}
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
application.environment.log.warn("authentication block for '/pet/{petId}/uploadImage' is duplicated in code. " +
"Generated endpoints may need to be merged under a 'route' entry.")
}
}
}

View File

@@ -0,0 +1,132 @@
/**
* Swagger Petstore
* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.server.apis
import com.google.gson.Gson
import io.ktor.application.call
import io.ktor.auth.UserIdPrincipal
import io.ktor.auth.authentication
import io.ktor.auth.basicAuthentication
import io.ktor.auth.oauth
import io.ktor.auth.OAuthAccessTokenResponse
import io.ktor.auth.OAuthServerSettings
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.locations.*
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.*
import kotlinx.coroutines.experimental.asCoroutineDispatcher
import io.swagger.server.ApplicationAuthProviders
import io.swagger.server.Paths
import io.swagger.server.ApplicationExecutors
import io.swagger.server.HTTP.client
import io.swagger.server.infrastructure.ApiPrincipal
import io.swagger.server.infrastructure.apiKeyAuth
// ktor 0.9.x is missing io.ktor.locations.DELETE, this adds it.
// see https://github.com/ktorio/ktor/issues/288
import io.swagger.server.delete
import io.swagger.server.models.Order
fun Route.StoreApi() {
val gson = Gson()
val empty = mutableMapOf<String, Any?>()
delete<Paths.deleteOrder> { it: Paths.deleteOrder ->
call.respond(HttpStatusCode.NotImplemented)
}
get<Paths.getInventory> { it: Paths.getInventory ->
val principal = call.authentication.principal<ApiPrincipal>()
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
val exampleContentType = "application/json"
val exampleContentString = """{
"key" : 0
}"""
when(exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
}
.apply {
// TODO: ktor doesn't allow different authentication registrations for endpoints sharing the same path but different methods.
// It could be the authentication block is being abused here. Until this is resolved, swallow duplicate exceptions.
try {
authentication {
// "Implement API key auth (api_key) for parameter name 'api_key'."
apiKeyAuth("api_key", "header") {
// TODO: "Verify key here , accessible as it.value"
if (it.value == "keyboardcat") {
ApiPrincipal(it)
} else {
null
}
}
}
} catch(e: io.ktor.application.DuplicateApplicationFeatureException){
application.environment.log.warn("authentication block for '/store/inventory' is duplicated in code. " +
"Generated endpoints may need to be merged under a 'route' entry.")
}
}
get<Paths.getOrderById> { it: Paths.getOrderById ->
val exampleContentType = "application/xml"
val exampleContentString = """<Order>
<id>123456789</id>
<petId>123456789</petId>
<quantity>123</quantity>
<shipDate>2000-01-23T04:56:07.000Z</shipDate>
<status>aeiou</status>
<complete>true</complete>
</Order>"""
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/xml"
val exampleContentString = """<Order>
<id>123456789</id>
<petId>123456789</petId>
<quantity>123</quantity>
<shipDate>2000-01-23T04:56:07.000Z</shipDate>
<status>aeiou</status>
<complete>true</complete>
</Order>"""
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

@@ -0,0 +1,118 @@
/**
* Swagger Petstore
* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.server.apis
import com.google.gson.Gson
import io.ktor.application.call
import io.ktor.auth.UserIdPrincipal
import io.ktor.auth.authentication
import io.ktor.auth.basicAuthentication
import io.ktor.auth.oauth
import io.ktor.auth.OAuthAccessTokenResponse
import io.ktor.auth.OAuthServerSettings
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.locations.*
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.*
import kotlinx.coroutines.experimental.asCoroutineDispatcher
import io.swagger.server.ApplicationAuthProviders
import io.swagger.server.Paths
import io.swagger.server.ApplicationExecutors
import io.swagger.server.HTTP.client
import io.swagger.server.infrastructure.ApiPrincipal
import io.swagger.server.infrastructure.apiKeyAuth
// ktor 0.9.x is missing io.ktor.locations.DELETE, this adds it.
// see https://github.com/ktorio/ktor/issues/288
import io.swagger.server.delete
import io.swagger.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> { it: Paths.deleteUser ->
call.respond(HttpStatusCode.NotImplemented)
}
get<Paths.getUserByName> { it: Paths.getUserByName ->
val exampleContentType = "application/xml"
val exampleContentString = """<User>
<id>123456789</id>
<username>aeiou</username>
<firstName>aeiou</firstName>
<lastName>aeiou</lastName>
<email>aeiou</email>
<password>aeiou</password>
<phone>aeiou</phone>
<userStatus>123</userStatus>
</User>"""
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> { it: Paths.loginUser ->
val exampleContentType = "application/xml"
val exampleContentString = """aeiou"""
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.logoutUser> { it: Paths.logoutUser ->
call.respond(HttpStatusCode.NotImplemented)
}
route("/user/{username}") {
put {
call.respond(HttpStatusCode.NotImplemented)
}
}
}

View File

@@ -0,0 +1,57 @@
package io.swagger.server.infrastructure
import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.auth.*
import io.ktor.request.ApplicationRequest
import io.ktor.response.respond
import io.ktor.application.*
import io.ktor.pipeline.*
import io.ktor.request.*
import io.ktor.response.*
import java.util.*
enum class ApiKeyLocation(val location: String) {
QUERY("query"),
HEADER("header")
}
data class ApiKey(val value: String): Credential
data class ApiPrincipal(val apiKey: ApiKey?) : Principal
fun ApplicationCall.apiKey(key: String, keyLocation: ApiKeyLocation = ApiKeyLocation.valueOf("header")): ApiKey? = request.apiKey(key, keyLocation)
fun ApplicationRequest.apiKey(key: String, keyLocation: ApiKeyLocation = ApiKeyLocation.valueOf("header")): ApiKey? {
val value: String? = when(keyLocation) {
ApiKeyLocation.QUERY -> this.queryParameters[key]
ApiKeyLocation.HEADER -> this.headers[key]
}
when (value) {
null -> return null
else -> return ApiKey(value)
}
}
fun AuthenticationPipeline.apiKeyAuth(apiKeyName: String, authLocation: String, validate: suspend (ApiKey) -> ApiPrincipal?) {
intercept(AuthenticationPipeline.RequestAuthentication) { context ->
val credentials = call.request.apiKey(apiKeyName, ApiKeyLocation.values().first { it.location == authLocation })
val principal = credentials?.let { validate(it) }
val cause = when {
credentials == null -> AuthenticationFailedCause.NoCredentials
principal == null -> AuthenticationFailedCause.InvalidCredentials
else -> null
}
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)
}
}
}

View File

@@ -0,0 +1,28 @@
/**
* Swagger Petstore
* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.server.models
/**
* Describes the result of uploading an image resource
* @param code
* @param type
* @param message
*/
data class ApiResponse (
val code: kotlin.Int? = null,
val type: kotlin.String? = null,
val message: kotlin.String? = null
) {
}

View File

@@ -0,0 +1,26 @@
/**
* Swagger Petstore
* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.server.models
/**
* A category for a pet
* @param id
* @param name
*/
data class Category (
val id: kotlin.Long? = null,
val name: kotlin.String? = null
) {
}

View File

@@ -0,0 +1,49 @@
/**
* Swagger Petstore
* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.server.models
/**
* 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.LocalDateTime? = null,
/* Order Status */
val status: Order.Status? = null,
val complete: kotlin.Boolean? = null
) {
/**
* Order Status
* Values: placed,approved,delivered
*/
enum class Status(val value: kotlin.Any){
placed("placed"),
approved("approved"),
delivered("delivered");
}
}

View File

@@ -0,0 +1,51 @@
/**
* Swagger Petstore
* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.server.models
import io.swagger.server.models.Category
import io.swagger.server.models.Tag
/**
* A pet for sale in the pet store
* @param id
* @param category
* @param name
* @param photoUrls
* @param tags
* @param status pet status in the store
*/
data class Pet (
val name: kotlin.String,
val photoUrls: kotlin.Array<kotlin.String>,
val id: kotlin.Long? = null,
val category: Category? = null,
val tags: kotlin.Array<Tag>? = null,
/* pet status in the store */
val status: Pet.Status? = null
) {
/**
* pet status in the store
* Values: available,pending,sold
*/
enum class Status(val value: kotlin.Any){
available("available"),
pending("pending"),
sold("sold");
}
}

View File

@@ -0,0 +1,26 @@
/**
* Swagger Petstore
* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.server.models
/**
* A tag for a pet
* @param id
* @param name
*/
data class Tag (
val id: kotlin.Long? = null,
val name: kotlin.String? = null
) {
}

View File

@@ -0,0 +1,39 @@
/**
* Swagger Petstore
* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package io.swagger.server.models
/**
* 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
) {
}

View File

@@ -0,0 +1,23 @@
ktor {
deployment {
environment = development
port = 8080
autoreload = true
watch = [ io.swagger.server ]
}
application {
modules = [ io.swagger.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 = ""
}
}
}

View File

@@ -0,0 +1,15 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="trace">
<appender-ref ref="STDOUT"/>
</root>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="INFO"/>
</configuration>