Compare commits

...

3 Commits

10 changed files with 234 additions and 57 deletions

View File

@ -1,5 +1,18 @@
# 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
### Changed

View File

@ -6,6 +6,8 @@
## 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).
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.
@ -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
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
> ⚠️ 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>(
summary = "Example Parameters",
description = "A test for setting parameter examples",
operationId = "getExamples",
responseInfo = ResponseInfo(
status = 200,
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
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 :)

View File

@ -1,5 +1,5 @@
# Kompendium
project.version=1.9.1
project.version=1.10.0
# Kotlin
kotlin.code.style=official
# Gradle

View File

@ -49,6 +49,7 @@ object MethodParser {
) = OpenApiSpecPathItemOperation(
summary = info.summary,
description = info.description,
operationId = info.operationId,
tags = info.tags,
deprecated = info.deprecated,
parameters = paramType.toParameterSpec(),

View File

@ -11,6 +11,7 @@ sealed class MethodInfo<TParam, TResp>(
open val canThrow: Set<KClass<*>> = emptySet(),
open val responseInfo: ResponseInfo<TResp>? = null,
open val parameterExamples: Map<String, TParam> = emptyMap(),
open val operationId: String? = null
) {
data class GetInfo<TParam, TResp>(
@ -21,7 +22,8 @@ sealed class MethodInfo<TParam, TResp>(
override val deprecated: Boolean = false,
override val securitySchemes: Set<String> = 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>(
summary = summary,
description = description,
@ -30,7 +32,8 @@ sealed class MethodInfo<TParam, TResp>(
securitySchemes = securitySchemes,
canThrow = canThrow,
responseInfo = responseInfo,
parameterExamples = parameterExamples
parameterExamples = parameterExamples,
operationId = operationId
)
data class PostInfo<TParam, TReq, TResp>(
@ -42,7 +45,8 @@ sealed class MethodInfo<TParam, TResp>(
override val deprecated: Boolean = false,
override val securitySchemes: Set<String> = 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>(
summary = summary,
description = description,
@ -51,7 +55,8 @@ sealed class MethodInfo<TParam, TResp>(
securitySchemes = securitySchemes,
canThrow = canThrow,
responseInfo = responseInfo,
parameterExamples = parameterExamples
parameterExamples = parameterExamples,
operationId = operationId
)
data class PutInfo<TParam, TReq, TResp>(
@ -63,7 +68,8 @@ sealed class MethodInfo<TParam, TResp>(
override val deprecated: Boolean = false,
override val securitySchemes: Set<String> = 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>(
summary = summary,
description = description,
@ -71,7 +77,8 @@ sealed class MethodInfo<TParam, TResp>(
deprecated = deprecated,
securitySchemes = securitySchemes,
canThrow = canThrow,
parameterExamples = parameterExamples
parameterExamples = parameterExamples,
operationId = operationId
)
data class DeleteInfo<TParam, TResp>(
@ -82,7 +89,8 @@ sealed class MethodInfo<TParam, TResp>(
override val deprecated: Boolean = false,
override val securitySchemes: Set<String> = 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>(
summary = summary,
description = description,
@ -90,6 +98,7 @@ sealed class MethodInfo<TParam, TResp>(
deprecated = deprecated,
securitySchemes = securitySchemes,
canThrow = canThrow,
parameterExamples = parameterExamples
parameterExamples = parameterExamples,
operationId = operationId
)
}

View File

@ -13,14 +13,19 @@ import io.ktor.routing.route
/**
* Provides an out-of-the-box route to return the generated [OpenApiSpec]
* @param oas spec that is returned
* @param om provider for Jackson
*/
fun Routing.openApi(oas: OpenApiSpec) {
val om = ObjectMapper()
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.enable(SerializationFeature.INDENT_OUTPUT)
fun Routing.openApi(
oas: OpenApiSpec,
om: ObjectMapper = objectMapper
) {
route("/openapi.json") {
get {
call.respondText { om.writeValueAsString(oas) }
}
}
}
private val objectMapper = ObjectMapper()
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.enable(SerializationFeature.INDENT_OUTPUT)

View File

@ -46,6 +46,7 @@ import io.bkbn.kompendium.util.trailingSlash
import io.bkbn.kompendium.util.undeclaredType
import io.bkbn.kompendium.util.withDefaultParameter
import io.bkbn.kompendium.util.withExamples
import io.bkbn.kompendium.util.withOperationId
internal class KompendiumTest {
@ -139,7 +140,6 @@ internal class KompendiumTest {
}
}
@Test
fun `Notarized put does not interrupt the pipeline`() {
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
fun `Generates the expected redoc`() {
withTestApplication({
@ -607,5 +623,4 @@ internal class KompendiumTest {
redoc(oas)
}
}
}

View File

@ -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() {
routing {
route("/test/optional") {

View File

@ -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" : [ ]
}

View File

@ -4,6 +4,7 @@ import io.bkbn.kompendium.annotations.KompendiumField
import io.bkbn.kompendium.annotations.KompendiumParam
import io.bkbn.kompendium.annotations.ParamType
import io.bkbn.kompendium.annotations.UndeclaredField
import kotlinx.serialization.Serializable
import org.joda.time.DateTime
data class ExampleParams(
@ -27,6 +28,7 @@ data class ExampleRequest(
val aaa: List<Long>
)
@Serializable
data class ExampleResponse(val c: String)
data class ExceptionResponse(val message: String)