Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
bf6d08c2bd | |||
68bae4918e | |||
592c116c3b | |||
c7fe5c288f | |||
e059633055 | |||
884a50fc83 | |||
588e52c9df | |||
f49fcb2a22 | |||
0a9475a7ab | |||
f8fbb7ad25 | |||
f792fb5d1f | |||
845d1a971d | |||
9396b2ecfe | |||
d74b7b3f28 | |||
e783630845 | |||
d907ba004a | |||
5c7ec4fdb4 | |||
097413067b | |||
eb7360b8d2 | |||
3ec467c1d8 | |||
152476c26e | |||
c4df4c7b2c | |||
87fa0b5602 | |||
53c76d6037 | |||
44324ca3a4 | |||
9364fb19e4 | |||
ca1f632665 | |||
9bb3184735 | |||
9a139b5713 | |||
9861c8e258 | |||
02f8ed04f2 | |||
3e23939386 | |||
d0575944db | |||
ccc81c2813 | |||
e8da52fd75 | |||
34c14e26da | |||
209b5e38a3 | |||
902faa9471 | |||
c7ef70a844 | |||
f83c3d203d | |||
bd05dbcfcb | |||
cb5c0e71a3 | |||
a209cafa74 | |||
3d27d30a31 | |||
8cc229667e | |||
54d12de67a | |||
9b93c887a2 | |||
19d828956b | |||
eabe90acfc | |||
7c2a2a9c9d | |||
15cdfb229d | |||
5a40f37f81 | |||
64f2516f19 | |||
2e5e39d3b2 | |||
5342cf00d1 | |||
8c0b658033 | |||
b7b1171685 | |||
1f730869a8 | |||
377a60614e | |||
e34bea1769 |
48
CHANGELOG.md
48
CHANGELOG.md
@ -12,6 +12,54 @@
|
|||||||
|
|
||||||
## Released
|
## Released
|
||||||
|
|
||||||
|
## [3.14.4] - June 5th, 2023
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Components definitions were not in the proper schema section. Prefixed the path with component slug in `protobuf java converter`.
|
||||||
|
|
||||||
|
## [3.14.3] - May 22nd, 2023
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `required` parameter in request info builder.
|
||||||
|
|
||||||
|
## [3.14.2] - May 8rd, 2023
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Fixed bug where routes were not resolving when a parameter was declared via the ktor SDK
|
||||||
|
|
||||||
|
## [3.14.1] - April 28th, 2023
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Generating enrichments for generic classes no longer throws the `Slugs should not be generated for field enrichments` error.
|
||||||
|
|
||||||
|
## [3.14.0] - April 6th, 2023
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add support for response headers
|
||||||
|
|
||||||
|
## [3.13.0] - March 15th, 2023
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Post, Put, and Patch now support not providing request info
|
||||||
|
|
||||||
|
## [3.12.0] - March 14th, 2023
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add support for swagger documentation
|
||||||
|
|
||||||
|
## [3.11.0] - January 5th, 2023
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Support for type constraints.
|
||||||
|
|
||||||
## [3.10.0] - January 4th, 2023
|
## [3.10.0] - January 4th, 2023
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -3,3 +3,7 @@
|
|||||||
[](https://search.maven.org/search?q=io.bkbn%20kompendium)
|
[](https://search.maven.org/search?q=io.bkbn%20kompendium)
|
||||||
|
|
||||||
Documentation is stored in the `docs` folder and is hosted [here](https://bkbn.gitbook.io/kompendium)
|
Documentation is stored in the `docs` folder and is hosted [here](https://bkbn.gitbook.io/kompendium)
|
||||||
|
|
||||||
|
## Showcase
|
||||||
|
|
||||||
|
If you would like to showcase docs that you build using Kompendium, add them to [the showcase discussion](https://github.com/bkbnio/kompendium/discussions/444)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.8.0" apply false
|
kotlin("jvm") version "1.8.21" apply false
|
||||||
kotlin("plugin.serialization") version "1.8.0" apply false
|
kotlin("plugin.serialization") version "1.8.21" apply false
|
||||||
id("io.bkbn.sourdough.library.jvm") version "0.12.0" apply false
|
id("io.bkbn.sourdough.library.jvm") version "0.12.0" apply false
|
||||||
id("io.bkbn.sourdough.application.jvm") version "0.12.0" apply false
|
id("io.bkbn.sourdough.application.jvm") version "0.12.0" apply false
|
||||||
id("io.bkbn.sourdough.root") version "0.12.0"
|
id("io.bkbn.sourdough.root") version "0.12.0"
|
||||||
id("com.github.jakemarsden.git-hooks") version "0.0.2"
|
id("com.github.jakemarsden.git-hooks") version "0.0.2"
|
||||||
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
||||||
id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
|
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
gitHooks {
|
gitHooks {
|
||||||
|
@ -33,7 +33,6 @@ dependencies {
|
|||||||
implementation("io.ktor:ktor-server-html-builder:$ktorVersion")
|
implementation("io.ktor:ktor-server-html-builder:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
|
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
|
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
|
||||||
implementation("ch.qos.logback:logback-classic:1.4.5")
|
|
||||||
|
|
||||||
// Formatting
|
// Formatting
|
||||||
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion")
|
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion")
|
||||||
@ -58,9 +57,9 @@ dependencies {
|
|||||||
testFixturesApi("io.ktor:ktor-client:$ktorVersion")
|
testFixturesApi("io.ktor:ktor-client:$ktorVersion")
|
||||||
testFixturesApi("io.ktor:ktor-client-cio:$ktorVersion")
|
testFixturesApi("io.ktor:ktor-client-cio:$ktorVersion")
|
||||||
|
|
||||||
testFixturesApi("dev.forst:ktor-api-key:2.1.3")
|
testFixturesApi("dev.forst:ktor-api-key:2.2.4")
|
||||||
|
|
||||||
testFixturesApi("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
|
testFixturesApi("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
testing {
|
testing {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package io.bkbn.kompendium.core.metadata
|
package io.bkbn.kompendium.core.metadata
|
||||||
|
|
||||||
sealed interface MethodInfoWithRequest : MethodInfo {
|
sealed interface MethodInfoWithRequest : MethodInfo {
|
||||||
val request: RequestInfo
|
val request: RequestInfo?
|
||||||
|
|
||||||
abstract class Builder<T : MethodInfoWithRequest> : MethodInfo.Builder<T>() {
|
abstract class Builder<T : MethodInfoWithRequest> : MethodInfo.Builder<T>() {
|
||||||
internal var request: RequestInfo? = null
|
internal var request: RequestInfo? = null
|
||||||
|
@ -4,7 +4,7 @@ import io.bkbn.kompendium.oas.common.ExternalDocumentation
|
|||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
|
|
||||||
class PatchInfo private constructor(
|
class PatchInfo private constructor(
|
||||||
override val request: RequestInfo,
|
override val request: RequestInfo?,
|
||||||
override val errors: MutableList<ResponseInfo>,
|
override val errors: MutableList<ResponseInfo>,
|
||||||
override val response: ResponseInfo,
|
override val response: ResponseInfo,
|
||||||
override val tags: Set<String>,
|
override val tags: Set<String>,
|
||||||
@ -26,7 +26,7 @@ class PatchInfo private constructor(
|
|||||||
|
|
||||||
class Builder : MethodInfoWithRequest.Builder<PatchInfo>() {
|
class Builder : MethodInfoWithRequest.Builder<PatchInfo>() {
|
||||||
override fun build() = PatchInfo(
|
override fun build() = PatchInfo(
|
||||||
request = request ?: error("request info must be present"),
|
request = request,
|
||||||
errors = errors,
|
errors = errors,
|
||||||
response = response ?: error("response info must be present"),
|
response = response ?: error("response info must be present"),
|
||||||
tags = tags,
|
tags = tags,
|
||||||
|
@ -4,7 +4,7 @@ import io.bkbn.kompendium.oas.common.ExternalDocumentation
|
|||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
|
|
||||||
class PostInfo private constructor(
|
class PostInfo private constructor(
|
||||||
override val request: RequestInfo,
|
override val request: RequestInfo?,
|
||||||
override val errors: MutableList<ResponseInfo>,
|
override val errors: MutableList<ResponseInfo>,
|
||||||
override val response: ResponseInfo,
|
override val response: ResponseInfo,
|
||||||
override val tags: Set<String>,
|
override val tags: Set<String>,
|
||||||
@ -26,7 +26,7 @@ class PostInfo private constructor(
|
|||||||
|
|
||||||
class Builder : MethodInfoWithRequest.Builder<PostInfo>() {
|
class Builder : MethodInfoWithRequest.Builder<PostInfo>() {
|
||||||
override fun build() = PostInfo(
|
override fun build() = PostInfo(
|
||||||
request = request ?: error("request info must be present"),
|
request = request,
|
||||||
errors = errors,
|
errors = errors,
|
||||||
response = response ?: error("response info must be present"),
|
response = response ?: error("response info must be present"),
|
||||||
tags = tags,
|
tags = tags,
|
||||||
|
@ -4,7 +4,7 @@ import io.bkbn.kompendium.oas.common.ExternalDocumentation
|
|||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
|
|
||||||
class PutInfo private constructor(
|
class PutInfo private constructor(
|
||||||
override val request: RequestInfo,
|
override val request: RequestInfo?,
|
||||||
override val errors: MutableList<ResponseInfo>,
|
override val errors: MutableList<ResponseInfo>,
|
||||||
override val response: ResponseInfo,
|
override val response: ResponseInfo,
|
||||||
override val tags: Set<String>,
|
override val tags: Set<String>,
|
||||||
@ -26,7 +26,7 @@ class PutInfo private constructor(
|
|||||||
|
|
||||||
class Builder : MethodInfoWithRequest.Builder<PutInfo>() {
|
class Builder : MethodInfoWithRequest.Builder<PutInfo>() {
|
||||||
override fun build() = PutInfo(
|
override fun build() = PutInfo(
|
||||||
request = request ?: error("request info must be present"),
|
request = request,
|
||||||
errors = errors,
|
errors = errors,
|
||||||
response = response ?: error("response info must be present"),
|
response = response ?: error("response info must be present"),
|
||||||
tags = tags,
|
tags = tags,
|
||||||
|
@ -10,7 +10,8 @@ class RequestInfo private constructor(
|
|||||||
val typeEnrichment: TypeEnrichment<*>?,
|
val typeEnrichment: TypeEnrichment<*>?,
|
||||||
val description: String,
|
val description: String,
|
||||||
val examples: Map<String, MediaType.Example>?,
|
val examples: Map<String, MediaType.Example>?,
|
||||||
val mediaTypes: Set<String>
|
val mediaTypes: Set<String>,
|
||||||
|
val required: Boolean
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -27,6 +28,11 @@ class RequestInfo private constructor(
|
|||||||
private var description: String? = null
|
private var description: String? = null
|
||||||
private var examples: Map<String, MediaType.Example>? = null
|
private var examples: Map<String, MediaType.Example>? = null
|
||||||
private var mediaTypes: Set<String>? = null
|
private var mediaTypes: Set<String>? = null
|
||||||
|
private var required: Boolean? = null
|
||||||
|
|
||||||
|
fun required(r: Boolean) = apply {
|
||||||
|
this.required = r
|
||||||
|
}
|
||||||
|
|
||||||
fun requestType(t: KType) = apply {
|
fun requestType(t: KType) = apply {
|
||||||
this.requestType = t
|
this.requestType = t
|
||||||
@ -56,7 +62,8 @@ class RequestInfo private constructor(
|
|||||||
description = description ?: error("Description must be present"),
|
description = description ?: error("Description must be present"),
|
||||||
typeEnrichment = typeEnrichment,
|
typeEnrichment = typeEnrichment,
|
||||||
examples = examples,
|
examples = examples,
|
||||||
mediaTypes = mediaTypes ?: setOf("application/json")
|
mediaTypes = mediaTypes ?: setOf("application/json"),
|
||||||
|
required = required ?: true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.bkbn.kompendium.core.metadata
|
package io.bkbn.kompendium.core.metadata
|
||||||
|
|
||||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||||
|
import io.bkbn.kompendium.oas.payload.Header
|
||||||
import io.bkbn.kompendium.oas.payload.MediaType
|
import io.bkbn.kompendium.oas.payload.MediaType
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
@ -12,7 +13,8 @@ class ResponseInfo private constructor(
|
|||||||
val typeEnrichment: TypeEnrichment<*>?,
|
val typeEnrichment: TypeEnrichment<*>?,
|
||||||
val description: String,
|
val description: String,
|
||||||
val examples: Map<String, MediaType.Example>?,
|
val examples: Map<String, MediaType.Example>?,
|
||||||
val mediaTypes: Set<String>
|
val mediaTypes: Set<String>,
|
||||||
|
val responseHeaders: Map<String, Header>?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -30,6 +32,11 @@ class ResponseInfo private constructor(
|
|||||||
private var description: String? = null
|
private var description: String? = null
|
||||||
private var examples: Map<String, MediaType.Example>? = null
|
private var examples: Map<String, MediaType.Example>? = null
|
||||||
private var mediaTypes: Set<String>? = null
|
private var mediaTypes: Set<String>? = null
|
||||||
|
private var responseHeaders: Map<String, Header>? = null
|
||||||
|
|
||||||
|
fun responseHeaders(headers: Map<String, Header>) = apply {
|
||||||
|
this.responseHeaders = headers
|
||||||
|
}
|
||||||
|
|
||||||
fun responseCode(code: HttpStatusCode) = apply {
|
fun responseCode(code: HttpStatusCode) = apply {
|
||||||
this.responseCode = code
|
this.responseCode = code
|
||||||
@ -64,7 +71,8 @@ class ResponseInfo private constructor(
|
|||||||
description = description ?: error("You must provide a description in order to build a Response!"),
|
description = description ?: error("You must provide a description in order to build a Response!"),
|
||||||
typeEnrichment = typeEnrichment,
|
typeEnrichment = typeEnrichment,
|
||||||
examples = examples,
|
examples = examples,
|
||||||
mediaTypes = mediaTypes ?: setOf("application/json")
|
mediaTypes = mediaTypes ?: setOf("application/json"),
|
||||||
|
responseHeaders = responseHeaders
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,7 @@ object NotarizedRoute {
|
|||||||
?: it
|
?: it
|
||||||
}
|
}
|
||||||
.replace(Regex("/\\(.+\\)"), "")
|
.replace(Regex("/\\(.+\\)"), "")
|
||||||
|
.replace(Regex("/\\[.+\\]"), "")
|
||||||
|
|
||||||
fun Route.collectAuthMethods() = toString()
|
fun Route.collectAuthMethods() = toString()
|
||||||
.split("/")
|
.split("/")
|
||||||
|
@ -18,7 +18,7 @@ import kotlinx.html.unsafe
|
|||||||
/**
|
/**
|
||||||
* Provides an out-of-the-box route to view docs using ReDoc on the specified [path].
|
* Provides an out-of-the-box route to view docs using ReDoc on the specified [path].
|
||||||
* @param pageTitle Webpage title you wish to be displayed on your docs
|
* @param pageTitle Webpage title you wish to be displayed on your docs
|
||||||
* @param route path to docs resource
|
* @param path path to docs resource
|
||||||
* @param specUrl url to point ReDoc to the OpenAPI json document
|
* @param specUrl url to point ReDoc to the OpenAPI json document
|
||||||
*/
|
*/
|
||||||
fun Route.redoc(pageTitle: String = "Docs", path: String = "/docs", specUrl: String = "/openapi.json") {
|
fun Route.redoc(pageTitle: String = "Docs", path: String = "/docs", specUrl: String = "/openapi.json") {
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
package io.bkbn.kompendium.core.routes
|
||||||
|
|
||||||
|
import io.ktor.server.application.call
|
||||||
|
import io.ktor.server.html.respondHtml
|
||||||
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.get
|
||||||
|
import io.ktor.server.routing.route
|
||||||
|
import kotlinx.html.body
|
||||||
|
import kotlinx.html.div
|
||||||
|
import kotlinx.html.head
|
||||||
|
import kotlinx.html.id
|
||||||
|
import kotlinx.html.link
|
||||||
|
import kotlinx.html.meta
|
||||||
|
import kotlinx.html.script
|
||||||
|
import kotlinx.html.title
|
||||||
|
import kotlinx.html.unsafe
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an out-of-the-box route to view docs using Swagger
|
||||||
|
* @see <a href="https://swagger.io/specification/">Swagger OpenApi Specification</a>
|
||||||
|
* for the latest supported open api version.
|
||||||
|
* @param pageTitle Webpage title you wish to be displayed on your docs
|
||||||
|
* @param path path to docs resource
|
||||||
|
* @param specUrl url to point Swagger to the OpenAPI json document
|
||||||
|
* @param swaggerVersion version of swagger-ui distribution
|
||||||
|
*/
|
||||||
|
fun Route.swagger(
|
||||||
|
pageTitle: String = "Docs",
|
||||||
|
path: String = "/swagger-ui",
|
||||||
|
specUrl: String = "/openapi.json",
|
||||||
|
swaggerVersion: String? = null
|
||||||
|
) {
|
||||||
|
val swaggerVersionSuffix = if (swaggerVersion == null) "" else "@$swaggerVersion"
|
||||||
|
|
||||||
|
route(path) {
|
||||||
|
get {
|
||||||
|
call.respondHtml {
|
||||||
|
head {
|
||||||
|
title {
|
||||||
|
+pageTitle
|
||||||
|
}
|
||||||
|
meta {
|
||||||
|
charset = "utf-8"
|
||||||
|
}
|
||||||
|
meta {
|
||||||
|
name = "viewport"
|
||||||
|
content = "width=device-width, initial-scale=1"
|
||||||
|
}
|
||||||
|
link {
|
||||||
|
href = "https://unpkg.com/swagger-ui-dist$swaggerVersionSuffix/swagger-ui.css"
|
||||||
|
rel = "stylesheet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
div {
|
||||||
|
id = "swagger-ui"
|
||||||
|
}
|
||||||
|
script {
|
||||||
|
src = "https://unpkg.com/swagger-ui-dist$swaggerVersionSuffix/swagger-ui-standalone-preset.js"
|
||||||
|
}
|
||||||
|
script {
|
||||||
|
src = "https://unpkg.com/swagger-ui-dist$swaggerVersionSuffix/swagger-ui-bundle.js"
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
+"""
|
||||||
|
<script>
|
||||||
|
window.onload = function () {
|
||||||
|
// Build a system
|
||||||
|
const ui = SwaggerUIBundle({
|
||||||
|
url: "$specUrl",
|
||||||
|
dom_id: '#swagger-ui',
|
||||||
|
deepLinking: true,
|
||||||
|
presets: [
|
||||||
|
SwaggerUIBundle.presets.apis,
|
||||||
|
SwaggerUIStandalonePreset
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
SwaggerUIBundle.plugins.DownloadUrl
|
||||||
|
],
|
||||||
|
layout: "StandaloneLayout",
|
||||||
|
})
|
||||||
|
window.ui = ui
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -72,13 +72,15 @@ object Helpers {
|
|||||||
|
|
||||||
when (this) {
|
when (this) {
|
||||||
is MethodInfoWithRequest -> {
|
is MethodInfoWithRequest -> {
|
||||||
SchemaGenerator.fromTypeOrUnit(
|
this.request?.let { reqInfo ->
|
||||||
type = this.request.requestType,
|
SchemaGenerator.fromTypeOrUnit(
|
||||||
cache = spec.components.schemas,
|
type = reqInfo.requestType,
|
||||||
schemaConfigurator = schemaConfigurator,
|
cache = spec.components.schemas,
|
||||||
enrichment = this.request.typeEnrichment,
|
schemaConfigurator = schemaConfigurator,
|
||||||
)?.let { schema ->
|
enrichment = reqInfo.typeEnrichment,
|
||||||
spec.components.schemas[this.request.requestType.getSlug(this.request.typeEnrichment)] = schema
|
)?.let { schema ->
|
||||||
|
spec.components.schemas[reqInfo.requestType.getSlug(reqInfo.typeEnrichment)] = schema
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,21 +123,24 @@ object Helpers {
|
|||||||
?.map { listOf(it).toMap() }
|
?.map { listOf(it).toMap() }
|
||||||
?.toMutableList(),
|
?.toMutableList(),
|
||||||
requestBody = when (this) {
|
requestBody = when (this) {
|
||||||
is MethodInfoWithRequest -> Request(
|
is MethodInfoWithRequest -> this.request?.let { reqInfo ->
|
||||||
description = this.request.description,
|
Request(
|
||||||
content = this.request.requestType.toReferenceContent(
|
description = reqInfo.description,
|
||||||
examples = this.request.examples,
|
content = reqInfo.requestType.toReferenceContent(
|
||||||
mediaTypes = this.request.mediaTypes,
|
examples = reqInfo.examples,
|
||||||
enrichment = this.request.typeEnrichment
|
mediaTypes = reqInfo.mediaTypes,
|
||||||
),
|
enrichment = reqInfo.typeEnrichment
|
||||||
required = true
|
),
|
||||||
)
|
required = reqInfo.required
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
},
|
},
|
||||||
responses = mapOf(
|
responses = mapOf(
|
||||||
this.response.responseCode.value to Response(
|
this.response.responseCode.value to Response(
|
||||||
description = this.response.description,
|
description = this.response.description,
|
||||||
|
headers = this.response.responseHeaders,
|
||||||
content = this.response.responseType.toReferenceContent(
|
content = this.response.responseType.toReferenceContent(
|
||||||
examples = this.response.examples,
|
examples = this.response.examples,
|
||||||
mediaTypes = this.response.mediaTypes,
|
mediaTypes = this.response.mediaTypes,
|
||||||
@ -148,6 +153,7 @@ object Helpers {
|
|||||||
private fun List<ResponseInfo>.toResponseMap(): Map<Int, Response> = associate { error ->
|
private fun List<ResponseInfo>.toResponseMap(): Map<Int, Response> = associate { error ->
|
||||||
error.responseCode.value to Response(
|
error.responseCode.value to Response(
|
||||||
description = error.description,
|
description = error.description,
|
||||||
|
headers = error.responseHeaders,
|
||||||
content = error.responseType.toReferenceContent(
|
content = error.responseType.toReferenceContent(
|
||||||
examples = error.examples,
|
examples = error.examples,
|
||||||
mediaTypes = error.mediaTypes,
|
mediaTypes = error.mediaTypes,
|
||||||
|
@ -2,6 +2,7 @@ package io.bkbn.kompendium.core
|
|||||||
|
|
||||||
import dev.forst.ktor.apikey.apiKey
|
import dev.forst.ktor.apikey.apiKey
|
||||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
||||||
|
import io.bkbn.kompendium.core.util.arrayConstraints
|
||||||
import io.bkbn.kompendium.core.util.complexRequest
|
import io.bkbn.kompendium.core.util.complexRequest
|
||||||
import io.bkbn.kompendium.core.util.customAuthConfig
|
import io.bkbn.kompendium.core.util.customAuthConfig
|
||||||
import io.bkbn.kompendium.core.util.customFieldNameResponse
|
import io.bkbn.kompendium.core.util.customFieldNameResponse
|
||||||
@ -9,6 +10,8 @@ import io.bkbn.kompendium.core.util.dateTimeString
|
|||||||
import io.bkbn.kompendium.core.util.defaultAuthConfig
|
import io.bkbn.kompendium.core.util.defaultAuthConfig
|
||||||
import io.bkbn.kompendium.core.util.defaultField
|
import io.bkbn.kompendium.core.util.defaultField
|
||||||
import io.bkbn.kompendium.core.util.defaultParameter
|
import io.bkbn.kompendium.core.util.defaultParameter
|
||||||
|
import io.bkbn.kompendium.core.util.doubleConstraints
|
||||||
|
import io.bkbn.kompendium.core.util.enrichedGenericResponse
|
||||||
import io.bkbn.kompendium.core.util.enrichedComplexGenericType
|
import io.bkbn.kompendium.core.util.enrichedComplexGenericType
|
||||||
import io.bkbn.kompendium.core.util.enrichedNestedCollection
|
import io.bkbn.kompendium.core.util.enrichedNestedCollection
|
||||||
import io.bkbn.kompendium.core.util.enrichedSimpleRequest
|
import io.bkbn.kompendium.core.util.enrichedSimpleRequest
|
||||||
@ -20,6 +23,7 @@ import io.bkbn.kompendium.core.util.genericPolymorphicResponseMultipleImpls
|
|||||||
import io.bkbn.kompendium.core.util.gnarlyGenericResponse
|
import io.bkbn.kompendium.core.util.gnarlyGenericResponse
|
||||||
import io.bkbn.kompendium.core.util.headerParameter
|
import io.bkbn.kompendium.core.util.headerParameter
|
||||||
import io.bkbn.kompendium.core.util.ignoredFieldsResponse
|
import io.bkbn.kompendium.core.util.ignoredFieldsResponse
|
||||||
|
import io.bkbn.kompendium.core.util.intConstraints
|
||||||
import io.bkbn.kompendium.core.util.multipleAuthStrategies
|
import io.bkbn.kompendium.core.util.multipleAuthStrategies
|
||||||
import io.bkbn.kompendium.core.util.multipleExceptions
|
import io.bkbn.kompendium.core.util.multipleExceptions
|
||||||
import io.bkbn.kompendium.core.util.nestedGenericCollection
|
import io.bkbn.kompendium.core.util.nestedGenericCollection
|
||||||
@ -45,9 +49,12 @@ import io.bkbn.kompendium.core.util.polymorphicCollectionResponse
|
|||||||
import io.bkbn.kompendium.core.util.polymorphicException
|
import io.bkbn.kompendium.core.util.polymorphicException
|
||||||
import io.bkbn.kompendium.core.util.polymorphicMapResponse
|
import io.bkbn.kompendium.core.util.polymorphicMapResponse
|
||||||
import io.bkbn.kompendium.core.util.polymorphicResponse
|
import io.bkbn.kompendium.core.util.polymorphicResponse
|
||||||
|
import io.bkbn.kompendium.core.util.postNoReqBody
|
||||||
import io.bkbn.kompendium.core.util.primitives
|
import io.bkbn.kompendium.core.util.primitives
|
||||||
import io.bkbn.kompendium.core.util.reqRespExamples
|
import io.bkbn.kompendium.core.util.reqRespExamples
|
||||||
|
import io.bkbn.kompendium.core.util.optionalReqExample
|
||||||
import io.bkbn.kompendium.core.util.requiredParams
|
import io.bkbn.kompendium.core.util.requiredParams
|
||||||
|
import io.bkbn.kompendium.core.util.responseHeaders
|
||||||
import io.bkbn.kompendium.core.util.returnsList
|
import io.bkbn.kompendium.core.util.returnsList
|
||||||
import io.bkbn.kompendium.core.util.rootRoute
|
import io.bkbn.kompendium.core.util.rootRoute
|
||||||
import io.bkbn.kompendium.core.util.samePathDifferentMethodsAndAuth
|
import io.bkbn.kompendium.core.util.samePathDifferentMethodsAndAuth
|
||||||
@ -56,8 +63,12 @@ import io.bkbn.kompendium.core.util.simpleGenericResponse
|
|||||||
import io.bkbn.kompendium.core.util.simplePathParsing
|
import io.bkbn.kompendium.core.util.simplePathParsing
|
||||||
import io.bkbn.kompendium.core.util.simpleRecursive
|
import io.bkbn.kompendium.core.util.simpleRecursive
|
||||||
import io.bkbn.kompendium.core.util.singleException
|
import io.bkbn.kompendium.core.util.singleException
|
||||||
|
import io.bkbn.kompendium.core.util.stringConstraints
|
||||||
|
import io.bkbn.kompendium.core.util.stringContentEncodingConstraints
|
||||||
|
import io.bkbn.kompendium.core.util.stringPatternConstraints
|
||||||
import io.bkbn.kompendium.core.util.topLevelNullable
|
import io.bkbn.kompendium.core.util.topLevelNullable
|
||||||
import io.bkbn.kompendium.core.util.trailingSlash
|
import io.bkbn.kompendium.core.util.trailingSlash
|
||||||
|
import io.bkbn.kompendium.core.util.paramWrapper
|
||||||
import io.bkbn.kompendium.core.util.unbackedFieldsResponse
|
import io.bkbn.kompendium.core.util.unbackedFieldsResponse
|
||||||
import io.bkbn.kompendium.core.util.withOperationId
|
import io.bkbn.kompendium.core.util.withOperationId
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
@ -123,6 +134,12 @@ class KompendiumTest : DescribeSpec({
|
|||||||
it("Can override media types") {
|
it("Can override media types") {
|
||||||
openApiTestAllSerializers("T0052__override_media_types.json") { overrideMediaTypes() }
|
openApiTestAllSerializers("T0052__override_media_types.json") { overrideMediaTypes() }
|
||||||
}
|
}
|
||||||
|
it("Can support a post request with no request body") {
|
||||||
|
openApiTestAllSerializers("T0065__post_no_req_body.json") { postNoReqBody() }
|
||||||
|
}
|
||||||
|
it("Can notarize a route with response headers") {
|
||||||
|
openApiTestAllSerializers("T0066__notarized_get_with_response_headers.json") { responseHeaders() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
describe("Route Parsing") {
|
describe("Route Parsing") {
|
||||||
it("Can parse a simple path and store it under the expected route") {
|
it("Can parse a simple path and store it under the expected route") {
|
||||||
@ -137,6 +154,9 @@ class KompendiumTest : DescribeSpec({
|
|||||||
it("Can notarize a route with a trailing slash") {
|
it("Can notarize a route with a trailing slash") {
|
||||||
openApiTestAllSerializers("T0015__trailing_slash.json") { trailingSlash() }
|
openApiTestAllSerializers("T0015__trailing_slash.json") { trailingSlash() }
|
||||||
}
|
}
|
||||||
|
it("Can notarize a route with a parameter") {
|
||||||
|
openApiTestAllSerializers("T0068__param_wrapper.json") { paramWrapper() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
describe("Exceptions") {
|
describe("Exceptions") {
|
||||||
it("Can add an exception status code to a response") {
|
it("Can add an exception status code to a response") {
|
||||||
@ -159,6 +179,9 @@ class KompendiumTest : DescribeSpec({
|
|||||||
it("Can describe example parameters") {
|
it("Can describe example parameters") {
|
||||||
openApiTestAllSerializers("T0021__example_parameters.json") { exampleParams() }
|
openApiTestAllSerializers("T0021__example_parameters.json") { exampleParams() }
|
||||||
}
|
}
|
||||||
|
it("Can generate example optional request body") {
|
||||||
|
openApiTestAllSerializers("T0069__example_optional_req.json") { optionalReqExample() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
describe("Defaults") {
|
describe("Defaults") {
|
||||||
it("Can generate a default parameter value") {
|
it("Can generate a default parameter value") {
|
||||||
@ -318,9 +341,6 @@ class KompendiumTest : DescribeSpec({
|
|||||||
exception.message should startWith("A route has already been registered for path: /test/{a} and method: GET")
|
exception.message should startWith("A route has already been registered for path: /test/{a} and method: GET")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
describe("Constraints") {
|
|
||||||
// TODO Assess strategies here
|
|
||||||
}
|
|
||||||
describe("Formats") {
|
describe("Formats") {
|
||||||
it("Can set a format for a simple type schema") {
|
it("Can set a format for a simple type schema") {
|
||||||
openApiTestAllSerializers(
|
openApiTestAllSerializers(
|
||||||
@ -441,5 +461,30 @@ class KompendiumTest : DescribeSpec({
|
|||||||
it("Can enrich a complex generic type") {
|
it("Can enrich a complex generic type") {
|
||||||
openApiTestAllSerializers("T0057__enriched_complex_generic_type.json") { enrichedComplexGenericType() }
|
openApiTestAllSerializers("T0057__enriched_complex_generic_type.json") { enrichedComplexGenericType() }
|
||||||
}
|
}
|
||||||
|
it("Can enrich a generic object") {
|
||||||
|
openApiTestAllSerializers("T0067__enriched_generic_object.json") { enrichedGenericResponse() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
describe("Constraints") {
|
||||||
|
it("Can apply constraints to an int field") {
|
||||||
|
openApiTestAllSerializers("T0059__int_constraints.json") { intConstraints() }
|
||||||
|
}
|
||||||
|
it("Can apply constraints to a double field") {
|
||||||
|
openApiTestAllSerializers("T0060__double_constraints.json") { doubleConstraints() }
|
||||||
|
}
|
||||||
|
it("Can apply a min and max length to a string field") {
|
||||||
|
openApiTestAllSerializers("T0061__string_min_max_constraints.json") { stringConstraints() }
|
||||||
|
}
|
||||||
|
it("Can apply a pattern to a string field") {
|
||||||
|
openApiTestAllSerializers("T0062__string_pattern_constraints.json") { stringPatternConstraints() }
|
||||||
|
}
|
||||||
|
it("Can apply a content encoding and media type to a string field") {
|
||||||
|
openApiTestAllSerializers("T0063__string_content_encoding_constraints.json") {
|
||||||
|
stringContentEncodingConstraints()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it("Can apply constraints to an array field") {
|
||||||
|
openApiTestAllSerializers("T0064__array_constraints.json") { arrayConstraints() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
160
core/src/test/kotlin/io/bkbn/kompendium/core/util/Constraints.kt
Normal file
160
core/src/test/kotlin/io/bkbn/kompendium/core/util/Constraints.kt
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package io.bkbn.kompendium.core.util
|
||||||
|
|
||||||
|
import io.bkbn.kompendium.core.fixtures.DoubleResponse
|
||||||
|
import io.bkbn.kompendium.core.fixtures.Page
|
||||||
|
import io.bkbn.kompendium.core.fixtures.TestCreatedResponse
|
||||||
|
import io.bkbn.kompendium.core.fixtures.TestNested
|
||||||
|
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||||
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
|
import io.bkbn.kompendium.core.util.TestModules.defaultPath
|
||||||
|
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.server.application.install
|
||||||
|
import io.ktor.server.routing.Routing
|
||||||
|
import io.ktor.server.routing.route
|
||||||
|
|
||||||
|
fun Routing.intConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get an int")
|
||||||
|
description("Get an int")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("An int")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
TestCreatedResponse::id {
|
||||||
|
minimum = 2
|
||||||
|
maximum = 100
|
||||||
|
multipleOf = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Routing.doubleConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get a double")
|
||||||
|
description("Get a double")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("A double")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
DoubleResponse::payload {
|
||||||
|
minimum = 2.0
|
||||||
|
maximum = 100.0
|
||||||
|
multipleOf = 2.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Routing.stringConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get a string")
|
||||||
|
description("Get a string with constraints")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("A string")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
TestNested::nesty {
|
||||||
|
maxLength = 10
|
||||||
|
minLength = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Routing.stringPatternConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get a string")
|
||||||
|
description("This is a description")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("A string")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
TestNested::nesty {
|
||||||
|
pattern = "[a-z]+"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Routing.stringContentEncodingConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get a string")
|
||||||
|
description("This is a description")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("A string")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
TestNested::nesty {
|
||||||
|
contentEncoding = "base64"
|
||||||
|
contentMediaType = "image/png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Routing.arrayConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get an array")
|
||||||
|
description("Get an array of strings")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("An array")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
Page<String>::content {
|
||||||
|
minItems = 2
|
||||||
|
maxItems = 10
|
||||||
|
uniqueItems = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import io.bkbn.kompendium.core.fixtures.NestedComplexItem
|
|||||||
import io.bkbn.kompendium.core.fixtures.TestCreatedResponse
|
import io.bkbn.kompendium.core.fixtures.TestCreatedResponse
|
||||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||||
import io.bkbn.kompendium.core.fixtures.TestSimpleRequest
|
import io.bkbn.kompendium.core.fixtures.TestSimpleRequest
|
||||||
|
import io.bkbn.kompendium.core.fixtures.GenericObject
|
||||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||||
import io.bkbn.kompendium.core.metadata.PostInfo
|
import io.bkbn.kompendium.core.metadata.PostInfo
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
@ -135,3 +136,33 @@ fun Routing.enrichedComplexGenericType() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Routing.enrichedGenericResponse() {
|
||||||
|
route("/example") {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary(TestModules.defaultPathSummary)
|
||||||
|
description(TestModules.defaultPathDescription)
|
||||||
|
response {
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("generic") {
|
||||||
|
GenericObject<TestSimpleRequest>::data {
|
||||||
|
description = "A simple description"
|
||||||
|
typeEnrichment = TypeEnrichment("simple") {
|
||||||
|
TestSimpleRequest::a {
|
||||||
|
description = "A simple description"
|
||||||
|
}
|
||||||
|
TestSimpleRequest::b {
|
||||||
|
deprecated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
description("A good response")
|
||||||
|
responseCode(HttpStatusCode.Created)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -55,3 +55,30 @@ fun Routing.exampleParams() = basicGetGenerator<TestResponse>(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun Routing.optionalReqExample() {
|
||||||
|
route(rootPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
post = PostInfo.builder {
|
||||||
|
summary(defaultPathSummary)
|
||||||
|
description(defaultPathDescription)
|
||||||
|
request {
|
||||||
|
description(defaultRequestDescription)
|
||||||
|
requestType<TestRequest>()
|
||||||
|
examples(
|
||||||
|
"Testerina" to TestRequest(TestNested("asdf"), 1.5, emptyList())
|
||||||
|
)
|
||||||
|
required(false)
|
||||||
|
}
|
||||||
|
response {
|
||||||
|
description(defaultResponseDescription)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
responseType<TestResponse>()
|
||||||
|
examples(
|
||||||
|
"Testerino" to TestResponse("Heya")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,7 +20,9 @@ import io.bkbn.kompendium.core.util.TestModules.defaultPathSummary
|
|||||||
import io.bkbn.kompendium.core.util.TestModules.defaultResponseDescription
|
import io.bkbn.kompendium.core.util.TestModules.defaultResponseDescription
|
||||||
import io.bkbn.kompendium.core.util.TestModules.rootPath
|
import io.bkbn.kompendium.core.util.TestModules.rootPath
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
|
import io.bkbn.kompendium.oas.payload.Header
|
||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
|
import io.ktor.http.HttpHeaders
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.server.application.call
|
import io.ktor.server.application.call
|
||||||
import io.ktor.server.application.install
|
import io.ktor.server.application.install
|
||||||
@ -56,6 +58,38 @@ fun Routing.notarizedGet() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Routing.responseHeaders() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
parameters = defaultParams
|
||||||
|
get = GetInfo.builder {
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
responseType<TestResponse>()
|
||||||
|
description(defaultResponseDescription)
|
||||||
|
responseHeaders(
|
||||||
|
mapOf(
|
||||||
|
HttpHeaders.ETag to Header(
|
||||||
|
TypeDefinition.STRING,
|
||||||
|
"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag"
|
||||||
|
),
|
||||||
|
HttpHeaders.LastModified to Header(
|
||||||
|
TypeDefinition.STRING,
|
||||||
|
"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
summary(defaultPathSummary)
|
||||||
|
description(defaultPathDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
call.respondText { "hey dude ‼️ congrats on the get request" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Routing.notarizedPost() {
|
fun Routing.notarizedPost() {
|
||||||
route(defaultPath) {
|
route(defaultPath) {
|
||||||
install(NotarizedRoute()) {
|
install(NotarizedRoute()) {
|
||||||
@ -299,3 +333,19 @@ fun Routing.overrideMediaTypes() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Routing.postNoReqBody() {
|
||||||
|
route("/no_req_body") {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
post = PostInfo.builder {
|
||||||
|
summary(defaultPathSummary)
|
||||||
|
description(defaultPathDescription)
|
||||||
|
response {
|
||||||
|
responseType<TestResponse>()
|
||||||
|
description("Cool response")
|
||||||
|
responseCode(HttpStatusCode.Created)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ import io.ktor.http.HttpStatusCode
|
|||||||
import io.ktor.server.application.install
|
import io.ktor.server.application.install
|
||||||
import io.ktor.server.routing.Routing
|
import io.ktor.server.routing.Routing
|
||||||
import io.ktor.server.routing.route
|
import io.ktor.server.routing.route
|
||||||
|
import io.ktor.server.routing.param
|
||||||
|
|
||||||
fun Routing.simplePathParsing() {
|
fun Routing.simplePathParsing() {
|
||||||
route("/this") {
|
route("/this") {
|
||||||
@ -100,3 +101,32 @@ fun Routing.trailingSlash() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Routing.paramWrapper() {
|
||||||
|
route("/test") {
|
||||||
|
param("a") {
|
||||||
|
param("b") {
|
||||||
|
param("c") {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
parameters = listOf(
|
||||||
|
Parameter(
|
||||||
|
name = "test",
|
||||||
|
`in` = Parameter.Location.query,
|
||||||
|
schema = TypeDefinition.STRING
|
||||||
|
)
|
||||||
|
)
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary(defaultPathSummary)
|
||||||
|
description(defaultPathDescription)
|
||||||
|
response {
|
||||||
|
description(defaultResponseDescription)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
responseType<TestResponse>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
80
core/src/test/resources/T0059__int_constraints.json
Normal file
80
core/src/test/resources/T0059__int_constraints.json
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get an int",
|
||||||
|
"description": "Get an int",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "An int",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestCreatedResponse-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestCreatedResponse-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"c": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32",
|
||||||
|
"multipleOf": 2,
|
||||||
|
"maximum": 100,
|
||||||
|
"minimum": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"c",
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
76
core/src/test/resources/T0060__double_constraints.json
Normal file
76
core/src/test/resources/T0060__double_constraints.json
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get a double",
|
||||||
|
"description": "Get a double",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A double",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/DoubleResponse-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"DoubleResponse-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"payload": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double",
|
||||||
|
"multipleOf": 2.0,
|
||||||
|
"maximum": 100.0,
|
||||||
|
"minimum": 2.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"payload"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get a string",
|
||||||
|
"description": "Get a string with constraints",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A string",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestNested-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestNested-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"nesty": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 10,
|
||||||
|
"minLength": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"nesty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get a string",
|
||||||
|
"description": "This is a description",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A string",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestNested-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestNested-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"nesty": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "[a-z]+"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"nesty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get a string",
|
||||||
|
"description": "This is a description",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A string",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestNested-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestNested-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"nesty": {
|
||||||
|
"type": "string",
|
||||||
|
"contentEncoding": "base64",
|
||||||
|
"contentMediaType": "image/png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"nesty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
103
core/src/test/resources/T0064__array_constraints.json
Normal file
103
core/src/test/resources/T0064__array_constraints.json
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get an array",
|
||||||
|
"description": "Get an array of strings",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "An array",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Page-String-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Page-String-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"content": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"maxItems": 10,
|
||||||
|
"minItems": 2,
|
||||||
|
"uniqueItems": true,
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"number": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"numberOfElements": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"totalElements": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"totalPages": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"content",
|
||||||
|
"number",
|
||||||
|
"numberOfElements",
|
||||||
|
"size",
|
||||||
|
"totalElements",
|
||||||
|
"totalPages"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
72
core/src/test/resources/T0065__post_no_req_body.json
Normal file
72
core/src/test/resources/T0065__post_no_req_body.json
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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": {
|
||||||
|
"/no_req_body": {
|
||||||
|
"post": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Great Summary!",
|
||||||
|
"description": "testing more",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": "Cool response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"c": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"c"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Great Summary!",
|
||||||
|
"description": "testing more",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A Successful Endeavor",
|
||||||
|
"headers": {
|
||||||
|
"ETag": {
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag",
|
||||||
|
"required": true,
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"Last-Modified": {
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified",
|
||||||
|
"required": true,
|
||||||
|
"deprecated": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "a",
|
||||||
|
"in": "path",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aa",
|
||||||
|
"in": "query",
|
||||||
|
"schema": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"deprecated": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"c": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"c"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
91
core/src/test/resources/T0067__enriched_generic_object.json
Normal file
91
core/src/test/resources/T0067__enriched_generic_object.json
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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": {
|
||||||
|
"/example": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Great Summary!",
|
||||||
|
"description": "testing more",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": "A good response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/GenericObject-TestSimpleRequest-generic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"GenericObject-TestSimpleRequest-generic": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/components/schemas/TestSimpleRequest-simple",
|
||||||
|
"description": "A simple description"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"data"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestSimpleRequest-simple": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"a": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A simple description"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32",
|
||||||
|
"deprecated": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"a",
|
||||||
|
"b"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
82
core/src/test/resources/T0068__param_wrapper.json
Normal file
82
core/src/test/resources/T0068__param_wrapper.json
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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": "Great Summary!",
|
||||||
|
"description": "testing more",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A Successful Endeavor",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"in": "query",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"deprecated": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"c": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"c"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
136
core/src/test/resources/T0069__example_optional_req.json
Normal file
136
core/src/test/resources/T0069__example_optional_req.json
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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": {
|
||||||
|
"/": {
|
||||||
|
"post": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Great Summary!",
|
||||||
|
"description": "testing more",
|
||||||
|
"parameters": [],
|
||||||
|
"requestBody": {
|
||||||
|
"description": "You gotta send it",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestRequest"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"Testerina": {
|
||||||
|
"value": {
|
||||||
|
"fieldName": {
|
||||||
|
"nesty": "asdf"
|
||||||
|
},
|
||||||
|
"b": 1.5,
|
||||||
|
"aaa": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A Successful Endeavor",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestResponse"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"Testerino": {
|
||||||
|
"value": {
|
||||||
|
"c": "Heya"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"c": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"c"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"aaa": {
|
||||||
|
"items": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"fieldName": {
|
||||||
|
"$ref": "#/components/schemas/TestNested"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"aaa",
|
||||||
|
"b",
|
||||||
|
"fieldName"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestNested": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"nesty": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"nesty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.SerializationFeature
|
|||||||
import io.bkbn.kompendium.core.fixtures.TestSpecs.defaultSpec
|
import io.bkbn.kompendium.core.fixtures.TestSpecs.defaultSpec
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||||
import io.bkbn.kompendium.core.routes.redoc
|
import io.bkbn.kompendium.core.routes.redoc
|
||||||
|
import io.bkbn.kompendium.core.routes.swagger
|
||||||
import io.bkbn.kompendium.json.schema.KotlinXSchemaConfigurator
|
import io.bkbn.kompendium.json.schema.KotlinXSchemaConfigurator
|
||||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||||
import io.bkbn.kompendium.oas.OpenApiSpec
|
import io.bkbn.kompendium.oas.OpenApiSpec
|
||||||
@ -130,6 +131,7 @@ object TestHelpers {
|
|||||||
}
|
}
|
||||||
application(applicationSetup)
|
application(applicationSetup)
|
||||||
routing {
|
routing {
|
||||||
|
swagger()
|
||||||
redoc()
|
redoc()
|
||||||
routeUnderTest()
|
routeUnderTest()
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,9 @@ import java.time.Instant
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class TestNested(val nesty: String)
|
data class TestNested(val nesty: String)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class DoubleResponse(val payload: Double)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TestRequest(
|
data class TestRequest(
|
||||||
val fieldName: TestNested,
|
val fieldName: TestNested,
|
||||||
@ -177,6 +180,10 @@ data class SerialNameObject(
|
|||||||
val camelCaseName: String
|
val camelCaseName: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class GenericObject<T>(
|
||||||
|
val data: T
|
||||||
|
)
|
||||||
|
|
||||||
enum class Color {
|
enum class Color {
|
||||||
RED,
|
RED,
|
||||||
GREEN,
|
GREEN,
|
||||||
|
@ -22,6 +22,8 @@ formatting:
|
|||||||
indentSize: 2
|
indentSize: 2
|
||||||
ImportOrdering:
|
ImportOrdering:
|
||||||
active: false
|
active: false
|
||||||
|
EnumEntryNameCase:
|
||||||
|
active: false
|
||||||
naming:
|
naming:
|
||||||
ConstructorParameterNaming:
|
ConstructorParameterNaming:
|
||||||
active: false
|
active: false
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
# Summary
|
# Summary
|
||||||
|
|
||||||
* [Introduction](index.md)
|
* [Introduction](index.md)
|
||||||
* [Helpers](helpers/index.md)
|
|
||||||
* [Protobuf java converter](helpers/protobuf_java_converter.md)
|
|
||||||
* [Plugins](plugins/index.md)
|
* [Plugins](plugins/index.md)
|
||||||
* [Notarized Application](plugins/notarized_application.md)
|
* [Notarized Application](plugins/notarized_application.md)
|
||||||
* [Notarized Route](plugins/notarized_route.md)
|
* [Notarized Route](plugins/notarized_route.md)
|
||||||
* [Notarized Locations](plugins/notarized_locations.md)
|
* [Notarized Locations](plugins/notarized_locations.md)
|
||||||
* [Notarized Resources](plugins/notarized_resources.md)
|
* [Notarized Resources](plugins/notarized_resources.md)
|
||||||
|
* [Concepts](concepts/index.md)
|
||||||
|
* [Enrichment](concepts/enrichment.md)
|
||||||
|
* [Helpers](helpers/index.md)
|
||||||
|
* [Protobuf java converter](helpers/protobuf_java_converter.md)
|
||||||
* [The Playground](playground.md)
|
* [The Playground](playground.md)
|
||||||
|
106
docs/concepts/enrichment.md
Normal file
106
docs/concepts/enrichment.md
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
Kompendium allows users to enrich their data types with additional information. This can be done by defining a
|
||||||
|
`TypeEnrichment` object and passing it to the `enrichment` parameter of the relevant `requestType` or `responseType`.
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
data class SimpleData(val a: String, val b: Int? = null)
|
||||||
|
|
||||||
|
val myEnrichment = TypeEnrichment<SimpleData>(id = "simple-enrichment") {
|
||||||
|
SimpleData::a {
|
||||||
|
description = "This will update the field description"
|
||||||
|
}
|
||||||
|
SimpleData::b {
|
||||||
|
// Will indicate in the UI that the field will be removed soon
|
||||||
|
deprecated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In your route documentation
|
||||||
|
fun Routing.enrichedSimpleRequest() {
|
||||||
|
route("/example") {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
parameters = TestModules.defaultParams
|
||||||
|
post = PostInfo.builder {
|
||||||
|
summary(TestModules.defaultPathSummary)
|
||||||
|
description(TestModules.defaultPathDescription)
|
||||||
|
request {
|
||||||
|
requestType<SimpleData>(enrichment = myEnrichment) // Simply attach the enrichment to the request
|
||||||
|
description("A test request")
|
||||||
|
}
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.Created)
|
||||||
|
responseType<TestCreatedResponse>()
|
||||||
|
description(TestModules.defaultResponseDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{% hint style="warning" %}
|
||||||
|
An enrichment must provide an `id` field that is unique to the data class that is being enriched. This is because
|
||||||
|
under the hood, Kompendium appends this id to the data class identifier in order to support multiple different
|
||||||
|
enrichments
|
||||||
|
on the same data class.
|
||||||
|
|
||||||
|
If you provide duplicate ids, all but the first enrichment will be ignored, as Kompendium will view that as a cache hit,
|
||||||
|
and skip analyzing the new enrichment.
|
||||||
|
{% endhint %}
|
||||||
|
|
||||||
|
### Nested Enrichments
|
||||||
|
|
||||||
|
Enrichments are portable and composable, meaning that we can take an enrichment for a child data class
|
||||||
|
and apply it inside a parent data class using the `typeEnrichment` property.
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
data class ParentData(val a: String, val b: ChildData)
|
||||||
|
data class ChildData(val c: String, val d: Int? = null)
|
||||||
|
|
||||||
|
val childEnrichment = TypeEnrichment<ChildData>(id = "child-enrichment") {
|
||||||
|
ChildData::c {
|
||||||
|
description = "This will update the field description of field c on child data"
|
||||||
|
}
|
||||||
|
ChildData::d {
|
||||||
|
description = "This will update the field description of field d on child data"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val parentEnrichment = TypeEnrichment<ParentData>(id = "parent-enrichment") {
|
||||||
|
ParentData::a {
|
||||||
|
description = "This will update the field description"
|
||||||
|
}
|
||||||
|
ParentData::b {
|
||||||
|
description = "This will update the field description of field b on parent data"
|
||||||
|
typeEnrichment = childEnrichment // Will apply the child enrichment to the internals of field b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Enrichments
|
||||||
|
|
||||||
|
All enrichments support the following properties:
|
||||||
|
|
||||||
|
- description -> Provides a reader friendly description of the field in the object
|
||||||
|
- deprecated -> Indicates that the field is deprecated and should not be used
|
||||||
|
|
||||||
|
### String
|
||||||
|
|
||||||
|
- minLength -> The minimum length of the string
|
||||||
|
- maxLength -> The maximum length of the string
|
||||||
|
- pattern -> A regex pattern that the string must match
|
||||||
|
- contentEncoding -> The encoding of the string
|
||||||
|
- contentMediaType -> The media type of the string
|
||||||
|
|
||||||
|
### Numbers
|
||||||
|
|
||||||
|
- minimum -> The minimum value of the number
|
||||||
|
- maximum -> The maximum value of the number
|
||||||
|
- exclusiveMinimum -> Indicates that the minimum value is exclusive
|
||||||
|
- exclusiveMaximum -> Indicates that the maximum value is exclusive
|
||||||
|
- multipleOf -> Indicates that the number must be a multiple of the provided value
|
||||||
|
|
||||||
|
### Arrays
|
||||||
|
|
||||||
|
- minItems -> The minimum number of items in the array
|
||||||
|
- maxItems -> The maximum number of items in the array
|
||||||
|
- uniqueItems -> Indicates that the array must contain unique items
|
2
docs/concepts/index.md
Normal file
2
docs/concepts/index.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Various concepts that are core to Kompendium but not necessarily exclusive
|
||||||
|
to any given module or plugin
|
@ -204,89 +204,3 @@ route("/user/{id}") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Enrichment
|
|
||||||
|
|
||||||
Kompendium allows users to enrich their data types with additional information. This can be done by defining a
|
|
||||||
`TypeEnrichment` object and passing it to the `enrich` function on the `NotarizedRoute` builder. Enrichments
|
|
||||||
can be added to any request or response.
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
data class SimpleData(val a: String, val b: Int? = null)
|
|
||||||
|
|
||||||
val myEnrichment = TypeEnrichment<SimpleData>(id = "simple-enrichment") {
|
|
||||||
SimpleData::a {
|
|
||||||
description = "This will update the field description"
|
|
||||||
}
|
|
||||||
SimpleData::b {
|
|
||||||
// Will indicate in the UI that the field will be removed soon
|
|
||||||
deprecated = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In your route documentation
|
|
||||||
fun Routing.enrichedSimpleRequest() {
|
|
||||||
route("/example") {
|
|
||||||
install(NotarizedRoute()) {
|
|
||||||
parameters = TestModules.defaultParams
|
|
||||||
post = PostInfo.builder {
|
|
||||||
summary(TestModules.defaultPathSummary)
|
|
||||||
description(TestModules.defaultPathDescription)
|
|
||||||
request {
|
|
||||||
requestType<SimpleData>(enrichment = myEnrichment) // Simply attach the enrichment to the request
|
|
||||||
description("A test request")
|
|
||||||
}
|
|
||||||
response {
|
|
||||||
responseCode(HttpStatusCode.Created)
|
|
||||||
responseType<TestCreatedResponse>()
|
|
||||||
description(TestModules.defaultResponseDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
{% hint style="warning" %}
|
|
||||||
An enrichment must provide an `id` field that is unique to the data class that is being enriched. This is because
|
|
||||||
under the hood, Kompendium appends this id to the data class identifier in order to support multiple different
|
|
||||||
enrichments
|
|
||||||
on the same data class.
|
|
||||||
|
|
||||||
If you provide duplicate ids, all but the first enrichment will be ignored, as Kompendium will view that as a cache hit,
|
|
||||||
and skip analyzing the new enrichment.
|
|
||||||
{% endhint %}
|
|
||||||
|
|
||||||
At the moment, the only available enrichments are the following
|
|
||||||
|
|
||||||
- description -> Provides a reader friendly description of the field in the object
|
|
||||||
- deprecated -> Indicates that the field is deprecated and should not be used
|
|
||||||
|
|
||||||
### Nested Enrichments
|
|
||||||
|
|
||||||
Enrichments are portable and composable, meaning that we can take an enrichment for a child data class
|
|
||||||
and apply it inside a parent data class using the `typeEnrichment` property.
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
data class ParentData(val a: String, val b: ChildData)
|
|
||||||
data class ChildData(val c: String, val d: Int? = null)
|
|
||||||
|
|
||||||
val childEnrichment = TypeEnrichment<ChildData>(id = "child-enrichment") {
|
|
||||||
ChildData::c {
|
|
||||||
description = "This will update the field description of field c on child data"
|
|
||||||
}
|
|
||||||
ChildData::d {
|
|
||||||
description = "This will update the field description of field d on child data"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val parentEnrichment = TypeEnrichment<ParentData>(id = "parent-enrichment") {
|
|
||||||
ParentData::a {
|
|
||||||
description = "This will update the field description"
|
|
||||||
}
|
|
||||||
ParentData::b {
|
|
||||||
description = "This will update the field description of field b on parent data"
|
|
||||||
typeEnrichment = childEnrichment // Will apply the child enrichment to the internals of field b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
@ -1,7 +1,36 @@
|
|||||||
package io.bkbn.kompendium.enrichment
|
package io.bkbn.kompendium.enrichment
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference https://json-schema.org/draft/2020-12/json-schema-validation.html#name-multipleof
|
||||||
|
*/
|
||||||
class PropertyEnrichment : Enrichment {
|
class PropertyEnrichment : Enrichment {
|
||||||
|
// Metadata
|
||||||
var deprecated: Boolean? = null
|
var deprecated: Boolean? = null
|
||||||
var description: String? = null
|
var description: String? = null
|
||||||
var typeEnrichment: TypeEnrichment<*>? = null
|
var typeEnrichment: TypeEnrichment<*>? = null
|
||||||
|
|
||||||
|
// Number and Integer Constraints
|
||||||
|
var multipleOf: Number? = null
|
||||||
|
var maximum: Number? = null
|
||||||
|
var exclusiveMaximum: Number? = null
|
||||||
|
var minimum: Number? = null
|
||||||
|
var exclusiveMinimum: Number? = null
|
||||||
|
|
||||||
|
// String constraints
|
||||||
|
var maxLength: Int? = null
|
||||||
|
var minLength: Int? = null
|
||||||
|
var pattern: String? = null
|
||||||
|
var contentEncoding: String? = null
|
||||||
|
var contentMediaType: String? = null
|
||||||
|
// TODO how to handle contentSchema?
|
||||||
|
|
||||||
|
// Array constraints
|
||||||
|
var maxItems: Int? = null
|
||||||
|
var minItems: Int? = null
|
||||||
|
var uniqueItems: Boolean? = null
|
||||||
|
// TODO How to handle contains, minContains, maxContains?
|
||||||
|
|
||||||
|
// Object constraints
|
||||||
|
var maxProperties: Int? = null
|
||||||
|
var minProperties: Int? = null
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
# Kompendium
|
# Kompendium
|
||||||
project.version=3.10.0
|
project.version=3.14.4
|
||||||
# Kotlin
|
# Kotlin
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
# Gradle
|
# Gradle
|
||||||
org.gradle.vfs.watch=true
|
org.gradle.vfs.watch=true
|
||||||
org.gradle.vfs.verbose=true
|
org.gradle.vfs.verbose=true
|
||||||
org.gradle.jvmargs=-Xmx2000m
|
org.gradle.jvmargs=-Xmx2000m
|
||||||
|
org.gradle.parallel=true
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
ktorVersion=2.2.1
|
ktorVersion=2.3.1
|
||||||
kotestVersion=5.5.4
|
kotestVersion=5.6.2
|
||||||
detektVersion=1.21.0
|
detektVersion=1.22.0
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
19
gradlew
vendored
19
gradlew
vendored
@ -55,7 +55,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@ -80,13 +80,10 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@ -143,12 +140,16 @@ fi
|
|||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@ -193,6 +194,10 @@ if "$cygwin" || "$msys" ; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
# Collect all arguments for the java command;
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
1
gradlew.bat
vendored
1
gradlew.bat
vendored
@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
|
|||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ dependencies {
|
|||||||
// Kompendium
|
// Kompendium
|
||||||
api(projects.kompendiumEnrichment)
|
api(projects.kompendiumEnrichment)
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.0")
|
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.21")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||||
|
|
||||||
// Formatting
|
// Formatting
|
||||||
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion")
|
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion")
|
||||||
|
@ -7,6 +7,11 @@ data class ArrayDefinition(
|
|||||||
val items: JsonSchema,
|
val items: JsonSchema,
|
||||||
override val deprecated: Boolean? = null,
|
override val deprecated: Boolean? = null,
|
||||||
override val description: String? = null,
|
override val description: String? = null,
|
||||||
|
|
||||||
|
// Constraints
|
||||||
|
val maxItems: Int? = null,
|
||||||
|
val minItems: Int? = null,
|
||||||
|
val uniqueItems: Boolean? = null,
|
||||||
) : JsonSchema {
|
) : JsonSchema {
|
||||||
val type: String = "array"
|
val type: String = "array"
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.bkbn.kompendium.json.schema.definition
|
package io.bkbn.kompendium.json.schema.definition
|
||||||
|
|
||||||
|
import io.bkbn.kompendium.json.schema.util.Serializers
|
||||||
import kotlinx.serialization.Contextual
|
import kotlinx.serialization.Contextual
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@ -12,6 +13,30 @@ data class TypeDefinition(
|
|||||||
@Contextual val default: Any? = null,
|
@Contextual val default: Any? = null,
|
||||||
override val deprecated: Boolean? = null,
|
override val deprecated: Boolean? = null,
|
||||||
override val description: String? = null,
|
override val description: String? = null,
|
||||||
|
// Constraints
|
||||||
|
|
||||||
|
// Number
|
||||||
|
@Serializable(with = Serializers.Number::class)
|
||||||
|
val multipleOf: Number? = null,
|
||||||
|
@Serializable(with = Serializers.Number::class)
|
||||||
|
val maximum: Number? = null,
|
||||||
|
@Serializable(with = Serializers.Number::class)
|
||||||
|
val exclusiveMaximum: Number? = null,
|
||||||
|
@Serializable(with = Serializers.Number::class)
|
||||||
|
val minimum: Number? = null,
|
||||||
|
@Serializable(with = Serializers.Number::class)
|
||||||
|
val exclusiveMinimum: Number? = null,
|
||||||
|
|
||||||
|
// String
|
||||||
|
val maxLength: Int? = null,
|
||||||
|
val minLength: Int? = null,
|
||||||
|
val pattern: String? = null,
|
||||||
|
val contentEncoding: String? = null,
|
||||||
|
val contentMediaType: String? = null,
|
||||||
|
|
||||||
|
// Object
|
||||||
|
val maxProperties: Int? = null,
|
||||||
|
val minProperties: Int? = null,
|
||||||
) : JsonSchema {
|
) : JsonSchema {
|
||||||
|
|
||||||
fun withDefault(default: Any): TypeDefinition = this.copy(default = default)
|
fun withDefault(default: Any): TypeDefinition = this.copy(default = default)
|
||||||
|
@ -34,7 +34,6 @@ object SimpleObjectHandler {
|
|||||||
schemaConfigurator: SchemaConfigurator,
|
schemaConfigurator: SchemaConfigurator,
|
||||||
enrichment: TypeEnrichment<*>?,
|
enrichment: TypeEnrichment<*>?,
|
||||||
): JsonSchema {
|
): JsonSchema {
|
||||||
|
|
||||||
cache[type.getSlug(enrichment)] = ReferenceDefinition(type.getReferenceSlug(enrichment))
|
cache[type.getSlug(enrichment)] = ReferenceDefinition(type.getReferenceSlug(enrichment))
|
||||||
|
|
||||||
val typeMap = clazz.typeParameters.zip(type.arguments).toMap()
|
val typeMap = clazz.typeParameters.zip(type.arguments).toMap()
|
||||||
@ -134,8 +133,8 @@ object SimpleObjectHandler {
|
|||||||
?: error("This indicates a bug in Kompendium, please open a GitHub issue")
|
?: error("This indicates a bug in Kompendium, please open a GitHub issue")
|
||||||
return SchemaGenerator.fromTypeToSchema(type, cache, schemaConfigurator, propEnrichment?.typeEnrichment).let {
|
return SchemaGenerator.fromTypeToSchema(type, cache, schemaConfigurator, propEnrichment?.typeEnrichment).let {
|
||||||
if (it.isOrContainsObjectOrEnumDef()) {
|
if (it.isOrContainsObjectOrEnumDef()) {
|
||||||
cache[type.getSlug(propEnrichment)] = it
|
cache[type.getSlug(propEnrichment?.typeEnrichment)] = it
|
||||||
ReferenceDefinition(type.getReferenceSlug(propEnrichment))
|
ReferenceDefinition(type.getReferenceSlug(propEnrichment?.typeEnrichment))
|
||||||
} else {
|
} else {
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
@ -169,12 +168,34 @@ object SimpleObjectHandler {
|
|||||||
|
|
||||||
private fun PropertyEnrichment.applyToSchema(schema: JsonSchema): JsonSchema = when (schema) {
|
private fun PropertyEnrichment.applyToSchema(schema: JsonSchema): JsonSchema = when (schema) {
|
||||||
is AnyOfDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is AnyOfDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is ArrayDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is ArrayDefinition -> schema.copy(
|
||||||
|
deprecated = deprecated,
|
||||||
|
description = description,
|
||||||
|
minItems = minItems,
|
||||||
|
maxItems = maxItems,
|
||||||
|
uniqueItems = uniqueItems,
|
||||||
|
)
|
||||||
|
|
||||||
is EnumDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is EnumDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is MapDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is MapDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is NullableDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is NullableDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is OneOfDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is OneOfDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is ReferenceDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is ReferenceDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is TypeDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is TypeDefinition -> schema.copy(
|
||||||
|
deprecated = deprecated,
|
||||||
|
description = description,
|
||||||
|
multipleOf = multipleOf,
|
||||||
|
maximum = maximum,
|
||||||
|
exclusiveMaximum = exclusiveMaximum,
|
||||||
|
minimum = minimum,
|
||||||
|
exclusiveMinimum = exclusiveMinimum,
|
||||||
|
maxLength = maxLength,
|
||||||
|
minLength = minLength,
|
||||||
|
pattern = pattern,
|
||||||
|
contentEncoding = contentEncoding,
|
||||||
|
contentMediaType = contentMediaType,
|
||||||
|
maxProperties = maxProperties,
|
||||||
|
minProperties = minProperties,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import kotlin.reflect.KType
|
|||||||
|
|
||||||
object Helpers {
|
object Helpers {
|
||||||
|
|
||||||
private const val COMPONENT_SLUG = "#/components/schemas"
|
const val COMPONENT_SLUG = "#/components/schemas"
|
||||||
|
|
||||||
fun KType.getSlug(enrichment: Enrichment? = null) = when (enrichment) {
|
fun KType.getSlug(enrichment: Enrichment? = null) = when (enrichment) {
|
||||||
is TypeEnrichment<*> -> getEnrichedSlug(enrichment)
|
is TypeEnrichment<*> -> getEnrichedSlug(enrichment)
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package io.bkbn.kompendium.json.schema.util
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import java.util.UUID
|
||||||
|
import kotlin.Number as KNumber
|
||||||
|
|
||||||
|
object Serializers {
|
||||||
|
|
||||||
|
object Uuid : KSerializer<UUID> {
|
||||||
|
override val descriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): UUID {
|
||||||
|
return UUID.fromString(decoder.decodeString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: UUID) {
|
||||||
|
encoder.encodeString(value.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Number : KSerializer<KNumber> {
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Number", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): KNumber {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: KNumber) {
|
||||||
|
when (value) {
|
||||||
|
is Int -> encoder.encodeInt(value)
|
||||||
|
is Long -> encoder.encodeLong(value)
|
||||||
|
is Double -> encoder.encodeDouble(value)
|
||||||
|
is Float -> encoder.encodeFloat(value)
|
||||||
|
else -> throw IllegalArgumentException("Number is not a valid type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,11 +7,12 @@ import io.bkbn.kompendium.core.fixtures.ObjectWithEnum
|
|||||||
import io.bkbn.kompendium.core.fixtures.SerialNameObject
|
import io.bkbn.kompendium.core.fixtures.SerialNameObject
|
||||||
import io.bkbn.kompendium.core.fixtures.SimpleEnum
|
import io.bkbn.kompendium.core.fixtures.SimpleEnum
|
||||||
import io.bkbn.kompendium.core.fixtures.SlammaJamma
|
import io.bkbn.kompendium.core.fixtures.SlammaJamma
|
||||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.getFileSnapshot
|
|
||||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||||
import io.bkbn.kompendium.core.fixtures.TestSimpleRequest
|
import io.bkbn.kompendium.core.fixtures.TestSimpleRequest
|
||||||
import io.bkbn.kompendium.core.fixtures.TransientObject
|
import io.bkbn.kompendium.core.fixtures.TransientObject
|
||||||
import io.bkbn.kompendium.core.fixtures.UnbackedObject
|
import io.bkbn.kompendium.core.fixtures.UnbackedObject
|
||||||
|
import io.bkbn.kompendium.core.fixtures.GenericObject
|
||||||
|
import io.bkbn.kompendium.core.fixtures.TestHelpers.getFileSnapshot
|
||||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||||
import io.kotest.assertions.json.shouldEqualJson
|
import io.kotest.assertions.json.shouldEqualJson
|
||||||
@ -62,6 +63,9 @@ class SchemaGeneratorTest : DescribeSpec({
|
|||||||
it("Can generate the schema for object with SerialName annotation") {
|
it("Can generate the schema for object with SerialName annotation") {
|
||||||
jsonSchemaTest<SerialNameObject>("T0020__serial_name_object.json")
|
jsonSchemaTest<SerialNameObject>("T0020__serial_name_object.json")
|
||||||
}
|
}
|
||||||
|
it("Can generate the schema for object with generic property") {
|
||||||
|
jsonSchemaTest<GenericObject<TestSimpleRequest>>("T0024__generic_object.json")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
describe("Enums") {
|
describe("Enums") {
|
||||||
it("Can generate the schema for a simple enum") {
|
it("Can generate the schema for a simple enum") {
|
||||||
@ -135,6 +139,24 @@ class SchemaGeneratorTest : DescribeSpec({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
it("Can properly assign a reference to a generic object") {
|
||||||
|
jsonSchemaTest<GenericObject<TestSimpleRequest>>(
|
||||||
|
snapshotName = "T0025__enrichment_generic_object.json",
|
||||||
|
enrichment = TypeEnrichment("generic") {
|
||||||
|
GenericObject<TestSimpleRequest>::data {
|
||||||
|
description = "This is a generic param"
|
||||||
|
typeEnrichment = TypeEnrichment("simple") {
|
||||||
|
TestSimpleRequest::a {
|
||||||
|
description = "This is a simple description"
|
||||||
|
}
|
||||||
|
TestSimpleRequest::b {
|
||||||
|
deprecated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
companion object {
|
companion object {
|
||||||
|
11
json-schema/src/test/resources/T0024__generic_object.json
Normal file
11
json-schema/src/test/resources/T0024__generic_object.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/components/schemas/TestSimpleRequest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"data"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"description": "This is a generic param",
|
||||||
|
"$ref": "#/components/schemas/TestSimpleRequest-simple"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"data"
|
||||||
|
]
|
||||||
|
}
|
@ -22,8 +22,8 @@ dependencies {
|
|||||||
// IMPLEMENTATION
|
// IMPLEMENTATION
|
||||||
|
|
||||||
implementation(projects.kompendiumCore)
|
implementation(projects.kompendiumCore)
|
||||||
implementation("io.ktor:ktor-server-core:2.1.3")
|
implementation("io.ktor:ktor-server-core:2.3.1")
|
||||||
implementation("io.ktor:ktor-server-locations:2.1.3")
|
implementation("io.ktor:ktor-server-locations:2.3.1")
|
||||||
|
|
||||||
// TESTING
|
// TESTING
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ data class Listing(val name: String, val page: Int)
|
|||||||
data class Type(val name: String) {
|
data class Type(val name: String) {
|
||||||
@Location("/edit")
|
@Location("/edit")
|
||||||
data class Edit(val parent: Type)
|
data class Edit(val parent: Type)
|
||||||
|
|
||||||
@Location("/other/{page}")
|
@Location("/other/{page}")
|
||||||
data class Other(val parent: Type, val page: Int)
|
data class Other(val parent: Type, val page: Int)
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ dependencies {
|
|||||||
|
|
||||||
api(projects.kompendiumJsonSchema)
|
api(projects.kompendiumJsonSchema)
|
||||||
api(projects.kompendiumEnrichment)
|
api(projects.kompendiumEnrichment)
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||||
|
|
||||||
// Formatting
|
// Formatting
|
||||||
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion")
|
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion")
|
||||||
|
@ -35,14 +35,14 @@ dependencies {
|
|||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation("org.apache.logging.log4j:log4j-api-kotlin:1.2.0")
|
implementation("org.apache.logging.log4j:log4j-api-kotlin:1.2.0")
|
||||||
implementation("org.apache.logging.log4j:log4j-api:2.19.0")
|
implementation("org.apache.logging.log4j:log4j-api:2.20.0")
|
||||||
implementation("org.apache.logging.log4j:log4j-core:2.19.0")
|
implementation("org.apache.logging.log4j:log4j-core:2.20.0")
|
||||||
implementation("org.slf4j:slf4j-api:2.0.6")
|
implementation("org.slf4j:slf4j-api:2.0.7")
|
||||||
implementation("org.slf4j:slf4j-simple:2.0.6")
|
implementation("org.slf4j:slf4j-simple:2.0.7")
|
||||||
|
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
|
||||||
|
|
||||||
implementation("joda-time:joda-time:2.12.2")
|
implementation("joda-time:joda-time:2.12.5")
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import io.bkbn.kompendium.core.metadata.GetInfo
|
|||||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
import io.bkbn.kompendium.core.routes.redoc
|
import io.bkbn.kompendium.core.routes.redoc
|
||||||
|
import io.bkbn.kompendium.core.routes.swagger
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
import io.bkbn.kompendium.oas.component.Components
|
import io.bkbn.kompendium.oas.component.Components
|
||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
@ -68,6 +69,7 @@ private fun Application.mainModule() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
|
swagger(pageTitle = "Simple API Docs")
|
||||||
redoc(pageTitle = "Simple API Docs")
|
redoc(pageTitle = "Simple API Docs")
|
||||||
authenticate("basic") {
|
authenticate("basic") {
|
||||||
route("/{id}") {
|
route("/{id}") {
|
||||||
|
@ -4,6 +4,7 @@ import io.bkbn.kompendium.core.metadata.GetInfo
|
|||||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
import io.bkbn.kompendium.core.routes.redoc
|
import io.bkbn.kompendium.core.routes.redoc
|
||||||
|
import io.bkbn.kompendium.core.routes.swagger
|
||||||
import io.bkbn.kompendium.json.schema.KotlinXSchemaConfigurator
|
import io.bkbn.kompendium.json.schema.KotlinXSchemaConfigurator
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
@ -49,6 +50,7 @@ private fun Application.mainModule() {
|
|||||||
schemaConfigurator = KotlinXSchemaConfigurator()
|
schemaConfigurator = KotlinXSchemaConfigurator()
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
|
swagger(pageTitle = "Simple API Docs")
|
||||||
redoc(pageTitle = "Simple API Docs")
|
redoc(pageTitle = "Simple API Docs")
|
||||||
route("/{id}") {
|
route("/{id}") {
|
||||||
idDocumentation()
|
idDocumentation()
|
||||||
|
@ -4,6 +4,7 @@ import io.bkbn.kompendium.core.metadata.GetInfo
|
|||||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
import io.bkbn.kompendium.core.routes.redoc
|
import io.bkbn.kompendium.core.routes.redoc
|
||||||
|
import io.bkbn.kompendium.core.routes.swagger
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
||||||
@ -51,6 +52,7 @@ private fun Application.mainModule() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
|
swagger(pageTitle = "Simple API Docs")
|
||||||
redoc(pageTitle = "Simple API Docs")
|
redoc(pageTitle = "Simple API Docs")
|
||||||
|
|
||||||
route("/{id}") {
|
route("/{id}") {
|
||||||
|
@ -5,6 +5,7 @@ import io.bkbn.kompendium.core.metadata.PostInfo
|
|||||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
import io.bkbn.kompendium.core.routes.redoc
|
import io.bkbn.kompendium.core.routes.redoc
|
||||||
|
import io.bkbn.kompendium.core.routes.swagger
|
||||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||||
import io.bkbn.kompendium.json.schema.KotlinXSchemaConfigurator
|
import io.bkbn.kompendium.json.schema.KotlinXSchemaConfigurator
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
@ -50,6 +51,7 @@ private fun Application.mainModule() {
|
|||||||
schemaConfigurator = KotlinXSchemaConfigurator()
|
schemaConfigurator = KotlinXSchemaConfigurator()
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
|
swagger(pageTitle = "Simple API Docs")
|
||||||
redoc(pageTitle = "Simple API Docs")
|
redoc(pageTitle = "Simple API Docs")
|
||||||
enrichedDocumentation()
|
enrichedDocumentation()
|
||||||
post {
|
post {
|
||||||
@ -70,6 +72,8 @@ private val testEnrichment = TypeEnrichment("testerino") {
|
|||||||
description = "A good but old field"
|
description = "A good but old field"
|
||||||
typeEnrichment = TypeEnrichment("big-tings") {
|
typeEnrichment = TypeEnrichment("big-tings") {
|
||||||
InnerRequest::d {
|
InnerRequest::d {
|
||||||
|
exclusiveMaximum = 10.0
|
||||||
|
exclusiveMinimum = 1.1
|
||||||
description = "THE BIG D"
|
description = "THE BIG D"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import io.bkbn.kompendium.core.metadata.GetInfo
|
|||||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
import io.bkbn.kompendium.core.routes.redoc
|
import io.bkbn.kompendium.core.routes.redoc
|
||||||
|
import io.bkbn.kompendium.core.routes.swagger
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
||||||
@ -50,6 +51,7 @@ private fun Application.mainModule() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
|
swagger(pageTitle = "Simple API Docs")
|
||||||
redoc(pageTitle = "Simple API Docs")
|
redoc(pageTitle = "Simple API Docs")
|
||||||
|
|
||||||
route("/{id}") {
|
route("/{id}") {
|
||||||
|
@ -6,6 +6,7 @@ import io.bkbn.kompendium.core.metadata.GetInfo
|
|||||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
import io.bkbn.kompendium.core.routes.redoc
|
import io.bkbn.kompendium.core.routes.redoc
|
||||||
|
import io.bkbn.kompendium.core.routes.swagger
|
||||||
import io.bkbn.kompendium.json.schema.SchemaConfigurator
|
import io.bkbn.kompendium.json.schema.SchemaConfigurator
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
@ -48,6 +49,7 @@ private fun Application.mainModule() {
|
|||||||
schemaConfigurator = GsonSchemaConfigurator()
|
schemaConfigurator = GsonSchemaConfigurator()
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
|
swagger(pageTitle = "Simple API Docs")
|
||||||
redoc(pageTitle = "Simple API Docs")
|
redoc(pageTitle = "Simple API Docs")
|
||||||
|
|
||||||
route("/{id}") {
|
route("/{id}") {
|
||||||
|
@ -5,6 +5,7 @@ import io.bkbn.kompendium.core.metadata.GetInfo
|
|||||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
import io.bkbn.kompendium.core.routes.redoc
|
import io.bkbn.kompendium.core.routes.redoc
|
||||||
|
import io.bkbn.kompendium.core.routes.swagger
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
import io.bkbn.kompendium.oas.component.Components
|
import io.bkbn.kompendium.oas.component.Components
|
||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
@ -80,6 +81,7 @@ private fun Application.mainModule() {
|
|||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
authenticate("basic") {
|
authenticate("basic") {
|
||||||
|
swagger(pageTitle = "Simple API Docs")
|
||||||
redoc(pageTitle = "Simple API Docs")
|
redoc(pageTitle = "Simple API Docs")
|
||||||
route("/{id}") {
|
route("/{id}") {
|
||||||
locationDocumentation()
|
locationDocumentation()
|
||||||
|
@ -8,6 +8,7 @@ import io.bkbn.kompendium.core.metadata.GetInfo
|
|||||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
import io.bkbn.kompendium.core.routes.redoc
|
import io.bkbn.kompendium.core.routes.redoc
|
||||||
|
import io.bkbn.kompendium.core.routes.swagger
|
||||||
import io.bkbn.kompendium.json.schema.SchemaConfigurator
|
import io.bkbn.kompendium.json.schema.SchemaConfigurator
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
@ -51,6 +52,7 @@ private fun Application.mainModule() {
|
|||||||
schemaConfigurator = JacksonSchemaConfigurator()
|
schemaConfigurator = JacksonSchemaConfigurator()
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
|
swagger(pageTitle = "Simple API Docs")
|
||||||
redoc(pageTitle = "Simple API Docs")
|
redoc(pageTitle = "Simple API Docs")
|
||||||
|
|
||||||
route("/{id}") {
|
route("/{id}") {
|
||||||
|
@ -3,6 +3,7 @@ package io.bkbn.kompendium.playground
|
|||||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||||
import io.bkbn.kompendium.core.routes.redoc
|
import io.bkbn.kompendium.core.routes.redoc
|
||||||
|
import io.bkbn.kompendium.core.routes.swagger
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
import io.bkbn.kompendium.locations.NotarizedLocations
|
import io.bkbn.kompendium.locations.NotarizedLocations
|
||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
@ -72,6 +73,7 @@ private fun Application.mainModule() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
|
swagger(pageTitle = "Simple API Docs")
|
||||||
redoc(pageTitle = "Simple API Docs")
|
redoc(pageTitle = "Simple API Docs")
|
||||||
get<Listing> { listing ->
|
get<Listing> { listing ->
|
||||||
call.respondText("Listing ${listing.name}, page ${listing.page}")
|
call.respondText("Listing ${listing.name}, page ${listing.page}")
|
||||||
|
@ -3,6 +3,7 @@ package io.bkbn.kompendium.playground
|
|||||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||||
import io.bkbn.kompendium.core.routes.redoc
|
import io.bkbn.kompendium.core.routes.redoc
|
||||||
|
import io.bkbn.kompendium.core.routes.swagger
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
import io.bkbn.kompendium.oas.payload.Parameter
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
||||||
@ -73,6 +74,7 @@ private fun Application.mainModule() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
|
swagger(pageTitle = "Simple API Docs")
|
||||||
redoc(pageTitle = "Simple API Docs")
|
redoc(pageTitle = "Simple API Docs")
|
||||||
get<ListingResource> { listing ->
|
get<ListingResource> { listing ->
|
||||||
call.respondText("Listing ${listing.name}, page ${listing.page}")
|
call.respondText("Listing ${listing.name}, page ${listing.page}")
|
||||||
|
@ -22,9 +22,9 @@ dependencies {
|
|||||||
|
|
||||||
|
|
||||||
implementation(projects.kompendiumJsonSchema)
|
implementation(projects.kompendiumJsonSchema)
|
||||||
implementation("com.google.protobuf:protobuf-java:3.21.12")
|
implementation("com.google.protobuf:protobuf-java:3.23.2")
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.0")
|
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.21")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||||
|
|
||||||
// Formatting
|
// Formatting
|
||||||
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion")
|
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion")
|
||||||
|
@ -9,6 +9,7 @@ import io.bkbn.kompendium.json.schema.definition.MapDefinition
|
|||||||
import io.bkbn.kompendium.json.schema.definition.NullableDefinition
|
import io.bkbn.kompendium.json.schema.definition.NullableDefinition
|
||||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
|
import io.bkbn.kompendium.json.schema.util.Helpers
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.full.createType
|
import kotlin.reflect.full.createType
|
||||||
|
|
||||||
@ -150,7 +151,7 @@ fun fromTypeToSchema(
|
|||||||
type = "string",
|
type = "string",
|
||||||
enum = javaProtoField.enumType.values.map { it.name }.toSet()
|
enum = javaProtoField.enumType.values.map { it.name }.toSet()
|
||||||
)
|
)
|
||||||
ReferenceDefinition(javaProtoField.enumType.name)
|
ReferenceDefinition("${Helpers.COMPONENT_SLUG}/${javaProtoField.enumType.name}")
|
||||||
}
|
}
|
||||||
Descriptors.FieldDescriptor.JavaType.MESSAGE -> {
|
Descriptors.FieldDescriptor.JavaType.MESSAGE -> {
|
||||||
// Traverse through possible nested messages
|
// Traverse through possible nested messages
|
||||||
@ -160,7 +161,7 @@ fun fromTypeToSchema(
|
|||||||
it.jsonName to fromNestedTypeToSchema(it, cache)
|
it.jsonName to fromNestedTypeToSchema(it, cache)
|
||||||
}.toMap()
|
}.toMap()
|
||||||
)
|
)
|
||||||
ReferenceDefinition(javaProtoField.messageType.name)
|
ReferenceDefinition("${Helpers.COMPONENT_SLUG}/${javaProtoField.messageType.name}")
|
||||||
}
|
}
|
||||||
null -> NullableDefinition()
|
null -> NullableDefinition()
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,21 @@ package io.bkbn.kompendium.protobufjavaconverter.converters
|
|||||||
|
|
||||||
import com.google.protobuf.Descriptors
|
import com.google.protobuf.Descriptors
|
||||||
import com.google.protobuf.GeneratedMessageV3
|
import com.google.protobuf.GeneratedMessageV3
|
||||||
|
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
||||||
|
import io.bkbn.kompendium.core.metadata.PostInfo
|
||||||
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
import io.bkbn.kompendium.json.schema.definition.ArrayDefinition
|
import io.bkbn.kompendium.json.schema.definition.ArrayDefinition
|
||||||
import io.bkbn.kompendium.json.schema.definition.EnumDefinition
|
import io.bkbn.kompendium.json.schema.definition.EnumDefinition
|
||||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||||
import io.bkbn.kompendium.json.schema.definition.MapDefinition
|
import io.bkbn.kompendium.json.schema.definition.MapDefinition
|
||||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||||
|
import io.bkbn.kompendium.json.schema.util.Helpers
|
||||||
import io.bkbn.kompendium.protobufjavaconverter.Corpus
|
import io.bkbn.kompendium.protobufjavaconverter.Corpus
|
||||||
import io.bkbn.kompendium.protobufjavaconverter.DoubleNestedMessage
|
import io.bkbn.kompendium.protobufjavaconverter.DoubleNestedMessage
|
||||||
import io.bkbn.kompendium.protobufjavaconverter.NestedMapMessage
|
|
||||||
import io.bkbn.kompendium.protobufjavaconverter.EnumMessage
|
import io.bkbn.kompendium.protobufjavaconverter.EnumMessage
|
||||||
import io.bkbn.kompendium.protobufjavaconverter.GoogleTypes
|
import io.bkbn.kompendium.protobufjavaconverter.GoogleTypes
|
||||||
|
import io.bkbn.kompendium.protobufjavaconverter.NestedMapMessage
|
||||||
import io.bkbn.kompendium.protobufjavaconverter.NestedMessage
|
import io.bkbn.kompendium.protobufjavaconverter.NestedMessage
|
||||||
import io.bkbn.kompendium.protobufjavaconverter.RepeatedEnumMessage
|
import io.bkbn.kompendium.protobufjavaconverter.RepeatedEnumMessage
|
||||||
import io.bkbn.kompendium.protobufjavaconverter.RepeatedMessage
|
import io.bkbn.kompendium.protobufjavaconverter.RepeatedMessage
|
||||||
@ -23,10 +27,15 @@ import io.kotest.matchers.maps.shouldContainExactly
|
|||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import io.kotest.matchers.types.shouldBeTypeOf
|
import io.kotest.matchers.types.shouldBeTypeOf
|
||||||
import io.kotest.matchers.types.shouldNotBeTypeOf
|
import io.kotest.matchers.types.shouldNotBeTypeOf
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.server.application.install
|
||||||
|
import io.ktor.server.routing.Route
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.full.createType
|
import kotlin.reflect.full.createType
|
||||||
|
|
||||||
class FieldDescriptiorConvertersKtTest : DescribeSpec({
|
class FieldDescriptiorConvertersKtTest : DescribeSpec({
|
||||||
|
|
||||||
|
val componentSlug = Helpers.COMPONENT_SLUG
|
||||||
describe("fromTypeToSchemaTests") {
|
describe("fromTypeToSchemaTests") {
|
||||||
val simpleMessageDescriptor = SimpleTestMessage.getDescriptor()
|
val simpleMessageDescriptor = SimpleTestMessage.getDescriptor()
|
||||||
it("java int field should return TypeDefinition INT") {
|
it("java int field should return TypeDefinition INT") {
|
||||||
@ -77,7 +86,7 @@ class FieldDescriptiorConvertersKtTest : DescribeSpec({
|
|||||||
val message = NestedMessage.getDescriptor()
|
val message = NestedMessage.getDescriptor()
|
||||||
val result = fromNestedTypeToSchema(message.findFieldByName("nested_field"))
|
val result = fromNestedTypeToSchema(message.findFieldByName("nested_field"))
|
||||||
result.shouldBeTypeOf<ReferenceDefinition>()
|
result.shouldBeTypeOf<ReferenceDefinition>()
|
||||||
result.`$ref`.shouldBe(message.findFieldByName("nested_field").messageType.name)
|
result.`$ref`.shouldBe("${Helpers.COMPONENT_SLUG}/${message.findFieldByName("nested_field").messageType.name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
it("Repeated message should return ArrayDefinition") {
|
it("Repeated message should return ArrayDefinition") {
|
||||||
@ -85,7 +94,7 @@ class FieldDescriptiorConvertersKtTest : DescribeSpec({
|
|||||||
val result = fromNestedTypeToSchema(message.findFieldByName("repeated_field"))
|
val result = fromNestedTypeToSchema(message.findFieldByName("repeated_field"))
|
||||||
result.shouldBeTypeOf<ArrayDefinition>()
|
result.shouldBeTypeOf<ArrayDefinition>()
|
||||||
result.items.shouldBeTypeOf<ReferenceDefinition>()
|
result.items.shouldBeTypeOf<ReferenceDefinition>()
|
||||||
(result.items as ReferenceDefinition).`$ref`.shouldBe(SimpleTestMessage.getDescriptor().name)
|
(result.items as ReferenceDefinition).`$ref`.shouldBe("$componentSlug/${SimpleTestMessage.getDescriptor().name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
it("Repeated enum message should return ArrayDefinition") {
|
it("Repeated enum message should return ArrayDefinition") {
|
||||||
@ -93,7 +102,7 @@ class FieldDescriptiorConvertersKtTest : DescribeSpec({
|
|||||||
val result: JsonSchema = fromNestedTypeToSchema(message.findFieldByName("repeated_field"))
|
val result: JsonSchema = fromNestedTypeToSchema(message.findFieldByName("repeated_field"))
|
||||||
result.shouldBeTypeOf<ArrayDefinition>()
|
result.shouldBeTypeOf<ArrayDefinition>()
|
||||||
result.items.shouldBeTypeOf<ReferenceDefinition>()
|
result.items.shouldBeTypeOf<ReferenceDefinition>()
|
||||||
(result.items as ReferenceDefinition).`$ref`.shouldBe(Corpus.getDescriptor().name)
|
(result.items as ReferenceDefinition).`$ref`.shouldBe("$componentSlug/${Corpus.getDescriptor().name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
it("SimpleMapMessage message should return MapDefinition") {
|
it("SimpleMapMessage message should return MapDefinition") {
|
||||||
@ -169,7 +178,7 @@ class FieldDescriptiorConvertersKtTest : DescribeSpec({
|
|||||||
// Our nested field should be a reference
|
// Our nested field should be a reference
|
||||||
result.shouldBeTypeOf<ReferenceDefinition>()
|
result.shouldBeTypeOf<ReferenceDefinition>()
|
||||||
// Our nested field should be a reference to simplemessage
|
// Our nested field should be a reference to simplemessage
|
||||||
result.`$ref`.shouldBe(SimpleTestMessage.getDescriptor().name)
|
result.`$ref`.shouldBe("$componentSlug/${SimpleTestMessage.getDescriptor().name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
it("Double nested message to schema") {
|
it("Double nested message to schema") {
|
||||||
@ -201,11 +210,11 @@ class FieldDescriptiorConvertersKtTest : DescribeSpec({
|
|||||||
// Our nested field should be a reference
|
// Our nested field should be a reference
|
||||||
result.shouldBeTypeOf<ReferenceDefinition>()
|
result.shouldBeTypeOf<ReferenceDefinition>()
|
||||||
// it should be a reference to our nested message
|
// it should be a reference to our nested message
|
||||||
result.`$ref`.shouldBe(NestedMessage.getDescriptor().name)
|
result.`$ref`.shouldBe("$componentSlug/${NestedMessage.getDescriptor().name}")
|
||||||
val nestedResult = (resultSchema[NestedMessage::class.createType()] as TypeDefinition).properties!!["nestedField"]
|
val nestedResult = (resultSchema[NestedMessage::class.createType()] as TypeDefinition).properties!!["nestedField"]
|
||||||
nestedResult.shouldBeTypeOf<ReferenceDefinition>()
|
nestedResult.shouldBeTypeOf<ReferenceDefinition>()
|
||||||
// Our nested message reference should be pointing to simpleTest message
|
// Our nested message reference should be pointing to simpleTest message
|
||||||
nestedResult.`$ref`.shouldBe(SimpleTestMessage.getDescriptor().name)
|
nestedResult.`$ref`.shouldBe("$componentSlug/${SimpleTestMessage.getDescriptor().name}")
|
||||||
// last but not least we should have definition for our SimpleTest message which is not a reference
|
// last but not least we should have definition for our SimpleTest message which is not a reference
|
||||||
(resultSchema[SimpleTestMessage::class.createType()] as TypeDefinition).shouldNotBeTypeOf<ReferenceDefinition>()
|
(resultSchema[SimpleTestMessage::class.createType()] as TypeDefinition).shouldNotBeTypeOf<ReferenceDefinition>()
|
||||||
}
|
}
|
||||||
@ -235,6 +244,39 @@ class FieldDescriptiorConvertersKtTest : DescribeSpec({
|
|||||||
testMessageBasics(message)
|
testMessageBasics(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe("Test spec generation") {
|
||||||
|
it("Generates simple message references") {
|
||||||
|
openApiTestAllSerializers(
|
||||||
|
"T0001__simpletestmessage_post.json",
|
||||||
|
testMessageBasics(SimpleTestMessage.getDefaultInstance())
|
||||||
|
) { testRoute<SimpleTestMessage>() }
|
||||||
|
}
|
||||||
|
it("Generates enum references") {
|
||||||
|
openApiTestAllSerializers(
|
||||||
|
"T0002__enummessage_post.json",
|
||||||
|
testMessageBasics(EnumMessage.getDefaultInstance())
|
||||||
|
) { testRoute<EnumMessage>() }
|
||||||
|
}
|
||||||
|
it("Generates repeated type references") {
|
||||||
|
openApiTestAllSerializers(
|
||||||
|
"T0003__repeatedmessage_post.json",
|
||||||
|
testMessageBasics(RepeatedMessage.getDefaultInstance())
|
||||||
|
) { testRoute<RepeatedMessage>() }
|
||||||
|
}
|
||||||
|
it("Generates nested type references") {
|
||||||
|
openApiTestAllSerializers(
|
||||||
|
"T0004__nestedmessage_post.json",
|
||||||
|
testMessageBasics(NestedMessage.getDefaultInstance())
|
||||||
|
) { testRoute<NestedMessage>() }
|
||||||
|
}
|
||||||
|
it("Generates nested map type references") {
|
||||||
|
openApiTestAllSerializers(
|
||||||
|
"T0005__nestedmapmessage_post.json",
|
||||||
|
testMessageBasics(NestedMapMessage.getDefaultInstance())
|
||||||
|
) { testRoute<NestedMapMessage>() }
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -256,3 +298,25 @@ fun testMessageBasics(message: GeneratedMessageV3): Map<KType, JsonSchema> {
|
|||||||
}
|
}
|
||||||
return resultSchema
|
return resultSchema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val DEFAULT_RESPONSE_DESCRIPTION = "A Successful Endeavor"
|
||||||
|
private const val DEFAULT_REQUEST_DESCRIPTION = "You gotta send it"
|
||||||
|
private const val DEFAULT_PATH_SUMMARY = "Great Summary!"
|
||||||
|
private const val DEFAULT_PATH_DESCRIPTION = "testing more"
|
||||||
|
private inline fun <reified T> Route.testRoute() {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
post = PostInfo.builder {
|
||||||
|
summary(DEFAULT_PATH_SUMMARY)
|
||||||
|
description(DEFAULT_PATH_DESCRIPTION)
|
||||||
|
request {
|
||||||
|
requestType<Unit>()
|
||||||
|
description(DEFAULT_REQUEST_DESCRIPTION)
|
||||||
|
}
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
responseType<T>()
|
||||||
|
description(DEFAULT_RESPONSE_DESCRIPTION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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": {
|
||||||
|
"/": {
|
||||||
|
"post": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Great Summary!",
|
||||||
|
"description": "testing more",
|
||||||
|
"parameters": [],
|
||||||
|
"requestBody": {
|
||||||
|
"description": "You gotta send it",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A Successful Endeavor",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/SimpleTestMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"SimpleTestMessage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"myTestDouble": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"myTestFloat": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "float"
|
||||||
|
},
|
||||||
|
"myTestInt32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestInt64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestUint32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestUint64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestSint32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestSint64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestFixed32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestFixed64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestSfixed32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestSfixed64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestBool": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"myTestBytes": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"myTestString": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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": {
|
||||||
|
"/": {
|
||||||
|
"post": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Great Summary!",
|
||||||
|
"description": "testing more",
|
||||||
|
"parameters": [],
|
||||||
|
"requestBody": {
|
||||||
|
"description": "You gotta send it",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A Successful Endeavor",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/EnumMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"EnumMessage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"corpus": {
|
||||||
|
"$ref": "#/components/schemas/Corpus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Corpus": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"CORPUS_UNSPECIFIED",
|
||||||
|
"CORPUS_UNIVERSAL",
|
||||||
|
"CORPUS_WEB",
|
||||||
|
"CORPUS_IMAGES",
|
||||||
|
"CORPUS_LOCAL",
|
||||||
|
"CORPUS_NEWS",
|
||||||
|
"CORPUS_PRODUCTS",
|
||||||
|
"CORPUS_VIDEO"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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": {
|
||||||
|
"/": {
|
||||||
|
"post": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Great Summary!",
|
||||||
|
"description": "testing more",
|
||||||
|
"parameters": [],
|
||||||
|
"requestBody": {
|
||||||
|
"description": "You gotta send it",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A Successful Endeavor",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/RepeatedMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"RepeatedMessage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"repeatedField": {
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/SimpleTestMessage"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SimpleTestMessage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"myTestDouble": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"myTestFloat": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "float"
|
||||||
|
},
|
||||||
|
"myTestInt32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestInt64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestUint32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestUint64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestSint32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestSint64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestFixed32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestFixed64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestSfixed32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestSfixed64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestBool": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"myTestBytes": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"myTestString": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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": {
|
||||||
|
"/": {
|
||||||
|
"post": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Great Summary!",
|
||||||
|
"description": "testing more",
|
||||||
|
"parameters": [],
|
||||||
|
"requestBody": {
|
||||||
|
"description": "You gotta send it",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A Successful Endeavor",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/NestedMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"NestedMessage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"nestedField": {
|
||||||
|
"$ref": "#/components/schemas/SimpleTestMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SimpleTestMessage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"myTestDouble": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"myTestFloat": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "float"
|
||||||
|
},
|
||||||
|
"myTestInt32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestInt64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestUint32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestUint64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestSint32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestSint64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestFixed32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestFixed64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestSfixed32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestSfixed64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestBool": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"myTestBytes": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"myTestString": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"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": {
|
||||||
|
"/": {
|
||||||
|
"post": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Great Summary!",
|
||||||
|
"description": "testing more",
|
||||||
|
"parameters": [],
|
||||||
|
"requestBody": {
|
||||||
|
"description": "You gotta send it",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A Successful Endeavor",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/NestedMapMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"NestedMapMessage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"mapField": {
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"myVariable0": {
|
||||||
|
"$ref": "#/components/schemas/SimpleTestMessage"
|
||||||
|
},
|
||||||
|
"myVariable1": {
|
||||||
|
"$ref": "#/components/schemas/SimpleTestMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SimpleTestMessage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"myTestDouble": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"myTestFloat": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "float"
|
||||||
|
},
|
||||||
|
"myTestInt32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestInt64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestUint32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestUint64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestSint32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestSint64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestFixed32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestFixed64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestSfixed32": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"myTestSfixed64": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"myTestBool": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"myTestBytes": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"myTestString": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -22,8 +22,8 @@ dependencies {
|
|||||||
// IMPLEMENTATION
|
// IMPLEMENTATION
|
||||||
|
|
||||||
implementation(projects.kompendiumCore)
|
implementation(projects.kompendiumCore)
|
||||||
implementation("io.ktor:ktor-server-core:2.1.3")
|
implementation("io.ktor:ktor-server-core:2.3.1")
|
||||||
implementation("io.ktor:ktor-server-resources:2.1.3")
|
implementation("io.ktor:ktor-server-resources:2.3.1")
|
||||||
|
|
||||||
// TESTING
|
// TESTING
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ data class Type(val name: String) {
|
|||||||
@Serializable
|
@Serializable
|
||||||
@Resource("/edit")
|
@Resource("/edit")
|
||||||
data class Edit(val parent: Type)
|
data class Edit(val parent: Type)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@Resource("/other/{page}")
|
@Resource("/other/{page}")
|
||||||
data class Other(val parent: Type, val page: Int)
|
data class Other(val parent: Type, val page: Int)
|
||||||
|
Reference in New Issue
Block a user