Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
dd780ad29d | |||
d2165d23bf | |||
d9d0f129b5 |
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,5 +1,18 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.10.0] - November 25th, 2021
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Added `operationId` parameter to `MethodInfo`
|
||||||
|
|
||||||
|
## [1.9.2] - October 24th, 2021
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Jackson ObjectMapper passed by parameter to openapi module
|
||||||
|
- Added serializable annotation to ExceptionResponse
|
||||||
|
|
||||||
## [1.9.1] - October 17th, 2021
|
## [1.9.1] - October 17th, 2021
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
32
README.md
32
README.md
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
## What is Kompendium
|
## What is Kompendium
|
||||||
|
|
||||||
|
### ⚠️ For info on V2 please see [here](#V2)
|
||||||
|
|
||||||
Kompendium is intended to be a minimally invasive OpenApi Specification generator for [Ktor](https://ktor.io).
|
Kompendium is intended to be a minimally invasive OpenApi Specification generator for [Ktor](https://ktor.io).
|
||||||
Minimally invasive meaning that users will use only Ktor native functions when implementing their API, and will
|
Minimally invasive meaning that users will use only Ktor native functions when implementing their API, and will
|
||||||
supplement with Kompendium code in order to generate the appropriate spec.
|
supplement with Kompendium code in order to generate the appropriate spec.
|
||||||
@ -126,6 +128,29 @@ suggestions on better implementations are welcome 🤠
|
|||||||
Under the hood, Kompendium uses Jackson to serialize the final api spec. However, this implementation detail
|
Under the hood, Kompendium uses Jackson to serialize the final api spec. However, this implementation detail
|
||||||
does not leak to the actual API, meaning that users are free to choose the serialization library of their choice.
|
does not leak to the actual API, meaning that users are free to choose the serialization library of their choice.
|
||||||
|
|
||||||
|
Added the possibility to add your own ObjectMapper for Jackson.
|
||||||
|
|
||||||
|
Added a default parameter with the following configuration:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
ObjectMapper()
|
||||||
|
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||||
|
.enable(SerializationFeature.INDENT_OUTPUT)
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to change this default configuration and use your own ObjectMapper you only need to pass it as a second argument to the openApi module:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
routing {
|
||||||
|
openApi(oas, objectMapper)
|
||||||
|
route("/potato/spud") {
|
||||||
|
notarizedGet(simpleGetInfo) {
|
||||||
|
call.respond(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Route Handling
|
### Route Handling
|
||||||
|
|
||||||
> ⚠️ Warning: Custom route handling is almost definitely an indication that either a new selector should be added to kompendium-core or that kompendium is in need of another module to handle a new ktor companion module. If you have encountered a route selector that is not already handled, please consider opening an [issue](https://github.com/bkbnio/kompendium/issues/new)
|
> ⚠️ Warning: Custom route handling is almost definitely an indication that either a new selector should be added to kompendium-core or that kompendium is in need of another module to handle a new ktor companion module. If you have encountered a route selector that is not already handled, please consider opening an [issue](https://github.com/bkbnio/kompendium/issues/new)
|
||||||
@ -185,6 +210,7 @@ fun Application.mainModule() {
|
|||||||
val simpleGetInfo = GetInfo<Unit, ExampleResponse>(
|
val simpleGetInfo = GetInfo<Unit, ExampleResponse>(
|
||||||
summary = "Example Parameters",
|
summary = "Example Parameters",
|
||||||
description = "A test for setting parameter examples",
|
description = "A test for setting parameter examples",
|
||||||
|
operationId = "getExamples",
|
||||||
responseInfo = ResponseInfo(
|
responseInfo = ResponseInfo(
|
||||||
status = 200,
|
status = 200,
|
||||||
description = "nice",
|
description = "nice",
|
||||||
@ -302,3 +328,9 @@ should have. There are several outstanding features that have been added to the
|
|||||||
|
|
||||||
If you have a feature that you would like to see implemented that is not on this list, or discover a 🐞, please open
|
If you have a feature that you would like to see implemented that is not on this list, or discover a 🐞, please open
|
||||||
an issue [here](https://github.com/bkbnio/kompendium/issues/new)
|
an issue [here](https://github.com/bkbnio/kompendium/issues/new)
|
||||||
|
|
||||||
|
### V2
|
||||||
|
|
||||||
|
Due to the large number of breaking changes that will be made in version 2, development is currently being done on the
|
||||||
|
long-lived `v2` feature branch. If you are working on any feature in the `V2` milestone, please target that branch!
|
||||||
|
If you are unsure where your changes should be, please open an issue first :)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Kompendium
|
# Kompendium
|
||||||
project.version=1.9.1
|
project.version=1.10.0
|
||||||
# Kotlin
|
# Kotlin
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
# Gradle
|
# Gradle
|
||||||
|
@ -49,6 +49,7 @@ object MethodParser {
|
|||||||
) = OpenApiSpecPathItemOperation(
|
) = OpenApiSpecPathItemOperation(
|
||||||
summary = info.summary,
|
summary = info.summary,
|
||||||
description = info.description,
|
description = info.description,
|
||||||
|
operationId = info.operationId,
|
||||||
tags = info.tags,
|
tags = info.tags,
|
||||||
deprecated = info.deprecated,
|
deprecated = info.deprecated,
|
||||||
parameters = paramType.toParameterSpec(),
|
parameters = paramType.toParameterSpec(),
|
||||||
|
@ -11,6 +11,7 @@ sealed class MethodInfo<TParam, TResp>(
|
|||||||
open val canThrow: Set<KClass<*>> = emptySet(),
|
open val canThrow: Set<KClass<*>> = emptySet(),
|
||||||
open val responseInfo: ResponseInfo<TResp>? = null,
|
open val responseInfo: ResponseInfo<TResp>? = null,
|
||||||
open val parameterExamples: Map<String, TParam> = emptyMap(),
|
open val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||||
|
open val operationId: String? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
data class GetInfo<TParam, TResp>(
|
data class GetInfo<TParam, TResp>(
|
||||||
@ -21,7 +22,8 @@ sealed class MethodInfo<TParam, TResp>(
|
|||||||
override val deprecated: Boolean = false,
|
override val deprecated: Boolean = false,
|
||||||
override val securitySchemes: Set<String> = emptySet(),
|
override val securitySchemes: Set<String> = emptySet(),
|
||||||
override val canThrow: Set<KClass<*>> = emptySet(),
|
override val canThrow: Set<KClass<*>> = emptySet(),
|
||||||
override val parameterExamples: Map<String, TParam> = emptyMap()
|
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||||
|
override val operationId: String? = null
|
||||||
) : MethodInfo<TParam, TResp>(
|
) : MethodInfo<TParam, TResp>(
|
||||||
summary = summary,
|
summary = summary,
|
||||||
description = description,
|
description = description,
|
||||||
@ -30,7 +32,8 @@ sealed class MethodInfo<TParam, TResp>(
|
|||||||
securitySchemes = securitySchemes,
|
securitySchemes = securitySchemes,
|
||||||
canThrow = canThrow,
|
canThrow = canThrow,
|
||||||
responseInfo = responseInfo,
|
responseInfo = responseInfo,
|
||||||
parameterExamples = parameterExamples
|
parameterExamples = parameterExamples,
|
||||||
|
operationId = operationId
|
||||||
)
|
)
|
||||||
|
|
||||||
data class PostInfo<TParam, TReq, TResp>(
|
data class PostInfo<TParam, TReq, TResp>(
|
||||||
@ -42,7 +45,8 @@ sealed class MethodInfo<TParam, TResp>(
|
|||||||
override val deprecated: Boolean = false,
|
override val deprecated: Boolean = false,
|
||||||
override val securitySchemes: Set<String> = emptySet(),
|
override val securitySchemes: Set<String> = emptySet(),
|
||||||
override val canThrow: Set<KClass<*>> = emptySet(),
|
override val canThrow: Set<KClass<*>> = emptySet(),
|
||||||
override val parameterExamples: Map<String, TParam> = emptyMap()
|
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||||
|
override val operationId: String? = null
|
||||||
) : MethodInfo<TParam, TResp>(
|
) : MethodInfo<TParam, TResp>(
|
||||||
summary = summary,
|
summary = summary,
|
||||||
description = description,
|
description = description,
|
||||||
@ -51,7 +55,8 @@ sealed class MethodInfo<TParam, TResp>(
|
|||||||
securitySchemes = securitySchemes,
|
securitySchemes = securitySchemes,
|
||||||
canThrow = canThrow,
|
canThrow = canThrow,
|
||||||
responseInfo = responseInfo,
|
responseInfo = responseInfo,
|
||||||
parameterExamples = parameterExamples
|
parameterExamples = parameterExamples,
|
||||||
|
operationId = operationId
|
||||||
)
|
)
|
||||||
|
|
||||||
data class PutInfo<TParam, TReq, TResp>(
|
data class PutInfo<TParam, TReq, TResp>(
|
||||||
@ -63,7 +68,8 @@ sealed class MethodInfo<TParam, TResp>(
|
|||||||
override val deprecated: Boolean = false,
|
override val deprecated: Boolean = false,
|
||||||
override val securitySchemes: Set<String> = emptySet(),
|
override val securitySchemes: Set<String> = emptySet(),
|
||||||
override val canThrow: Set<KClass<*>> = emptySet(),
|
override val canThrow: Set<KClass<*>> = emptySet(),
|
||||||
override val parameterExamples: Map<String, TParam> = emptyMap()
|
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||||
|
override val operationId: String? = null
|
||||||
) : MethodInfo<TParam, TResp>(
|
) : MethodInfo<TParam, TResp>(
|
||||||
summary = summary,
|
summary = summary,
|
||||||
description = description,
|
description = description,
|
||||||
@ -71,7 +77,8 @@ sealed class MethodInfo<TParam, TResp>(
|
|||||||
deprecated = deprecated,
|
deprecated = deprecated,
|
||||||
securitySchemes = securitySchemes,
|
securitySchemes = securitySchemes,
|
||||||
canThrow = canThrow,
|
canThrow = canThrow,
|
||||||
parameterExamples = parameterExamples
|
parameterExamples = parameterExamples,
|
||||||
|
operationId = operationId
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DeleteInfo<TParam, TResp>(
|
data class DeleteInfo<TParam, TResp>(
|
||||||
@ -82,7 +89,8 @@ sealed class MethodInfo<TParam, TResp>(
|
|||||||
override val deprecated: Boolean = false,
|
override val deprecated: Boolean = false,
|
||||||
override val securitySchemes: Set<String> = emptySet(),
|
override val securitySchemes: Set<String> = emptySet(),
|
||||||
override val canThrow: Set<KClass<*>> = emptySet(),
|
override val canThrow: Set<KClass<*>> = emptySet(),
|
||||||
override val parameterExamples: Map<String, TParam> = emptyMap()
|
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||||
|
override val operationId: String? = null
|
||||||
) : MethodInfo<TParam, TResp>(
|
) : MethodInfo<TParam, TResp>(
|
||||||
summary = summary,
|
summary = summary,
|
||||||
description = description,
|
description = description,
|
||||||
@ -90,6 +98,7 @@ sealed class MethodInfo<TParam, TResp>(
|
|||||||
deprecated = deprecated,
|
deprecated = deprecated,
|
||||||
securitySchemes = securitySchemes,
|
securitySchemes = securitySchemes,
|
||||||
canThrow = canThrow,
|
canThrow = canThrow,
|
||||||
parameterExamples = parameterExamples
|
parameterExamples = parameterExamples,
|
||||||
|
operationId = operationId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,19 @@ import io.ktor.routing.route
|
|||||||
/**
|
/**
|
||||||
* Provides an out-of-the-box route to return the generated [OpenApiSpec]
|
* Provides an out-of-the-box route to return the generated [OpenApiSpec]
|
||||||
* @param oas spec that is returned
|
* @param oas spec that is returned
|
||||||
|
* @param om provider for Jackson
|
||||||
*/
|
*/
|
||||||
fun Routing.openApi(oas: OpenApiSpec) {
|
fun Routing.openApi(
|
||||||
val om = ObjectMapper()
|
oas: OpenApiSpec,
|
||||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
om: ObjectMapper = objectMapper
|
||||||
.enable(SerializationFeature.INDENT_OUTPUT)
|
) {
|
||||||
route("/openapi.json") {
|
route("/openapi.json") {
|
||||||
get {
|
get {
|
||||||
call.respondText { om.writeValueAsString(oas) }
|
call.respondText { om.writeValueAsString(oas) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val objectMapper = ObjectMapper()
|
||||||
|
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||||
|
.enable(SerializationFeature.INDENT_OUTPUT)
|
||||||
|
@ -46,6 +46,7 @@ import io.bkbn.kompendium.util.trailingSlash
|
|||||||
import io.bkbn.kompendium.util.undeclaredType
|
import io.bkbn.kompendium.util.undeclaredType
|
||||||
import io.bkbn.kompendium.util.withDefaultParameter
|
import io.bkbn.kompendium.util.withDefaultParameter
|
||||||
import io.bkbn.kompendium.util.withExamples
|
import io.bkbn.kompendium.util.withExamples
|
||||||
|
import io.bkbn.kompendium.util.withOperationId
|
||||||
|
|
||||||
internal class KompendiumTest {
|
internal class KompendiumTest {
|
||||||
|
|
||||||
@ -139,7 +140,6 @@ internal class KompendiumTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Notarized put does not interrupt the pipeline`() {
|
fun `Notarized put does not interrupt the pipeline`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
@ -363,6 +363,22 @@ internal class KompendiumTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Can add operationId`() {
|
||||||
|
withTestApplication({
|
||||||
|
jacksonConfigModule()
|
||||||
|
docs()
|
||||||
|
withOperationId()
|
||||||
|
}) {
|
||||||
|
// do
|
||||||
|
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||||
|
|
||||||
|
// expect
|
||||||
|
val expected = getFileSnapshot("notarized_get_with_operation_id.json").trim()
|
||||||
|
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Generates the expected redoc`() {
|
fun `Generates the expected redoc`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
@ -607,5 +623,4 @@ internal class KompendiumTest {
|
|||||||
redoc(oas)
|
redoc(oas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -265,6 +265,18 @@ fun Application.withDefaultParameter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Application.withOperationId(){
|
||||||
|
routing {
|
||||||
|
route("/test") {
|
||||||
|
notarizedGet(
|
||||||
|
info = TestResponseInfo.testGetInfo.copy(operationId = "getTest")
|
||||||
|
){
|
||||||
|
call.respond(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Application.nonRequiredParamsGet() {
|
fun Application.nonRequiredParamsGet() {
|
||||||
routing {
|
routing {
|
||||||
route("/test/optional") {
|
route("/test/optional") {
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"openapi" : "3.0.3",
|
||||||
|
"info" : {
|
||||||
|
"title" : "Test API",
|
||||||
|
"version" : "1.33.7",
|
||||||
|
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||||
|
"termsOfService" : "https://example.com",
|
||||||
|
"contact" : {
|
||||||
|
"name" : "Homer Simpson",
|
||||||
|
"url" : "https://gph.is/1NPUDiM",
|
||||||
|
"email" : "chunkylover53@aol.com"
|
||||||
|
},
|
||||||
|
"license" : {
|
||||||
|
"name" : "MIT",
|
||||||
|
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"servers" : [ {
|
||||||
|
"url" : "https://myawesomeapi.com",
|
||||||
|
"description" : "Production instance of my API"
|
||||||
|
}, {
|
||||||
|
"url" : "https://staging.myawesomeapi.com",
|
||||||
|
"description" : "Where the fun stuff happens"
|
||||||
|
} ],
|
||||||
|
"paths" : {
|
||||||
|
"/test" : {
|
||||||
|
"get" : {
|
||||||
|
"tags" : [ ],
|
||||||
|
"summary" : "Another get test",
|
||||||
|
"description" : "testing more",
|
||||||
|
"operationId" : "getTest",
|
||||||
|
"parameters" : [ {
|
||||||
|
"name" : "a",
|
||||||
|
"in" : "path",
|
||||||
|
"schema" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"required" : true,
|
||||||
|
"deprecated" : false
|
||||||
|
}, {
|
||||||
|
"name" : "aa",
|
||||||
|
"in" : "query",
|
||||||
|
"schema" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"format" : "int32"
|
||||||
|
},
|
||||||
|
"required" : true,
|
||||||
|
"deprecated" : false
|
||||||
|
} ],
|
||||||
|
"responses" : {
|
||||||
|
"200" : {
|
||||||
|
"description" : "A Successful Endeavor",
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"$ref" : "#/components/schemas/TestResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated" : false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components" : {
|
||||||
|
"schemas" : {
|
||||||
|
"String" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"TestResponse" : {
|
||||||
|
"type" : "object",
|
||||||
|
"properties" : {
|
||||||
|
"c" : {
|
||||||
|
"$ref" : "#/components/schemas/String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Int" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"format" : "int32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes" : { }
|
||||||
|
},
|
||||||
|
"security" : [ ],
|
||||||
|
"tags" : [ ]
|
||||||
|
}
|
@ -4,6 +4,7 @@ import io.bkbn.kompendium.annotations.KompendiumField
|
|||||||
import io.bkbn.kompendium.annotations.KompendiumParam
|
import io.bkbn.kompendium.annotations.KompendiumParam
|
||||||
import io.bkbn.kompendium.annotations.ParamType
|
import io.bkbn.kompendium.annotations.ParamType
|
||||||
import io.bkbn.kompendium.annotations.UndeclaredField
|
import io.bkbn.kompendium.annotations.UndeclaredField
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
|
|
||||||
data class ExampleParams(
|
data class ExampleParams(
|
||||||
@ -27,6 +28,7 @@ data class ExampleRequest(
|
|||||||
val aaa: List<Long>
|
val aaa: List<Long>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ExampleResponse(val c: String)
|
data class ExampleResponse(val c: String)
|
||||||
|
|
||||||
data class ExceptionResponse(val message: String)
|
data class ExceptionResponse(val message: String)
|
||||||
|
Reference in New Issue
Block a user