breaking change: V3 alpha (#256)
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "1.6.21" apply false
|
||||
kotlin("plugin.serialization") version "1.6.21" apply false
|
||||
id("io.bkbn.sourdough.library.jvm") version "0.6.0" apply false
|
||||
id("io.bkbn.sourdough.application.jvm") version "0.6.0" apply false
|
||||
kotlin("jvm") version "1.7.10" apply false
|
||||
kotlin("plugin.serialization") version "1.7.10" apply false
|
||||
id("io.bkbn.sourdough.library.jvm") version "0.9.0" apply false
|
||||
id("io.bkbn.sourdough.application.jvm") version "0.9.0" apply false
|
||||
id("io.bkbn.sourdough.root") version "0.9.0"
|
||||
id("com.github.jakemarsden.git-hooks") version "0.0.2"
|
||||
id("org.jetbrains.dokka") version "1.7.10"
|
||||
|
62
core/build.gradle.kts
Normal file
62
core/build.gradle.kts
Normal file
@ -0,0 +1,62 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization")
|
||||
id("io.bkbn.sourdough.library.jvm")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
id("com.adarshr.test-logger")
|
||||
id("org.jetbrains.dokka")
|
||||
id("maven-publish")
|
||||
id("java-library")
|
||||
id("signing")
|
||||
id("java-test-fixtures")
|
||||
}
|
||||
|
||||
sourdoughLibrary {
|
||||
libraryName.set("Kompendium Core")
|
||||
libraryDescription.set("Core functionality for the Kompendium library")
|
||||
compilerArgs.set(listOf("-opt-in=kotlin.RequiresOptIn"))
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// VERSIONS
|
||||
val kotestVersion: String by project
|
||||
val ktorVersion: String by project
|
||||
|
||||
// IMPLEMENTATION
|
||||
|
||||
api(projects.kompendiumOas)
|
||||
api(projects.kompendiumJsonSchema)
|
||||
|
||||
implementation("io.ktor:ktor-server-core:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-cio:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-html-builder:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
|
||||
implementation("ch.qos.logback:logback-classic:1.2.11")
|
||||
|
||||
// TEST FIXTURES
|
||||
|
||||
testFixturesApi("io.kotest:kotest-runner-junit5-jvm:$kotestVersion")
|
||||
testFixturesApi("io.kotest:kotest-assertions-core-jvm:$kotestVersion")
|
||||
testFixturesApi("io.kotest:kotest-property-jvm:$kotestVersion")
|
||||
testFixturesApi("io.kotest:kotest-assertions-json-jvm:$kotestVersion")
|
||||
testFixturesApi("io.kotest:kotest-assertions-ktor-jvm:4.4.3")
|
||||
|
||||
testFixturesApi("io.ktor:ktor-server-core:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-server-test-host:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-serialization:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-serialization-jackson:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-serialization-gson:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-server-content-negotiation:$ktorVersion")
|
||||
|
||||
testFixturesApi("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3")
|
||||
}
|
||||
|
||||
testing {
|
||||
suites {
|
||||
named("test", JvmTestSuite::class) {
|
||||
useJUnitJupiter()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package io.bkbn.kompendium.core.attribute
|
||||
|
||||
import io.bkbn.kompendium.oas.OpenApiSpec
|
||||
import io.ktor.util.AttributeKey
|
||||
|
||||
object KompendiumAttributes {
|
||||
val openApiSpec = AttributeKey<OpenApiSpec>("OpenApiSpec")
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.bkbn.kompendium.oas.common.ExternalDocumentation
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
|
||||
class DeleteInfo private constructor(
|
||||
override val response: ResponseInfo,
|
||||
override val errors: MutableList<ResponseInfo>,
|
||||
override val tags: Set<String>,
|
||||
override val summary: String,
|
||||
override val description: String,
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
): MethodInfo {
|
||||
|
||||
companion object {
|
||||
fun builder(init: Builder.() -> Unit): DeleteInfo {
|
||||
val builder = Builder()
|
||||
builder.init()
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
class Builder : MethodInfo.Builder<DeleteInfo>() {
|
||||
override fun build() = DeleteInfo(
|
||||
response = response!!,
|
||||
errors = errors,
|
||||
tags = tags,
|
||||
summary = summary!!,
|
||||
description = description!!,
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.bkbn.kompendium.oas.common.ExternalDocumentation
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
|
||||
class GetInfo private constructor(
|
||||
override val response: ResponseInfo,
|
||||
override val errors: MutableList<ResponseInfo>,
|
||||
override val tags: Set<String>,
|
||||
override val summary: String,
|
||||
override val description: String,
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
): MethodInfo {
|
||||
|
||||
companion object {
|
||||
fun builder(init: Builder.() -> Unit): GetInfo {
|
||||
val builder = Builder()
|
||||
builder.init()
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
class Builder : MethodInfo.Builder<GetInfo>() {
|
||||
override fun build() = GetInfo(
|
||||
response = response ?: error("You must provide a response in order to notarize a GET"),
|
||||
errors = errors,
|
||||
tags = tags,
|
||||
summary = summary ?: error("You must provide a summary in order to notarize a GET"),
|
||||
description = description ?: error("You must provide a description in order to notarize a GET"),
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.bkbn.kompendium.oas.common.ExternalDocumentation
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
|
||||
class HeadInfo private constructor(
|
||||
override val response: ResponseInfo,
|
||||
override val errors: MutableList<ResponseInfo>,
|
||||
override val tags: Set<String>,
|
||||
override val summary: String,
|
||||
override val description: String,
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
): MethodInfo {
|
||||
|
||||
companion object {
|
||||
fun builder(init: Builder.() -> Unit): HeadInfo {
|
||||
val builder = Builder()
|
||||
builder.init()
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
class Builder : MethodInfo.Builder<HeadInfo>() {
|
||||
override fun build() = HeadInfo(
|
||||
response = response!!,
|
||||
errors = errors,
|
||||
tags = tags,
|
||||
summary = summary!!,
|
||||
description = description!!,
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.bkbn.kompendium.oas.common.ExternalDocumentation
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
|
||||
sealed interface MethodInfo {
|
||||
val response: ResponseInfo
|
||||
val errors: List<ResponseInfo>
|
||||
val tags: Set<String>
|
||||
val summary: String
|
||||
val description: String
|
||||
val externalDocumentation: ExternalDocumentation?
|
||||
get() = null
|
||||
val operationId: String?
|
||||
get() = null
|
||||
val deprecated: Boolean
|
||||
get() = false
|
||||
val parameters: List<Parameter>
|
||||
get() = emptyList()
|
||||
|
||||
abstract class Builder<T : MethodInfo> {
|
||||
internal var response: ResponseInfo? = null
|
||||
internal var summary: String? = null
|
||||
internal var description: String? = null
|
||||
internal var externalDocumentation: ExternalDocumentation? = null
|
||||
internal var operationId: String? = null
|
||||
internal var deprecated: Boolean = false
|
||||
internal var tags: Set<String> = emptySet()
|
||||
internal var parameters: List<Parameter> = emptyList()
|
||||
internal var errors: MutableList<ResponseInfo> = mutableListOf()
|
||||
|
||||
fun response(init: ResponseInfo.Builder.() -> Unit) = apply {
|
||||
val builder = ResponseInfo.Builder()
|
||||
builder.init()
|
||||
this.response = builder.build()
|
||||
}
|
||||
|
||||
fun canRespond(init: ResponseInfo.Builder.() -> Unit) = apply {
|
||||
val builder = ResponseInfo.Builder()
|
||||
builder.init()
|
||||
errors.add(builder.build())
|
||||
}
|
||||
|
||||
fun canRespond(responses: List<ResponseInfo>) = apply {
|
||||
errors.addAll(responses)
|
||||
}
|
||||
|
||||
fun summary(s: String) = apply { this.summary = s }
|
||||
|
||||
fun description(s: String) = apply { this.description = s }
|
||||
|
||||
fun externalDocumentation(docs: ExternalDocumentation) = apply { this.externalDocumentation = docs }
|
||||
|
||||
fun operationId(id: String) = apply { this.operationId = id }
|
||||
|
||||
fun isDeprecated() = apply { this.deprecated = true }
|
||||
|
||||
fun tags(vararg tags: String) = apply { this.tags = tags.toSet() }
|
||||
|
||||
fun parameters(vararg parameters: Parameter) = apply { this.parameters = parameters.toList() }
|
||||
|
||||
abstract fun build(): T
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
sealed interface MethodInfoWithRequest : MethodInfo {
|
||||
val request: RequestInfo
|
||||
|
||||
abstract class Builder<T: MethodInfoWithRequest> : MethodInfo.Builder<T>() {
|
||||
internal var request: RequestInfo? = null
|
||||
|
||||
fun request(init: RequestInfo.Builder.() -> Unit) = apply {
|
||||
val builder = RequestInfo.Builder()
|
||||
builder.init()
|
||||
this.request = builder.build()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.bkbn.kompendium.oas.common.ExternalDocumentation
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
|
||||
class OptionsInfo private constructor(
|
||||
override val response: ResponseInfo,
|
||||
override val errors: MutableList<ResponseInfo>,
|
||||
override val tags: Set<String>,
|
||||
override val summary: String,
|
||||
override val description: String,
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
): MethodInfo {
|
||||
|
||||
companion object {
|
||||
fun builder(init: Builder.() -> Unit): OptionsInfo {
|
||||
val builder = Builder()
|
||||
builder.init()
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
class Builder : MethodInfo.Builder<OptionsInfo>() {
|
||||
override fun build() = OptionsInfo(
|
||||
response = response!!,
|
||||
errors = errors,
|
||||
tags = tags,
|
||||
summary = summary!!,
|
||||
description = description!!,
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.bkbn.kompendium.oas.common.ExternalDocumentation
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
|
||||
class PatchInfo private constructor(
|
||||
override val request: RequestInfo,
|
||||
override val errors: MutableList<ResponseInfo>,
|
||||
override val response: ResponseInfo,
|
||||
override val tags: Set<String>,
|
||||
override val summary: String,
|
||||
override val description: String,
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
): MethodInfoWithRequest {
|
||||
|
||||
companion object {
|
||||
fun builder(init: Builder.() -> Unit): PatchInfo {
|
||||
val builder = Builder()
|
||||
builder.init()
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
class Builder : MethodInfoWithRequest.Builder<PatchInfo>() {
|
||||
override fun build() = PatchInfo(
|
||||
request = request!!,
|
||||
errors = errors,
|
||||
response = response!!,
|
||||
tags = tags,
|
||||
summary = summary!!,
|
||||
description = description!!,
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.bkbn.kompendium.oas.common.ExternalDocumentation
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
|
||||
class PostInfo private constructor(
|
||||
override val request: RequestInfo,
|
||||
override val errors: MutableList<ResponseInfo>,
|
||||
override val response: ResponseInfo,
|
||||
override val tags: Set<String>,
|
||||
override val summary: String,
|
||||
override val description: String,
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
): MethodInfoWithRequest {
|
||||
|
||||
companion object {
|
||||
fun builder(init: Builder.() -> Unit): PostInfo {
|
||||
val builder = Builder()
|
||||
builder.init()
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
class Builder : MethodInfoWithRequest.Builder<PostInfo>() {
|
||||
override fun build() = PostInfo(
|
||||
request = request!!,
|
||||
errors = errors,
|
||||
response = response!!,
|
||||
tags = tags,
|
||||
summary = summary!!,
|
||||
description = description!!,
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.bkbn.kompendium.oas.common.ExternalDocumentation
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
|
||||
class PutInfo private constructor(
|
||||
override val request: RequestInfo,
|
||||
override val errors: MutableList<ResponseInfo>,
|
||||
override val response: ResponseInfo,
|
||||
override val tags: Set<String>,
|
||||
override val summary: String,
|
||||
override val description: String,
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
): MethodInfoWithRequest {
|
||||
|
||||
companion object {
|
||||
fun builder(init: Builder.() -> Unit): PutInfo {
|
||||
val builder = Builder()
|
||||
builder.init()
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
class Builder : MethodInfoWithRequest.Builder<PutInfo>() {
|
||||
override fun build() = PutInfo(
|
||||
request = request!!,
|
||||
errors = errors,
|
||||
response = response!!,
|
||||
tags = tags,
|
||||
summary = summary!!,
|
||||
description = description!!,
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.bkbn.kompendium.oas.payload.MediaType
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
class RequestInfo private constructor(
|
||||
val requestType: KType,
|
||||
val description: String,
|
||||
val examples: Map<String, MediaType.Example>?
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun builder(init: Builder.() -> Unit): RequestInfo {
|
||||
val builder = Builder()
|
||||
builder.init()
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
class Builder {
|
||||
private var requestType: KType? = null
|
||||
private var description: String? = null
|
||||
private var examples: Map<String, MediaType.Example>? = null
|
||||
|
||||
fun requestType(t: KType) = apply {
|
||||
this.requestType = t
|
||||
}
|
||||
|
||||
inline fun <reified T> requestType() = apply { requestType(typeOf<T>()) }
|
||||
|
||||
fun description(s: String) = apply { this.description = s }
|
||||
|
||||
fun examples(vararg e: Pair<String, Any>) = apply {
|
||||
this.examples = e.toMap().mapValues { (_, v) -> MediaType.Example(v) }
|
||||
println(this.examples)
|
||||
}
|
||||
|
||||
fun build() = RequestInfo(
|
||||
requestType = requestType!!,
|
||||
description = description!!,
|
||||
examples = examples
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.bkbn.kompendium.oas.payload.MediaType
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
class ResponseInfo private constructor(
|
||||
val responseCode: HttpStatusCode,
|
||||
val responseType: KType,
|
||||
val description: String,
|
||||
val examples: Map<String, MediaType.Example>?
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun builder(init: Builder.() -> Unit): ResponseInfo {
|
||||
val builder = Builder()
|
||||
builder.init()
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
class Builder {
|
||||
private var responseCode: HttpStatusCode? = null
|
||||
private var responseType: KType? = null
|
||||
private var description: String? = null
|
||||
private var examples: Map<String, MediaType.Example>? = null
|
||||
|
||||
fun responseCode(code: HttpStatusCode) = apply {
|
||||
this.responseCode = code
|
||||
}
|
||||
|
||||
fun responseType(t: KType) = apply {
|
||||
this.responseType = t
|
||||
}
|
||||
|
||||
inline fun <reified T> responseType() = apply { responseType(typeOf<T>()) }
|
||||
|
||||
fun description(s: String) = apply { this.description = s }
|
||||
|
||||
fun examples(vararg e: Pair<String, Any>) = apply {
|
||||
this.examples = e.toMap().mapValues { (_, v) -> MediaType.Example(v) }
|
||||
println(this.examples)
|
||||
}
|
||||
|
||||
fun build() = ResponseInfo(
|
||||
responseCode = responseCode ?: error("You must provide a response code in order to build a Response!"),
|
||||
responseType = responseType ?: error("You must provide a response type in order to build a Response!"),
|
||||
description = description ?: error("You must provide a description in order to build a Response!"),
|
||||
examples = examples
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package io.bkbn.kompendium.core.plugin
|
||||
|
||||
import io.bkbn.kompendium.core.attribute.KompendiumAttributes
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getSimpleSlug
|
||||
import io.bkbn.kompendium.oas.OpenApiSpec
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.application.createApplicationPlugin
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.application
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.route
|
||||
import io.ktor.server.routing.routing
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
|
||||
object NotarizedApplication {
|
||||
|
||||
class Config {
|
||||
lateinit var spec: OpenApiSpec
|
||||
var openApiJson: Routing.() -> Unit = {
|
||||
route("/openapi.json") {
|
||||
get {
|
||||
call.respond(HttpStatusCode.OK, this@route.application.attributes[KompendiumAttributes.openApiSpec])
|
||||
}
|
||||
}
|
||||
}
|
||||
var customTypes: Map<KType, JsonSchema> = emptyMap()
|
||||
}
|
||||
|
||||
operator fun invoke() = createApplicationPlugin(
|
||||
name = "NotarizedApplication",
|
||||
createConfiguration = ::Config
|
||||
) {
|
||||
val spec = pluginConfig.spec
|
||||
val routing = application.routing { }
|
||||
pluginConfig.openApiJson(routing)
|
||||
pluginConfig.customTypes.forEach { (type, schema) ->
|
||||
spec.components.schemas[type.getSimpleSlug()] = schema
|
||||
}
|
||||
application.attributes.put(KompendiumAttributes.openApiSpec, spec)
|
||||
}
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
package io.bkbn.kompendium.core.plugin
|
||||
|
||||
import io.bkbn.kompendium.core.attribute.KompendiumAttributes
|
||||
import io.bkbn.kompendium.core.metadata.DeleteInfo
|
||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||
import io.bkbn.kompendium.core.metadata.HeadInfo
|
||||
import io.bkbn.kompendium.core.metadata.MethodInfo
|
||||
import io.bkbn.kompendium.core.metadata.MethodInfoWithRequest
|
||||
import io.bkbn.kompendium.core.metadata.OptionsInfo
|
||||
import io.bkbn.kompendium.core.metadata.PatchInfo
|
||||
import io.bkbn.kompendium.core.metadata.PostInfo
|
||||
import io.bkbn.kompendium.core.metadata.PutInfo
|
||||
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
||||
import io.bkbn.kompendium.core.util.Helpers.getReferenceSlug
|
||||
import io.bkbn.kompendium.core.util.Helpers.getSimpleSlug
|
||||
import io.bkbn.kompendium.json.schema.SchemaGenerator
|
||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||
import io.bkbn.kompendium.oas.OpenApiSpec
|
||||
import io.bkbn.kompendium.oas.path.Path
|
||||
import io.bkbn.kompendium.oas.path.PathOperation
|
||||
import io.bkbn.kompendium.oas.payload.MediaType
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.bkbn.kompendium.oas.payload.Request
|
||||
import io.bkbn.kompendium.oas.payload.Response
|
||||
import io.ktor.server.application.ApplicationCallPipeline
|
||||
import io.ktor.server.application.Hook
|
||||
import io.ktor.server.application.createRouteScopedPlugin
|
||||
import io.ktor.server.routing.Route
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
|
||||
object NotarizedRoute {
|
||||
|
||||
class Config {
|
||||
var tags: Set<String> = emptySet()
|
||||
var parameters: List<Parameter> = emptyList()
|
||||
var get: GetInfo? = null
|
||||
var post: PostInfo? = null
|
||||
var put: PutInfo? = null
|
||||
var delete: DeleteInfo? = null
|
||||
var patch: PatchInfo? = null
|
||||
var head: HeadInfo? = null
|
||||
var options: OptionsInfo? = null
|
||||
var security: Map<String, List<String>>? = null
|
||||
internal var path: Path? = null
|
||||
}
|
||||
|
||||
private object InstallHook : Hook<(ApplicationCallPipeline) -> Unit> {
|
||||
override fun install(pipeline: ApplicationCallPipeline, handler: (ApplicationCallPipeline) -> Unit) {
|
||||
handler(pipeline)
|
||||
}
|
||||
}
|
||||
|
||||
operator fun invoke() = createRouteScopedPlugin(
|
||||
name = "NotarizedRoute",
|
||||
createConfiguration = ::Config
|
||||
) {
|
||||
|
||||
// This is required in order to introspect the route path
|
||||
on(InstallHook) {
|
||||
val route = it as? Route ?: return@on
|
||||
val spec = application.attributes[KompendiumAttributes.openApiSpec]
|
||||
val routePath = route.calculateRoutePath()
|
||||
require(spec.paths[routePath] == null) {
|
||||
"""
|
||||
The specified path ${Parameter.Location.path} has already been documented!
|
||||
Please make sure that all notarized paths are unique
|
||||
""".trimIndent()
|
||||
}
|
||||
spec.paths[routePath] = pluginConfig.path!!
|
||||
}
|
||||
|
||||
val spec = application.attributes[KompendiumAttributes.openApiSpec]
|
||||
|
||||
val path = Path()
|
||||
path.parameters = pluginConfig.parameters
|
||||
|
||||
pluginConfig.get?.addToSpec(path, spec, pluginConfig)
|
||||
pluginConfig.delete?.addToSpec(path, spec, pluginConfig)
|
||||
pluginConfig.head?.addToSpec(path, spec, pluginConfig)
|
||||
pluginConfig.options?.addToSpec(path, spec, pluginConfig)
|
||||
pluginConfig.post?.addToSpec(path, spec, pluginConfig)
|
||||
pluginConfig.put?.addToSpec(path, spec, pluginConfig)
|
||||
pluginConfig.patch?.addToSpec(path, spec, pluginConfig)
|
||||
|
||||
pluginConfig.path = path
|
||||
}
|
||||
|
||||
private fun MethodInfo.addToSpec(path: Path, spec: OpenApiSpec, config: Config) {
|
||||
SchemaGenerator.fromTypeOrUnit(this.response.responseType, spec.components.schemas)?.let { schema ->
|
||||
spec.components.schemas[this.response.responseType.getSimpleSlug()] = schema
|
||||
}
|
||||
|
||||
errors.forEach { error ->
|
||||
SchemaGenerator.fromTypeOrUnit(error.responseType, spec.components.schemas)?.let { schema ->
|
||||
spec.components.schemas[error.responseType.getSimpleSlug()] = schema
|
||||
}
|
||||
}
|
||||
|
||||
when (this) {
|
||||
is MethodInfoWithRequest -> {
|
||||
SchemaGenerator.fromTypeOrUnit(this.request.requestType, spec.components.schemas)?.let { schema ->
|
||||
spec.components.schemas[this.request.requestType.getSimpleSlug()] = schema
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
val operations = this.toPathOperation(config)
|
||||
|
||||
when (this) {
|
||||
is DeleteInfo -> path.delete = operations
|
||||
is GetInfo -> path.get = operations
|
||||
is HeadInfo -> path.head = operations
|
||||
is PatchInfo -> path.patch = operations
|
||||
is PostInfo -> path.post = operations
|
||||
is PutInfo -> path.put = operations
|
||||
is OptionsInfo -> path.options = operations
|
||||
}
|
||||
}
|
||||
|
||||
private fun MethodInfo.toPathOperation(config: Config) = PathOperation(
|
||||
tags = config.tags.plus(this.tags),
|
||||
summary = this.summary,
|
||||
description = this.description,
|
||||
externalDocs = this.externalDocumentation,
|
||||
operationId = this.operationId,
|
||||
deprecated = this.deprecated,
|
||||
parameters = this.parameters,
|
||||
security = config.security
|
||||
?.map { (k, v) -> k to v }
|
||||
?.map { listOf(it).toMap() }
|
||||
?.toList(),
|
||||
requestBody = when (this) {
|
||||
is MethodInfoWithRequest -> Request(
|
||||
description = this.request.description,
|
||||
content = this.request.requestType.toReferenceContent(this.request.examples),
|
||||
required = true
|
||||
)
|
||||
|
||||
else -> null
|
||||
},
|
||||
responses = mapOf(
|
||||
this.response.responseCode.value to Response(
|
||||
description = this.response.description,
|
||||
content = this.response.responseType.toReferenceContent(this.response.examples)
|
||||
)
|
||||
).plus(this.errors.toResponseMap())
|
||||
)
|
||||
|
||||
private fun List<ResponseInfo>.toResponseMap(): Map<Int, Response> = associate { error ->
|
||||
error.responseCode.value to Response(
|
||||
description = error.description,
|
||||
content = error.responseType.toReferenceContent(error.examples)
|
||||
)
|
||||
}
|
||||
|
||||
private fun KType.toReferenceContent(examples: Map<String, MediaType.Example>?): Map<String, MediaType>? =
|
||||
when (this.classifier as KClass<*>) {
|
||||
Unit::class -> null
|
||||
else -> mapOf(
|
||||
"application/json" to MediaType(
|
||||
schema = ReferenceDefinition(this.getReferenceSlug()),
|
||||
examples = examples
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun Route.calculateRoutePath() = toString().replace(Regex("/\\(.+\\)"), "")
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
package io.bkbn.kompendium.core.routes
|
||||
|
||||
import io.ktor.application.call
|
||||
import io.ktor.html.respondHtml
|
||||
import io.ktor.routing.Routing
|
||||
import io.ktor.routing.get
|
||||
import io.ktor.routing.route
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.html.respondHtml
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.route
|
||||
import kotlinx.html.body
|
||||
import kotlinx.html.head
|
||||
import kotlinx.html.link
|
||||
@ -22,7 +23,7 @@ import kotlinx.html.unsafe
|
||||
fun Routing.redoc(pageTitle: String = "Docs", specUrl: String = "/openapi.json") {
|
||||
route("/docs") {
|
||||
get {
|
||||
call.respondHtml {
|
||||
call.respondHtml(HttpStatusCode.OK) {
|
||||
head {
|
||||
title {
|
||||
+pageTitle
|
35
core/src/main/kotlin/io/bkbn/kompendium/core/util/Helpers.kt
Normal file
35
core/src/main/kotlin/io/bkbn/kompendium/core/util/Helpers.kt
Normal file
@ -0,0 +1,35 @@
|
||||
package io.bkbn.kompendium.core.util
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createType
|
||||
import kotlin.reflect.jvm.javaField
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.util.Locale
|
||||
|
||||
object Helpers {
|
||||
|
||||
private const val COMPONENT_SLUG = "#/components/schemas"
|
||||
|
||||
fun KType.getSimpleSlug(): String = when {
|
||||
this.arguments.isNotEmpty() -> genericNameAdapter(this, classifier as KClass<*>)
|
||||
else -> (classifier as KClass<*>).simpleName ?: error("Could not determine simple name for $this")
|
||||
}
|
||||
|
||||
fun KType.getReferenceSlug(): String = when {
|
||||
arguments.isNotEmpty() -> "$COMPONENT_SLUG/${genericNameAdapter(this, classifier as KClass<*>)}"
|
||||
else -> "$COMPONENT_SLUG/${(classifier as KClass<*>).simpleName}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts a class with type parameters into a reference friendly string
|
||||
*/
|
||||
private fun genericNameAdapter(type: KType, clazz: KClass<*>): String {
|
||||
val classNames = type.arguments
|
||||
.map { it.type?.classifier as KClass<*> }
|
||||
.map { it.simpleName }
|
||||
return classNames.joinToString(separator = "-", prefix = "${clazz.simpleName}-")
|
||||
}
|
||||
}
|
201
core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumTest.kt
Normal file
201
core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumTest.kt
Normal file
@ -0,0 +1,201 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
||||
import io.bkbn.kompendium.core.util.TestModules.complexRequest
|
||||
import io.bkbn.kompendium.core.util.TestModules.dateTimeString
|
||||
import io.bkbn.kompendium.core.util.TestModules.defaultField
|
||||
import io.bkbn.kompendium.core.util.TestModules.defaultParameter
|
||||
import io.bkbn.kompendium.core.util.TestModules.exampleParams
|
||||
import io.bkbn.kompendium.core.util.TestModules.nestedUnderRoot
|
||||
import io.bkbn.kompendium.core.util.TestModules.nonRequiredParams
|
||||
import io.bkbn.kompendium.core.util.TestModules.notarizedDelete
|
||||
import io.bkbn.kompendium.core.util.TestModules.notarizedGet
|
||||
import io.bkbn.kompendium.core.util.TestModules.singleException
|
||||
import io.bkbn.kompendium.core.util.TestModules.genericException
|
||||
import io.bkbn.kompendium.core.util.TestModules.genericPolymorphicResponse
|
||||
import io.bkbn.kompendium.core.util.TestModules.genericPolymorphicResponseMultipleImpls
|
||||
import io.bkbn.kompendium.core.util.TestModules.headerParameter
|
||||
import io.bkbn.kompendium.core.util.TestModules.multipleExceptions
|
||||
import io.bkbn.kompendium.core.util.TestModules.nestedGenericResponse
|
||||
import io.bkbn.kompendium.core.util.TestModules.nonRequiredParam
|
||||
import io.bkbn.kompendium.core.util.TestModules.polymorphicException
|
||||
import io.bkbn.kompendium.core.util.TestModules.notarizedHead
|
||||
import io.bkbn.kompendium.core.util.TestModules.notarizedOptions
|
||||
import io.bkbn.kompendium.core.util.TestModules.notarizedPatch
|
||||
import io.bkbn.kompendium.core.util.TestModules.notarizedPost
|
||||
import io.bkbn.kompendium.core.util.TestModules.notarizedPut
|
||||
import io.bkbn.kompendium.core.util.TestModules.nullableEnumField
|
||||
import io.bkbn.kompendium.core.util.TestModules.nullableField
|
||||
import io.bkbn.kompendium.core.util.TestModules.nullableNestedObject
|
||||
import io.bkbn.kompendium.core.util.TestModules.polymorphicCollectionResponse
|
||||
import io.bkbn.kompendium.core.util.TestModules.polymorphicMapResponse
|
||||
import io.bkbn.kompendium.core.util.TestModules.polymorphicResponse
|
||||
import io.bkbn.kompendium.core.util.TestModules.primitives
|
||||
import io.bkbn.kompendium.core.util.TestModules.reqRespExamples
|
||||
import io.bkbn.kompendium.core.util.TestModules.requiredParams
|
||||
import io.bkbn.kompendium.core.util.TestModules.returnsList
|
||||
import io.bkbn.kompendium.core.util.TestModules.rootRoute
|
||||
import io.bkbn.kompendium.core.util.TestModules.simpleGenericResponse
|
||||
import io.bkbn.kompendium.core.util.TestModules.simplePathParsing
|
||||
import io.bkbn.kompendium.core.util.TestModules.trailingSlash
|
||||
import io.bkbn.kompendium.core.util.TestModules.withOperationId
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
import kotlin.reflect.typeOf
|
||||
import java.time.Instant
|
||||
|
||||
class KompendiumTest : DescribeSpec({
|
||||
describe("Notarized Open API Metadata Tests") {
|
||||
it("Can notarize a get request") {
|
||||
openApiTestAllSerializers("T0001__notarized_get.json") { notarizedGet() }
|
||||
}
|
||||
it("Can notarize a post request") {
|
||||
openApiTestAllSerializers("T0002__notarized_post.json") { notarizedPost() }
|
||||
}
|
||||
it("Can notarize a put request") {
|
||||
openApiTestAllSerializers("T0003__notarized_put.json") { notarizedPut() }
|
||||
}
|
||||
it("Can notarize a delete request") {
|
||||
openApiTestAllSerializers("T0004__notarized_delete.json") { notarizedDelete() }
|
||||
}
|
||||
it("Can notarize a patch request") {
|
||||
openApiTestAllSerializers("T0005__notarized_patch.json") { notarizedPatch() }
|
||||
}
|
||||
it("Can notarize a head request") {
|
||||
openApiTestAllSerializers("T0006__notarized_head.json") { notarizedHead() }
|
||||
}
|
||||
it("Can notarize an options request") {
|
||||
openApiTestAllSerializers("T0007__notarized_options.json") { notarizedOptions() }
|
||||
}
|
||||
it("Can notarize a complex type") {
|
||||
openApiTestAllSerializers("T0008__complex_type.json") { complexRequest() }
|
||||
}
|
||||
it("Can notarize primitives") {
|
||||
openApiTestAllSerializers("T0009__notarized_primitives.json") { primitives() }
|
||||
}
|
||||
it("Can notarize a top level list response") {
|
||||
openApiTestAllSerializers("T0010__response_list.json") { returnsList() }
|
||||
}
|
||||
it("Can notarize a route with non-required params") {
|
||||
openApiTestAllSerializers("T0011__non_required_params.json") { nonRequiredParams() }
|
||||
}
|
||||
}
|
||||
describe("Route Parsing") {
|
||||
it("Can parse a simple path and store it under the expected route") {
|
||||
openApiTestAllSerializers("T0012__path_parser.json") { simplePathParsing() }
|
||||
}
|
||||
it("Can notarize the root route") {
|
||||
openApiTestAllSerializers("T0013__root_route.json") { rootRoute() }
|
||||
}
|
||||
it("Can notarize a route under the root module without appending trailing slash") {
|
||||
openApiTestAllSerializers("T0014__nested_under_root.json") { nestedUnderRoot() }
|
||||
}
|
||||
it("Can notarize a route with a trailing slash") {
|
||||
openApiTestAllSerializers("T0015__trailing_slash.json") { trailingSlash() }
|
||||
}
|
||||
}
|
||||
describe("Exceptions") {
|
||||
it("Can add an exception status code to a response") {
|
||||
openApiTestAllSerializers("T0016__notarized_get_with_exception_response.json") { singleException() }
|
||||
}
|
||||
it("Can support multiple response codes") {
|
||||
openApiTestAllSerializers("T0017__notarized_get_with_multiple_exception_responses.json") { multipleExceptions() }
|
||||
}
|
||||
it("Can add a polymorphic exception response") {
|
||||
openApiTestAllSerializers("T0018__polymorphic_error_status_codes.json") { polymorphicException() }
|
||||
}
|
||||
it("Can add a generic exception response") {
|
||||
openApiTestAllSerializers("T0019__generic_exception.json") { genericException() }
|
||||
}
|
||||
}
|
||||
describe("Examples") {
|
||||
it("Can generate example response and request bodies") {
|
||||
openApiTestAllSerializers("T0020__example_req_and_resp.json") { reqRespExamples() }
|
||||
}
|
||||
it("Can describe example parameters") {
|
||||
openApiTestAllSerializers("T0021__example_parameters.json") { exampleParams() }
|
||||
}
|
||||
}
|
||||
describe("Defaults") {
|
||||
it("Can generate a default parameter value") {
|
||||
openApiTestAllSerializers("T0022__query_with_default_parameter.json") { defaultParameter() }
|
||||
}
|
||||
}
|
||||
describe("Required Fields") {
|
||||
it("Marks a parameter as required if there is no default and it is not marked nullable") {
|
||||
openApiTestAllSerializers("T0023__required_param.json") { requiredParams() }
|
||||
}
|
||||
it("Can mark a parameter as not required") {
|
||||
openApiTestAllSerializers("T0024__non_required_param.json") { nonRequiredParam() }
|
||||
}
|
||||
it("Does not mark a field as required if a default value is provided") {
|
||||
openApiTestAllSerializers("T0025__default_field.json") { defaultField() }
|
||||
}
|
||||
it("Does not mark a nullable field as required") {
|
||||
openApiTestAllSerializers("T0026__nullable_field.json") { nullableField() }
|
||||
}
|
||||
}
|
||||
describe("Polymorphism and Generics") {
|
||||
it("can generate a polymorphic response type") {
|
||||
openApiTestAllSerializers("T0027__polymorphic_response.json") { polymorphicResponse() }
|
||||
}
|
||||
it("Can generate a collection with polymorphic response type") {
|
||||
openApiTestAllSerializers("T0028__polymorphic_list_response.json") { polymorphicCollectionResponse() }
|
||||
}
|
||||
it("Can generate a map with a polymorphic response type") {
|
||||
openApiTestAllSerializers("T0029__polymorphic_map_response.json") { polymorphicMapResponse() }
|
||||
}
|
||||
it("Can generate a response type with a generic type") {
|
||||
openApiTestAllSerializers("T0030__simple_generic_response.json") { simpleGenericResponse() }
|
||||
}
|
||||
it("Can generate a response type with a nested generic type") {
|
||||
openApiTestAllSerializers("T0031__nested_generic_response.json") { nestedGenericResponse() }
|
||||
}
|
||||
it("Can generate a polymorphic response type with generics") {
|
||||
openApiTestAllSerializers("T0032__polymorphic_response_with_generics.json") { genericPolymorphicResponse() }
|
||||
}
|
||||
it("Can handle an absolutely psycho inheritance test") {
|
||||
openApiTestAllSerializers("T0033__crazy_polymorphic_example.json") { genericPolymorphicResponseMultipleImpls() }
|
||||
}
|
||||
}
|
||||
describe("Miscellaneous") {
|
||||
xit("Can generate the necessary ReDoc home page") {
|
||||
// TODO apiFunctionalityTest(getFileSnapshot("redoc.html"), "/docs") { returnsList() }
|
||||
}
|
||||
it("Can add an operation id to a notarized route") {
|
||||
openApiTestAllSerializers("T0034__notarized_get_with_operation_id.json") { withOperationId() }
|
||||
}
|
||||
xit("Can add an undeclared field") {
|
||||
// TODO openApiTestAllSerializers("undeclared_field.json") { undeclaredType() }
|
||||
}
|
||||
it("Can add a custom header parameter with a name override") {
|
||||
openApiTestAllSerializers("T0035__override_parameter_name.json") { headerParameter() }
|
||||
}
|
||||
xit("Can override field name") {
|
||||
// TODO Assess strategies here
|
||||
}
|
||||
xit("Can serialize a recursive type") {
|
||||
// TODO openApiTestAllSerializers("simple_recursive.json") { simpleRecursive() }
|
||||
}
|
||||
it("Nullable fields do not lead to doom") {
|
||||
openApiTestAllSerializers("T0036__nullable_fields.json") { nullableNestedObject() }
|
||||
}
|
||||
it("Can have a nullable enum as a member field") {
|
||||
openApiTestAllSerializers("T0037__nullable_enum_field.json") { nullableEnumField() }
|
||||
}
|
||||
}
|
||||
describe("Constraints") {
|
||||
// TODO Assess strategies here
|
||||
}
|
||||
describe("Formats") {
|
||||
it("Can set a format for a simple type schema") {
|
||||
openApiTestAllSerializers(
|
||||
snapshotName = "T0038__formatted_date_time_string.json",
|
||||
customTypes = mapOf(typeOf<Instant>() to TypeDefinition(type = "string", format = "date"))
|
||||
) { dateTimeString() }
|
||||
}
|
||||
}
|
||||
describe("Free Form") {
|
||||
// todo Assess strategies here
|
||||
}
|
||||
})
|
609
core/src/test/kotlin/io/bkbn/kompendium/core/util/TestModules.kt
Normal file
609
core/src/test/kotlin/io/bkbn/kompendium/core/util/TestModules.kt
Normal file
@ -0,0 +1,609 @@
|
||||
package io.bkbn.kompendium.core.util
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.ColumnSchema
|
||||
import io.bkbn.kompendium.core.fixtures.ComplexRequest
|
||||
import io.bkbn.kompendium.core.fixtures.DateTimeString
|
||||
import io.bkbn.kompendium.core.fixtures.DefaultField
|
||||
import io.bkbn.kompendium.core.fixtures.ExceptionResponse
|
||||
import io.bkbn.kompendium.core.fixtures.Flibbity
|
||||
import io.bkbn.kompendium.core.fixtures.FlibbityGibbit
|
||||
import io.bkbn.kompendium.core.fixtures.Gibbity
|
||||
import io.bkbn.kompendium.core.fixtures.NullableEnum
|
||||
import io.bkbn.kompendium.core.fixtures.NullableField
|
||||
import io.bkbn.kompendium.core.fixtures.ProfileUpdateRequest
|
||||
import io.bkbn.kompendium.core.fixtures.TestCreatedResponse
|
||||
import io.bkbn.kompendium.core.fixtures.TestNested
|
||||
import io.bkbn.kompendium.core.fixtures.TestRequest
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||
import io.bkbn.kompendium.core.fixtures.TestSimpleRequest
|
||||
import io.bkbn.kompendium.core.metadata.DeleteInfo
|
||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||
import io.bkbn.kompendium.core.metadata.HeadInfo
|
||||
import io.bkbn.kompendium.core.metadata.OptionsInfo
|
||||
import io.bkbn.kompendium.core.metadata.PatchInfo
|
||||
import io.bkbn.kompendium.core.metadata.PostInfo
|
||||
import io.bkbn.kompendium.core.metadata.PutInfo
|
||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.response.respondText
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.delete
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.head
|
||||
import io.ktor.server.routing.options
|
||||
import io.ktor.server.routing.patch
|
||||
import io.ktor.server.routing.post
|
||||
import io.ktor.server.routing.put
|
||||
import io.ktor.server.routing.route
|
||||
|
||||
object TestModules {
|
||||
private const val defaultPath = "/test/{a}"
|
||||
private const val rootPath = "/"
|
||||
private const val defaultResponseDescription = "A Successful Endeavor"
|
||||
private const val defaultRequestDescription = "You gotta send it"
|
||||
private const val defaultPathSummary = "Great Summary!"
|
||||
private const val defaultPathDescription = "testing more"
|
||||
|
||||
private val defaultParams = listOf(
|
||||
Parameter(
|
||||
name = "a",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.STRING,
|
||||
),
|
||||
Parameter(
|
||||
name = "aa",
|
||||
`in` = Parameter.Location.query,
|
||||
schema = TypeDefinition.INT
|
||||
)
|
||||
)
|
||||
|
||||
fun Routing.notarizedGet() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
get = GetInfo.builder {
|
||||
response {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
description(defaultResponseDescription)
|
||||
}
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
}
|
||||
}
|
||||
get {
|
||||
call.respondText { "hey dude ‼️ congrats on the get request" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedPost() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
post = PostInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
request {
|
||||
requestType<TestSimpleRequest>()
|
||||
description("A Test request")
|
||||
}
|
||||
response {
|
||||
responseCode(HttpStatusCode.Created)
|
||||
responseType<TestCreatedResponse>()
|
||||
description(defaultResponseDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
call.respondText { "hey dude ‼️ congrats on the post request" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedPut() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
put = PutInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
request {
|
||||
requestType<TestSimpleRequest>()
|
||||
description("A Test request")
|
||||
}
|
||||
response {
|
||||
responseCode(HttpStatusCode.Created)
|
||||
responseType<TestCreatedResponse>()
|
||||
description(defaultResponseDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
put {
|
||||
call.respondText { "hey dude ‼️ congrats on the post request" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedDelete() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
delete = DeleteInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
responseCode(HttpStatusCode.NoContent)
|
||||
responseType<Unit>()
|
||||
description(defaultResponseDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete {
|
||||
call.respond(HttpStatusCode.NoContent)
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedPatch() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
patch = PatchInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
request {
|
||||
description("A Test request")
|
||||
requestType<TestSimpleRequest>()
|
||||
}
|
||||
response {
|
||||
responseCode(HttpStatusCode.Created)
|
||||
responseType<TestCreatedResponse>()
|
||||
description(defaultResponseDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
patch {
|
||||
call.respond(HttpStatusCode.Created) { TestCreatedResponse(123, "Nice!") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedHead() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
head = HeadInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
|
||||
response {
|
||||
description("great!")
|
||||
responseCode(HttpStatusCode.Created)
|
||||
responseType<Unit>()
|
||||
}
|
||||
}
|
||||
}
|
||||
head {
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedOptions() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
options = OptionsInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
description("nice")
|
||||
}
|
||||
}
|
||||
}
|
||||
options {
|
||||
call.respond(HttpStatusCode.NoContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.complexRequest() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
put = PutInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
request {
|
||||
requestType<ComplexRequest>()
|
||||
description("A Complex request")
|
||||
}
|
||||
response {
|
||||
responseCode(HttpStatusCode.Created)
|
||||
responseType<TestCreatedResponse>()
|
||||
description(defaultResponseDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
patch {
|
||||
call.respond(HttpStatusCode.Created, TestCreatedResponse(123, "nice!"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.primitives() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
put = PutInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
request {
|
||||
requestType<Int>()
|
||||
description("A Test Request")
|
||||
}
|
||||
response {
|
||||
responseCode(HttpStatusCode.Created)
|
||||
responseType<Boolean>()
|
||||
description(defaultResponseDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.returnsList() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description("A Successful List-y Endeavor")
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<List<TestResponse>>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.nonRequiredParams() {
|
||||
route("/optional") {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
name = "notRequired",
|
||||
`in` = Parameter.Location.query,
|
||||
schema = TypeDefinition.STRING,
|
||||
required = false,
|
||||
),
|
||||
Parameter(
|
||||
name = "required",
|
||||
`in` = Parameter.Location.query,
|
||||
schema = TypeDefinition.STRING
|
||||
)
|
||||
)
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
responseType<Unit>()
|
||||
description("Empty")
|
||||
responseCode(HttpStatusCode.NoContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.simplePathParsing() {
|
||||
route("/this") {
|
||||
route("/is") {
|
||||
route("/a") {
|
||||
route("/complex") {
|
||||
route("path") {
|
||||
route("with/an/{id}") {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
name = "id",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.STRING
|
||||
)
|
||||
)
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.rootRoute() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = listOf(defaultParams.last())
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.nestedUnderRoot() {
|
||||
route("/") {
|
||||
route("/testerino") {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.trailingSlash() {
|
||||
route("/test") {
|
||||
route("/") {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.singleException() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
}
|
||||
canRespond {
|
||||
description("Bad Things Happened")
|
||||
responseCode(HttpStatusCode.BadRequest)
|
||||
responseType<ExceptionResponse>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.multipleExceptions() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
}
|
||||
canRespond {
|
||||
description("Bad Things Happened")
|
||||
responseCode(HttpStatusCode.BadRequest)
|
||||
responseType<ExceptionResponse>()
|
||||
}
|
||||
canRespond {
|
||||
description("Access Denied")
|
||||
responseCode(HttpStatusCode.Forbidden)
|
||||
responseType<ExceptionResponse>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.polymorphicException() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
}
|
||||
canRespond {
|
||||
description("Bad Things Happened")
|
||||
responseCode(HttpStatusCode.InternalServerError)
|
||||
responseType<FlibbityGibbit>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.genericException() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
}
|
||||
canRespond {
|
||||
description("Bad Things Happened")
|
||||
responseCode(HttpStatusCode.BadRequest)
|
||||
responseType<Flibbity<String>>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.reqRespExamples() {
|
||||
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())
|
||||
)
|
||||
}
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
examples(
|
||||
"Testerino" to TestResponse("Heya")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.exampleParams() = basicGetGenerator<TestResponse>(
|
||||
params = listOf(
|
||||
Parameter(
|
||||
name = "id",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.STRING,
|
||||
examples = mapOf(
|
||||
"foo" to Parameter.Example("testing")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fun Routing.defaultParameter() = basicGetGenerator<TestResponse>(
|
||||
params = listOf(
|
||||
Parameter(
|
||||
name = "id",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.STRING.withDefault("IDK")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fun Routing.requiredParams() = basicGetGenerator<TestResponse>(
|
||||
params = listOf(
|
||||
Parameter(
|
||||
name = "id",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.STRING
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fun Routing.nonRequiredParam() = basicGetGenerator<TestResponse>(
|
||||
params = listOf(
|
||||
Parameter(
|
||||
name = "id",
|
||||
`in` = Parameter.Location.query,
|
||||
schema = TypeDefinition.STRING,
|
||||
required = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fun Routing.defaultField() = basicGetGenerator<DefaultField>()
|
||||
|
||||
fun Routing.nullableField() = basicGetGenerator<NullableField>()
|
||||
|
||||
fun Routing.polymorphicResponse() = basicGetGenerator<FlibbityGibbit>()
|
||||
|
||||
fun Routing.polymorphicCollectionResponse() = basicGetGenerator<List<FlibbityGibbit>>()
|
||||
|
||||
fun Routing.polymorphicMapResponse() = basicGetGenerator<Map<String, FlibbityGibbit>>()
|
||||
|
||||
fun Routing.simpleGenericResponse() = basicGetGenerator<Gibbity<String>>()
|
||||
|
||||
fun Routing.nestedGenericResponse() = basicGetGenerator<Gibbity<Map<String, String>>>()
|
||||
|
||||
fun Routing.genericPolymorphicResponse() = basicGetGenerator<Flibbity<Double>>()
|
||||
|
||||
fun Routing.genericPolymorphicResponseMultipleImpls() = basicGetGenerator<Flibbity<FlibbityGibbit>>()
|
||||
|
||||
fun Routing.withOperationId() = basicGetGenerator<TestResponse>(operationId = "getThisDude")
|
||||
|
||||
fun Routing.nullableNestedObject() = basicGetGenerator<ProfileUpdateRequest>()
|
||||
|
||||
fun Routing.nullableEnumField() = basicGetGenerator<NullableEnum>()
|
||||
|
||||
fun Routing.dateTimeString() = basicGetGenerator<DateTimeString>()
|
||||
|
||||
fun Routing.headerParameter() = basicGetGenerator<TestResponse>( params = listOf(
|
||||
Parameter(
|
||||
name = "X-User-Email",
|
||||
`in` = Parameter.Location.header,
|
||||
schema = TypeDefinition.STRING,
|
||||
required = true
|
||||
)
|
||||
))
|
||||
|
||||
fun Routing.simpleRecursive() = basicGetGenerator<ColumnSchema>()
|
||||
|
||||
private inline fun <reified T> Routing.basicGetGenerator(
|
||||
params: List<Parameter> = emptyList(),
|
||||
operationId: String? = null
|
||||
) {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
operationId?.let { operationId(it) }
|
||||
parameters = params
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<T>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,33 +27,12 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/test/{a}": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"operationId": "getTest",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
@ -66,12 +46,35 @@
|
||||
}
|
||||
},
|
||||
"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"
|
||||
@ -79,8 +82,7 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,38 +27,18 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/test/{a}": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "Test post endpoint",
|
||||
"description": "Post your tests here!",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "A Test request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestRequest"
|
||||
"$ref": "#/components/schemas/TestSimpleRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -76,61 +57,64 @@
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"TestRequest": {
|
||||
"properties": {
|
||||
"aaa": {
|
||||
"items": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"b": {
|
||||
"format": "double",
|
||||
"type": "number"
|
||||
},
|
||||
"field_name": {
|
||||
"$ref": "#/components/schemas/TestNested"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"field_name",
|
||||
"b",
|
||||
"aaa"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TestNested": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TestCreatedResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
"c",
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"TestSimpleRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,38 +27,18 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/test/{a}": {
|
||||
"put": {
|
||||
"tags": [],
|
||||
"summary": "Test put endpoint",
|
||||
"description": "Put your tests here!",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "A Test request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestRequest"
|
||||
"$ref": "#/components/schemas/TestSimpleRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -76,61 +57,64 @@
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"TestRequest": {
|
||||
"properties": {
|
||||
"aaa": {
|
||||
"items": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"b": {
|
||||
"format": "double",
|
||||
"type": "number"
|
||||
},
|
||||
"field_name": {
|
||||
"$ref": "#/components/schemas/TestNested"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"field_name",
|
||||
"b",
|
||||
"aaa"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TestNested": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TestCreatedResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
"c",
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"TestSimpleRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,47 +27,45 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/polymorphic": {
|
||||
"get": {
|
||||
"/test/{a}": {
|
||||
"delete": {
|
||||
"tags": [],
|
||||
"summary": "Single Generic",
|
||||
"description": "Simple generic data class",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestGeneric-Int"
|
||||
}
|
||||
}
|
||||
}
|
||||
"204": {
|
||||
"description": "A Successful Endeavor"
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestGeneric-Int": {
|
||||
"properties": {
|
||||
"messy": {
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"potato": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
"required": [
|
||||
"messy",
|
||||
"potato"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,18 +27,18 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/test/{a}": {
|
||||
"patch": {
|
||||
"tags": [],
|
||||
"summary": "Test patch endpoint",
|
||||
"description": "patch your tests here!",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "A Test request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestRequest"
|
||||
"$ref": "#/components/schemas/TestSimpleRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -49,63 +50,71 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
"$ref": "#/components/schemas/TestCreatedResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"TestRequest": {
|
||||
"properties": {
|
||||
"aaa": {
|
||||
"items": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"b": {
|
||||
"format": "double",
|
||||
"type": "number"
|
||||
},
|
||||
"field_name": {
|
||||
"$ref": "#/components/schemas/TestNested"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"field_name",
|
||||
"b",
|
||||
"aaa"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TestNested": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TestResponse": {
|
||||
"TestCreatedResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
"c",
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"TestSimpleRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,21 +27,43 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/test/{a}": {
|
||||
"head": {
|
||||
"tags": [],
|
||||
"summary": "Test head endpoint",
|
||||
"description": "head test 💀",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"201": {
|
||||
"description": "great!"
|
||||
}
|
||||
},
|
||||
"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": {},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,32 +27,12 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/test/{a}": {
|
||||
"options": {
|
||||
"tags": [],
|
||||
"summary": "Test options",
|
||||
"description": "endpoint of options",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "nice",
|
||||
@ -65,12 +46,35 @@
|
||||
}
|
||||
},
|
||||
"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"
|
||||
@ -78,8 +82,7 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,11 +27,11 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/": {
|
||||
"put": {
|
||||
"tags": [],
|
||||
"summary": "Test put endpoint",
|
||||
"description": "Put your tests here!",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "A Complex request",
|
||||
@ -56,14 +57,33 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ComplexRequest": {
|
||||
"TestCreatedResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amazing_field": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c",
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"ComplexRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amazingField": {
|
||||
"type": "string"
|
||||
},
|
||||
"org": {
|
||||
@ -77,13 +97,13 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"amazingField",
|
||||
"org",
|
||||
"amazing_field",
|
||||
"tables"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"NestedComplexItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alias": {
|
||||
"additionalProperties": {
|
||||
@ -96,44 +116,23 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"alias"
|
||||
],
|
||||
"type": "object"
|
||||
"alias",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"CrazyItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enumeration": {
|
||||
"$ref": "#/components/schemas/SimpleEnum"
|
||||
"enum": [
|
||||
"ONE",
|
||||
"TWO"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"enumeration"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SimpleEnum": {
|
||||
"enum": [
|
||||
"ONE",
|
||||
"TWO"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"TestCreatedResponse": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,19 +27,18 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/": {
|
||||
"put": {
|
||||
"tags": [],
|
||||
"summary": "Test put endpoint",
|
||||
"description": "Put your tests here!",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "A Test request",
|
||||
"description": "A Test Request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
"$ref": "#/components/schemas/Int"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -50,18 +50,28 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
"$ref": "#/components/schemas/Boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {},
|
||||
"schemas": {
|
||||
"Boolean": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"Int": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,54 +27,54 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/test/{a}": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful List-y Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
},
|
||||
"type": "array"
|
||||
"$ref": "#/components/schemas/List-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"
|
||||
@ -81,8 +82,13 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"List-TestResponse": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,41 +27,42 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/optional": {
|
||||
"/optional": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "No request params and response body",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "notRequired",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"required": false,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "required",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Empty"
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "notRequired",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": false,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "required",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -29,27 +30,17 @@
|
||||
"/this/is/a/complex/path/with/an/{id}": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -65,12 +56,15 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -78,8 +72,7 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,32 +27,12 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/testerino": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
@ -65,12 +46,26 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -78,8 +73,7 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,26 +27,15 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"/testerino": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "default param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "b",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "heyo"
|
||||
},
|
||||
"required": false,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@ -56,12 +46,15 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -69,8 +62,7 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
72
core/src/test/resources/T0015__trailing_slash.json
Normal file
72
core/src/test/resources/T0015__trailing_slash.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": {
|
||||
"/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": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,32 +27,12 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
@ -75,12 +56,15 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -88,10 +72,10 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"ExceptionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
@ -99,8 +83,7 @@
|
||||
},
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,32 +27,12 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
@ -63,16 +44,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Access Denied",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ExceptionResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Things Happened",
|
||||
"content": {
|
||||
@ -82,15 +53,28 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Access Denied",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ExceptionResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -98,10 +82,10 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"ExceptionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
@ -109,8 +93,7 @@
|
||||
},
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,32 +27,12 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
@ -63,31 +44,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"501": {
|
||||
"description": "The Gibbits are ANGRY",
|
||||
"500": {
|
||||
"description": "Bad Things Happened",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ComplexGibbit"
|
||||
}
|
||||
]
|
||||
"$ref": "#/components/schemas/FlibbityGibbit"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -95,10 +72,30 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"ComplexGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c",
|
||||
"z"
|
||||
]
|
||||
},
|
||||
"SimpleGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
@ -109,27 +106,17 @@
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"ComplexGibbit": {
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
"FlibbityGibbit": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ComplexGibbit"
|
||||
},
|
||||
"c": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
{
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,32 +27,12 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
@ -64,30 +45,26 @@
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Wow serious things went wrong",
|
||||
"description": "Bad Things Happened",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Gibbity-String"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Bibbity-String"
|
||||
}
|
||||
]
|
||||
"$ref": "#/components/schemas/Flibbity-String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -95,21 +72,10 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"Gibbity-String": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"Bibbity-String": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
@ -121,8 +87,28 @@
|
||||
"required": [
|
||||
"b",
|
||||
"f"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"Gibbity-String": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
]
|
||||
},
|
||||
"Flibbity-String": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Bibbity-String"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Gibbity-String"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,39 +27,28 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/examples": {
|
||||
"/": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "Example Parameters",
|
||||
"description": "A test for setting parameter examples",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "Test",
|
||||
"description": "You gotta send it",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestRequest"
|
||||
},
|
||||
"examples": {
|
||||
"one": {
|
||||
"Testerina": {
|
||||
"value": {
|
||||
"fieldName": {
|
||||
"nesty": "hey"
|
||||
"nesty": "asdf"
|
||||
},
|
||||
"b": 4.0,
|
||||
"b": 1.5,
|
||||
"aaa": []
|
||||
}
|
||||
},
|
||||
"two": {
|
||||
"value": {
|
||||
"fieldName": {
|
||||
"nesty": "hello"
|
||||
},
|
||||
"b": 3.8,
|
||||
"aaa": [
|
||||
31324234
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,17 +56,17 @@
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "nice",
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
},
|
||||
"examples": {
|
||||
"test": {
|
||||
"Testerino": {
|
||||
"value": {
|
||||
"c": "spud"
|
||||
"c": "Heya"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,47 +75,15 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestRequest": {
|
||||
"properties": {
|
||||
"aaa": {
|
||||
"items": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"b": {
|
||||
"format": "double",
|
||||
"type": "number"
|
||||
},
|
||||
"field_name": {
|
||||
"$ref": "#/components/schemas/TestNested"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"field_name",
|
||||
"b",
|
||||
"aaa"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TestNested": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -133,8 +91,42 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"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": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,30 +27,25 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
"deprecated": false,
|
||||
"examples": {
|
||||
"foo": {
|
||||
"value": "testing"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -65,12 +61,15 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -78,8 +77,7 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,27 +27,18 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
"type": "string",
|
||||
"default": "IDK"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
@ -65,12 +57,15 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -78,8 +73,7 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -29,27 +30,17 @@
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -65,12 +56,15 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -78,8 +72,7 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,25 +27,25 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"name": "id",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"required": false,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@ -55,12 +56,15 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -68,8 +72,7 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,45 +27,46 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/constrained_int": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Constrained int field",
|
||||
"description": "Cool stuff",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MinMaxDouble"
|
||||
"$ref": "#/components/schemas/DefaultField"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"MinMaxDouble": {
|
||||
"DefaultField": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"format": "double",
|
||||
"type": "string"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"minimum": 5.5,
|
||||
"maximum": 13.37,
|
||||
"exclusiveMinimum": false,
|
||||
"exclusiveMaximum": false
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
"b"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,45 +27,47 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/constrained_int": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Constrained int field",
|
||||
"description": "Cool stuff",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ExclusiveMinMax"
|
||||
"$ref": "#/components/schemas/NullableField"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ExclusiveMinMax": {
|
||||
"NullableField": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"format": "int32",
|
||||
"type": "integer",
|
||||
"minimum": 5,
|
||||
"maximum": 100,
|
||||
"exclusiveMinimum": true,
|
||||
"exclusiveMaximum": true
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
"required": []
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,41 +27,54 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/polymorphiclist": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Oh so many gibbits",
|
||||
"description": "Polymorphic list response",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ComplexGibbit"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "array"
|
||||
"$ref": "#/components/schemas/FlibbityGibbit"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ComplexGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c",
|
||||
"z"
|
||||
]
|
||||
},
|
||||
"SimpleGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
@ -71,27 +85,17 @@
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"ComplexGibbit": {
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
"FlibbityGibbit": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ComplexGibbit"
|
||||
},
|
||||
"c": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
{
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,41 +27,54 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/polymorphicmap": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "By gawd that's a lot of gibbits",
|
||||
"description": "Polymorphic list response",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ComplexGibbit"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "object"
|
||||
"$ref": "#/components/schemas/List-FlibbityGibbit"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ComplexGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c",
|
||||
"z"
|
||||
]
|
||||
},
|
||||
"SimpleGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
@ -71,27 +85,20 @@
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"ComplexGibbit": {
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
}
|
||||
"List-FlibbityGibbit": {
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ComplexGibbit"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
}
|
||||
]
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,38 +27,54 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/polymorphic": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "All the gibbits",
|
||||
"description": "Polymorphic response",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ComplexGibbit"
|
||||
}
|
||||
]
|
||||
"$ref": "#/components/schemas/Map-String-FlibbityGibbit"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ComplexGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c",
|
||||
"z"
|
||||
]
|
||||
},
|
||||
"SimpleGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
@ -68,26 +85,19 @@
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"ComplexGibbit": {
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
}
|
||||
"Map-String-FlibbityGibbit": {
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ComplexGibbit"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
}
|
||||
]
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,31 +27,34 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/date_time_format": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Date time string test",
|
||||
"description": "Cool stuff",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/DateTimeString"
|
||||
"$ref": "#/components/schemas/Gibbity-String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"DateTimeString": {
|
||||
"Gibbity-String": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
@ -58,8 +62,7 @@
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,45 +27,45 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MinMaxArray"
|
||||
"$ref": "#/components/schemas/Gibbity-Map"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"MinMaxArray": {
|
||||
"Gibbity-Map": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"items": {
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"maxItems": 10,
|
||||
"type": "array"
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,73 +27,69 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/polymorphic": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "More flibbity",
|
||||
"description": "Polymorphic with generics",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Gibbity-TestNested"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Bibbity-TestNested"
|
||||
}
|
||||
]
|
||||
"$ref": "#/components/schemas/Flibbity-Double"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Gibbity-TestNested": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"$ref": "#/components/schemas/TestNested"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TestNested": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"Bibbity-TestNested": {
|
||||
"Bibbity-Double": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"f": {
|
||||
"$ref": "#/components/schemas/TestNested"
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"f"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"Gibbity-Double": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
]
|
||||
},
|
||||
"Flibbity-Double": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Bibbity-Double"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Gibbity-Double"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
145
core/src/test/resources/T0033__crazy_polymorphic_example.json
Normal file
145
core/src/test/resources/T0033__crazy_polymorphic_example.json
Normal file
@ -0,0 +1,145 @@
|
||||
{
|
||||
"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": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Flibbity-FlibbityGibbit"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ComplexGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c",
|
||||
"z"
|
||||
]
|
||||
},
|
||||
"SimpleGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
]
|
||||
},
|
||||
"Bibbity-FlibbityGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"f": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ComplexGibbit"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"f"
|
||||
]
|
||||
},
|
||||
"Gibbity-FlibbityGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ComplexGibbit"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
]
|
||||
},
|
||||
"Flibbity-FlibbityGibbit": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Bibbity-FlibbityGibbit"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Gibbity-FlibbityGibbit"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,26 +27,16 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "password"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"operationId": "getThisDude",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@ -56,12 +47,15 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -69,8 +63,7 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,14 +27,14 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/with_header": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "testing header stuffs",
|
||||
"description": "Good for many things",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-UserEmail",
|
||||
"name": "X-User-Email",
|
||||
"in": "header",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
@ -44,7 +45,7 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@ -55,12 +56,15 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
@ -68,8 +72,7 @@
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
121
core/src/test/resources/T0036__nullable_fields.json
Normal file
121
core/src/test/resources/T0036__nullable_fields.json
Normal file
@ -0,0 +1,121 @@
|
||||
{
|
||||
"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": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProfileUpdateRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ProfileUpdateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"metadata": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isPrivate": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"otherThing": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"mood": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"viewCount": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"format": "int64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,15 +27,15 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/nullable/enum": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Has a nullable enum field",
|
||||
"description": "should still work!",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@ -45,25 +46,31 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"NullableEnum": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"$ref": "#/components/schemas/TestEnum"
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"YES",
|
||||
"NO"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TestEnum": {
|
||||
"enum": [
|
||||
"YES",
|
||||
"NO"
|
||||
],
|
||||
"type": "string"
|
||||
"required": []
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,15 +27,15 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/date_time_format": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Date time string test",
|
||||
"description": "Cool stuff",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@ -45,22 +46,28 @@
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Instant": {
|
||||
"type": "string",
|
||||
"format": "date"
|
||||
},
|
||||
"DateTimeString": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
"format": "date"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
@ -0,0 +1,104 @@
|
||||
package io.bkbn.kompendium.core.fixtures
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import io.bkbn.kompendium.core.fixtures.TestSpecs.defaultSpec
|
||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||
import io.bkbn.kompendium.core.routes.redoc
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.oas.OpenApiSpec
|
||||
import io.bkbn.kompendium.oas.info.Contact
|
||||
import io.bkbn.kompendium.oas.info.Info
|
||||
import io.bkbn.kompendium.oas.info.License
|
||||
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
||||
import io.bkbn.kompendium.oas.server.Server
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.kotest.assertions.ktor.client.shouldHaveStatus
|
||||
import io.kotest.matchers.shouldNot
|
||||
import io.kotest.matchers.string.beBlank
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.serialization.gson.gson
|
||||
import io.ktor.serialization.jackson.jackson
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.testing.ApplicationTestBuilder
|
||||
import io.ktor.server.testing.testApplication
|
||||
import kotlin.reflect.KType
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
object TestHelpers {
|
||||
private const val OPEN_API_ENDPOINT = "/openapi.json"
|
||||
|
||||
fun getFileSnapshot(fileName: String): String {
|
||||
val snapshotPath = "src/test/resources"
|
||||
val file = File("$snapshotPath/$fileName")
|
||||
return file.readText()
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the baseline expected tests on an OpenAPI result. Confirms that the endpoint
|
||||
* exists as expected, and that the content matches the expected blob found in the specified file
|
||||
* @param snapshotName The snapshot file to retrieve from the resources folder
|
||||
*/
|
||||
private suspend fun ApplicationTestBuilder.compareOpenAPISpec(snapshotName: String) {
|
||||
val response = client.get(OPEN_API_ENDPOINT)
|
||||
response shouldHaveStatus HttpStatusCode.OK
|
||||
response.bodyAsText() shouldNot beBlank()
|
||||
response.bodyAsText() shouldEqualJson getFileSnapshot(snapshotName)
|
||||
}
|
||||
|
||||
/**
|
||||
* This will take a provided JSON snapshot file, retrieve it from the resource folder,
|
||||
* and build a test ktor server to compare the expected output with the output found in the default
|
||||
* OpenAPI json endpoint. By default, this will run the same test with Gson, Kotlinx, and Jackson serializers
|
||||
* @param snapshotName The snapshot file to retrieve from the resources folder
|
||||
* @param moduleFunction Initializer for the application to allow tests to pass the required Ktor modules
|
||||
*/
|
||||
fun openApiTestAllSerializers(
|
||||
snapshotName: String,
|
||||
customTypes: Map<KType, JsonSchema> = emptyMap(),
|
||||
routeUnderTest: Routing.() -> Unit
|
||||
) {
|
||||
openApiTest(snapshotName, SupportedSerializer.KOTLINX, routeUnderTest, customTypes)
|
||||
openApiTest(snapshotName, SupportedSerializer.JACKSON, routeUnderTest, customTypes)
|
||||
openApiTest(snapshotName, SupportedSerializer.GSON, routeUnderTest, customTypes)
|
||||
}
|
||||
|
||||
private fun openApiTest(
|
||||
snapshotName: String,
|
||||
serializer: SupportedSerializer,
|
||||
routeUnderTest: Routing.() -> Unit,
|
||||
typeOverrides: Map<KType, JsonSchema> = emptyMap()
|
||||
) = testApplication {
|
||||
install(NotarizedApplication()) {
|
||||
customTypes = typeOverrides
|
||||
spec = defaultSpec()
|
||||
}
|
||||
install(ContentNegotiation) {
|
||||
when (serializer) {
|
||||
SupportedSerializer.KOTLINX -> json(Json {
|
||||
encodeDefaults = true
|
||||
explicitNulls = false
|
||||
serializersModule = KompendiumSerializersModule.module
|
||||
})
|
||||
|
||||
SupportedSerializer.GSON -> gson()
|
||||
SupportedSerializer.JACKSON -> jackson(ContentType.Application.Json) {
|
||||
enable(SerializationFeature.INDENT_OUTPUT)
|
||||
setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
}
|
||||
}
|
||||
}
|
||||
routing {
|
||||
redoc()
|
||||
routeUnderTest()
|
||||
}
|
||||
compareOpenAPISpec(snapshotName)
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
package io.bkbn.kompendium.core.fixtures
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.time.Instant
|
||||
|
||||
@Serializable
|
||||
data class TestNested(val nesty: String)
|
||||
|
||||
@Serializable
|
||||
data class TestRequest(
|
||||
val fieldName: TestNested,
|
||||
val b: Double,
|
||||
val aaa: List<Long>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TestSimpleRequest(
|
||||
val a: String,
|
||||
val b: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TestResponse(val c: String)
|
||||
|
||||
@Serializable
|
||||
enum class TestEnum {
|
||||
YES,
|
||||
NO
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class NullableEnum(val a: TestEnum? = null)
|
||||
|
||||
data class TestCreatedResponse(val id: Int, val c: String)
|
||||
|
||||
data class DateTimeString(
|
||||
val a: Instant
|
||||
)
|
||||
|
||||
data class DefaultField(
|
||||
val a: String = "hi",
|
||||
val b: Int
|
||||
)
|
||||
|
||||
data class NullableField(
|
||||
val a: String?
|
||||
)
|
||||
|
||||
data class ComplexRequest(
|
||||
val org: String,
|
||||
val amazingField: String,
|
||||
val tables: List<NestedComplexItem>
|
||||
)
|
||||
|
||||
data class NestedComplexItem(
|
||||
val name: String,
|
||||
val alias: CustomAlias
|
||||
)
|
||||
|
||||
typealias CustomAlias = Map<String, CrazyItem>
|
||||
|
||||
data class CrazyItem(val enumeration: SimpleEnum)
|
||||
|
||||
enum class SimpleEnum {
|
||||
ONE,
|
||||
TWO
|
||||
}
|
||||
|
||||
data class ExceptionResponse(val message: String)
|
||||
|
||||
sealed class FlibbityGibbit {
|
||||
abstract val z: String
|
||||
}
|
||||
|
||||
data class SimpleGibbit(val a: String, override val z: String = "z") : FlibbityGibbit()
|
||||
data class ComplexGibbit(val b: String, val c: Int, override val z: String) : FlibbityGibbit()
|
||||
|
||||
sealed interface SlammaJamma
|
||||
|
||||
data class OneJamma(val a: Int) : SlammaJamma
|
||||
data class AnothaJamma(val b: Float) : SlammaJamma
|
||||
|
||||
data class InsaneJamma(val c: SlammaJamma) : SlammaJamma
|
||||
|
||||
sealed interface Flibbity<T>
|
||||
|
||||
data class Gibbity<T>(val a: T) : Flibbity<T>
|
||||
data class Bibbity<T>(val b: String, val f: T) : Flibbity<T>
|
||||
|
||||
data class NestedFlibbity<T>(
|
||||
val flibbity: Flibbity<T>
|
||||
)
|
||||
|
||||
enum class ColumnMode {
|
||||
NULLABLE,
|
||||
REQUIRED,
|
||||
REPEATED
|
||||
}
|
||||
|
||||
data class ColumnSchema(
|
||||
val name: String,
|
||||
val type: String,
|
||||
val description: String,
|
||||
val mode: ColumnMode,
|
||||
val subColumns: List<ColumnSchema> = emptyList()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
public data class ProfileUpdateRequest(
|
||||
public val mood: String?,
|
||||
public val viewCount: Long?,
|
||||
public val metadata: ProfileMetadataUpdateRequest?
|
||||
)
|
||||
|
||||
|
||||
@Serializable
|
||||
public data class ProfileMetadataUpdateRequest(
|
||||
public val isPrivate: Boolean?,
|
||||
public val otherThing: String?
|
||||
)
|
@ -13,7 +13,7 @@ object TestSpecs {
|
||||
info = Info(
|
||||
title = "Test API",
|
||||
version = "1.33.7",
|
||||
description = "An amazing, fully-ish 😉 generated API spec",
|
||||
description = "An amazing, fully-ish \uD83D\uDE09 generated API spec",
|
||||
termsOfService = URI("https://example.com"),
|
||||
contact = Contact(
|
||||
name = "Homer Simpson",
|
@ -4,17 +4,15 @@ complexity:
|
||||
LongParameterList:
|
||||
active: true
|
||||
functionThreshold: 10
|
||||
constructorThreshold: 10
|
||||
constructorThreshold: 15
|
||||
ComplexMethod:
|
||||
threshold: 20
|
||||
formatting:
|
||||
ParameterListWrapping:
|
||||
active: false
|
||||
style:
|
||||
MaxLineLength:
|
||||
excludes: ['**/test/**/*']
|
||||
active: true
|
||||
maxLineLength: 120
|
||||
excludeCommentStatements: true
|
||||
MagicNumber:
|
||||
excludes: ['**/kompendium-playground/**/*', '**/test/**/*']
|
||||
naming:
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Kompendium
|
||||
project.version=2.3.5
|
||||
project.version=3.0.0-alpha
|
||||
# Kotlin
|
||||
kotlin.code.style=official
|
||||
# Gradle
|
||||
@ -8,5 +8,5 @@ org.gradle.vfs.verbose=true
|
||||
org.gradle.jvmargs=-Xmx2000m
|
||||
|
||||
# Dependencies
|
||||
ktorVersion=1.6.8
|
||||
ktorVersion=2.1.0
|
||||
kotestVersion=5.4.2
|
||||
|
32
json-schema/build.gradle.kts
Normal file
32
json-schema/build.gradle.kts
Normal file
@ -0,0 +1,32 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization")
|
||||
id("io.bkbn.sourdough.library.jvm")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
id("com.adarshr.test-logger")
|
||||
id("org.jetbrains.dokka")
|
||||
id("maven-publish")
|
||||
id("java-library")
|
||||
id("signing")
|
||||
}
|
||||
|
||||
sourdoughLibrary {
|
||||
libraryName.set("Kompendium JSON Schema")
|
||||
libraryDescription.set("Json Schema Generator")
|
||||
compilerArgs.set(listOf("-opt-in=kotlin.RequiresOptIn"))
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect:1.7.10")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3")
|
||||
|
||||
testImplementation(testFixtures(projects.kompendiumCore))
|
||||
}
|
||||
|
||||
testing {
|
||||
suites {
|
||||
named("test", JvmTestSuite::class) {
|
||||
useJUnitJupiter()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package io.bkbn.kompendium.json.schema
|
||||
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.definition.NullableDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.OneOfDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.json.schema.handler.CollectionHandler
|
||||
import io.bkbn.kompendium.json.schema.handler.EnumHandler
|
||||
import io.bkbn.kompendium.json.schema.handler.MapHandler
|
||||
import io.bkbn.kompendium.json.schema.handler.SimpleObjectHandler
|
||||
import io.bkbn.kompendium.json.schema.handler.SealedObjectHandler
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getSimpleSlug
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
object SchemaGenerator {
|
||||
inline fun <reified T : Any?> fromTypeToSchema(cache: MutableMap<String, JsonSchema> = mutableMapOf()) =
|
||||
fromTypeToSchema(typeOf<T>(), cache)
|
||||
|
||||
fun fromTypeToSchema(type: KType, cache: MutableMap<String, JsonSchema>): JsonSchema {
|
||||
cache[type.getSimpleSlug()]?.let {
|
||||
return it
|
||||
}
|
||||
return when (val clazz = type.classifier as KClass<*>) {
|
||||
Unit::class -> error(
|
||||
"""
|
||||
Unit cannot be converted to JsonSchema.
|
||||
If you are looking for a method will return null when called with Unit,
|
||||
please call SchemaGenerator.fromTypeOrUnit()
|
||||
""".trimIndent()
|
||||
)
|
||||
Int::class -> checkForNull(type, TypeDefinition.INT)
|
||||
Long::class -> checkForNull(type, TypeDefinition.LONG)
|
||||
Double::class -> checkForNull(type, TypeDefinition.DOUBLE)
|
||||
Float::class -> checkForNull(type, TypeDefinition.FLOAT)
|
||||
String::class -> checkForNull(type, TypeDefinition.STRING)
|
||||
Boolean::class -> checkForNull(type, TypeDefinition.BOOLEAN)
|
||||
else -> when {
|
||||
clazz.isSubclassOf(Enum::class) -> EnumHandler.handle(type, clazz)
|
||||
clazz.isSubclassOf(Collection::class) -> CollectionHandler.handle(type, cache)
|
||||
clazz.isSubclassOf(Map::class) -> MapHandler.handle(type, cache)
|
||||
else -> {
|
||||
if (clazz.isSealed) {
|
||||
SealedObjectHandler.handle(type, clazz, cache)
|
||||
} else {
|
||||
SimpleObjectHandler.handle(type, clazz, cache)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fromTypeOrUnit(type: KType, cache: MutableMap<String, JsonSchema> = mutableMapOf()): JsonSchema? =
|
||||
when (type.classifier as KClass<*>) {
|
||||
Unit::class -> null
|
||||
else -> fromTypeToSchema(type, cache)
|
||||
}
|
||||
|
||||
private fun checkForNull(type: KType, schema: JsonSchema): JsonSchema = when (type.isMarkedNullable) {
|
||||
true -> OneOfDefinition(NullableDefinition(), schema)
|
||||
false -> schema
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package io.bkbn.kompendium.json.schema.definition
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class AnyOfDefinition(val anyOf: Set<JsonSchema>) : JsonSchema
|
@ -0,0 +1,10 @@
|
||||
package io.bkbn.kompendium.json.schema.definition
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ArrayDefinition(
|
||||
val items: JsonSchema
|
||||
) : JsonSchema {
|
||||
val type: String = "array"
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package io.bkbn.kompendium.json.schema.definition
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class EnumDefinition(
|
||||
val enum: Set<String>
|
||||
) : JsonSchema
|
@ -0,0 +1,33 @@
|
||||
package io.bkbn.kompendium.json.schema.definition
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
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
|
||||
|
||||
@Serializable(with = JsonSchema.Serializer::class)
|
||||
sealed interface JsonSchema {
|
||||
|
||||
object Serializer : KSerializer<JsonSchema> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("JsonSchema", PrimitiveKind.STRING)
|
||||
override fun deserialize(decoder: Decoder): JsonSchema {
|
||||
error("Abandon all hope ye who enter 💀")
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: JsonSchema) {
|
||||
when (value) {
|
||||
is ReferenceDefinition -> ReferenceDefinition.serializer().serialize(encoder, value)
|
||||
is TypeDefinition -> TypeDefinition.serializer().serialize(encoder, value)
|
||||
is EnumDefinition -> EnumDefinition.serializer().serialize(encoder, value)
|
||||
is ArrayDefinition -> ArrayDefinition.serializer().serialize(encoder, value)
|
||||
is MapDefinition -> MapDefinition.serializer().serialize(encoder, value)
|
||||
is NullableDefinition -> NullableDefinition.serializer().serialize(encoder, value)
|
||||
is OneOfDefinition -> OneOfDefinition.serializer().serialize(encoder, value)
|
||||
is AnyOfDefinition -> AnyOfDefinition.serializer().serialize(encoder, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package io.bkbn.kompendium.json.schema.definition
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class MapDefinition(
|
||||
val additionalProperties: JsonSchema
|
||||
) : JsonSchema {
|
||||
val type: String = "object"
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package io.bkbn.kompendium.json.schema.definition
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class NullableDefinition(val type: String = "null") : JsonSchema
|
@ -0,0 +1,8 @@
|
||||
package io.bkbn.kompendium.json.schema.definition
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class OneOfDefinition(val oneOf: Set<JsonSchema>) : JsonSchema {
|
||||
constructor(vararg types: JsonSchema) : this(types.toSet())
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package io.bkbn.kompendium.json.schema.definition
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ReferenceDefinition(val `$ref`: String) : JsonSchema
|
@ -0,0 +1,47 @@
|
||||
package io.bkbn.kompendium.json.schema.definition
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class TypeDefinition(
|
||||
val type: String,
|
||||
val format: String? = null,
|
||||
val description: String? = null,
|
||||
val properties: Map<String, JsonSchema>? = null,
|
||||
val required: Set<String>? = null,
|
||||
@Contextual val default: Any? = null,
|
||||
) : JsonSchema {
|
||||
|
||||
fun withDefault(default: Any): TypeDefinition = this.copy(default = default)
|
||||
|
||||
companion object {
|
||||
val INT = TypeDefinition(
|
||||
type = "number",
|
||||
format = "int32"
|
||||
)
|
||||
|
||||
val LONG = TypeDefinition(
|
||||
type = "number",
|
||||
format = "int64"
|
||||
)
|
||||
|
||||
val DOUBLE = TypeDefinition(
|
||||
type = "number",
|
||||
format = "double"
|
||||
)
|
||||
|
||||
val FLOAT = TypeDefinition(
|
||||
type = "number",
|
||||
format = "float"
|
||||
)
|
||||
|
||||
val STRING = TypeDefinition(
|
||||
type = "string"
|
||||
)
|
||||
|
||||
val BOOLEAN = TypeDefinition(
|
||||
type = "boolean"
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package io.bkbn.kompendium.json.schema.handler
|
||||
|
||||
import io.bkbn.kompendium.json.schema.SchemaGenerator
|
||||
import io.bkbn.kompendium.json.schema.definition.ArrayDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.definition.NullableDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.OneOfDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getReferenceSlug
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getSimpleSlug
|
||||
import kotlin.reflect.KType
|
||||
|
||||
object CollectionHandler {
|
||||
|
||||
fun handle(type: KType, cache: MutableMap<String, JsonSchema>): JsonSchema {
|
||||
val collectionType = type.arguments.first().type!!
|
||||
val typeSchema = SchemaGenerator.fromTypeToSchema(collectionType, cache).let {
|
||||
if (it is TypeDefinition && it.type == "object") {
|
||||
cache[collectionType.getSimpleSlug()] = it
|
||||
ReferenceDefinition(collectionType.getReferenceSlug())
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
val definition = ArrayDefinition(typeSchema)
|
||||
return when (type.isMarkedNullable) {
|
||||
true -> OneOfDefinition(NullableDefinition(), definition)
|
||||
false -> definition
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package io.bkbn.kompendium.json.schema.handler
|
||||
|
||||
import io.bkbn.kompendium.json.schema.definition.EnumDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.definition.NullableDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.OneOfDefinition
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
|
||||
object EnumHandler {
|
||||
|
||||
fun handle(type: KType, clazz: KClass<*>): JsonSchema {
|
||||
val options = clazz.java.enumConstants.map { it.toString() }.toSet()
|
||||
val definition = EnumDefinition(enum = options)
|
||||
return when (type.isMarkedNullable) {
|
||||
true -> OneOfDefinition(NullableDefinition(), definition)
|
||||
false -> definition
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package io.bkbn.kompendium.json.schema.handler
|
||||
|
||||
import io.bkbn.kompendium.json.schema.SchemaGenerator
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.definition.MapDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.NullableDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.OneOfDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getReferenceSlug
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getSimpleSlug
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
|
||||
object MapHandler {
|
||||
|
||||
fun handle(type: KType, cache: MutableMap<String, JsonSchema>): JsonSchema {
|
||||
require(type.arguments.first().type!!.classifier as KClass<*> == String::class) {
|
||||
"JSON requires that map keys MUST be Strings. You provided ${type.arguments.first().type}"
|
||||
}
|
||||
val valueType = type.arguments[1].type!!
|
||||
val valueSchema = SchemaGenerator.fromTypeToSchema(valueType, cache).let {
|
||||
if (it is TypeDefinition && it.type == "object") {
|
||||
cache[valueType.getSimpleSlug()] = it
|
||||
ReferenceDefinition(valueType.getReferenceSlug())
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
val definition = MapDefinition(valueSchema)
|
||||
return when (type.isMarkedNullable) {
|
||||
true -> OneOfDefinition(NullableDefinition(), definition)
|
||||
false -> definition
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package io.bkbn.kompendium.json.schema.handler
|
||||
|
||||
import io.bkbn.kompendium.json.schema.SchemaGenerator
|
||||
import io.bkbn.kompendium.json.schema.definition.AnyOfDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getReferenceSlug
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getSimpleSlug
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createType
|
||||
|
||||
object SealedObjectHandler {
|
||||
|
||||
fun handle(type: KType, clazz: KClass<*>, cache: MutableMap<String, JsonSchema>): JsonSchema {
|
||||
val subclasses = clazz.sealedSubclasses
|
||||
.map { it.createType(type.arguments) }
|
||||
.map { t ->
|
||||
SchemaGenerator.fromTypeToSchema(t, cache).let { js ->
|
||||
if (js is TypeDefinition && js.type == "object") {
|
||||
cache[t.getSimpleSlug()] = js
|
||||
ReferenceDefinition(t.getReferenceSlug())
|
||||
} else {
|
||||
js
|
||||
}
|
||||
}
|
||||
}
|
||||
.toSet()
|
||||
return AnyOfDefinition(subclasses)
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package io.bkbn.kompendium.json.schema.handler
|
||||
|
||||
import io.bkbn.kompendium.json.schema.SchemaGenerator
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.definition.NullableDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.OneOfDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getReferenceSlug
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getSimpleSlug
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.KTypeParameter
|
||||
import kotlin.reflect.KTypeProjection
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
object SimpleObjectHandler {
|
||||
|
||||
fun handle(type: KType, clazz: KClass<*>, cache: MutableMap<String, JsonSchema>): JsonSchema {
|
||||
// cache[type.getSimpleSlug()] = ReferenceDefinition("RECURSION_PLACEHOLDER")
|
||||
val typeMap = clazz.typeParameters.zip(type.arguments).toMap()
|
||||
val props = clazz.memberProperties.associate { prop ->
|
||||
val schema = when (typeMap.containsKey(prop.returnType.classifier)) {
|
||||
true -> handleGenericProperty(prop, typeMap, cache)
|
||||
false -> handleProperty(prop, cache)
|
||||
}
|
||||
|
||||
prop.name to schema
|
||||
}
|
||||
|
||||
val required = clazz.memberProperties.filterNot { prop -> prop.returnType.isMarkedNullable }
|
||||
.filterNot { prop -> clazz.primaryConstructor!!.parameters.find { it.name == prop.name }!!.isOptional }
|
||||
.map { it.name }
|
||||
.toSet()
|
||||
|
||||
|
||||
val definition = TypeDefinition(
|
||||
type = "object",
|
||||
properties = props,
|
||||
required = required
|
||||
)
|
||||
|
||||
return when (type.isMarkedNullable) {
|
||||
true -> OneOfDefinition(NullableDefinition(), definition)
|
||||
false -> definition
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleGenericProperty(
|
||||
prop: KProperty<*>,
|
||||
typeMap: Map<KTypeParameter, KTypeProjection>,
|
||||
cache: MutableMap<String, JsonSchema>
|
||||
): JsonSchema {
|
||||
val type = typeMap[prop.returnType.classifier]?.type!!
|
||||
return SchemaGenerator.fromTypeToSchema(type, cache).let {
|
||||
if (it is TypeDefinition && it.type == "object") {
|
||||
cache[type.getSimpleSlug()] = it
|
||||
ReferenceDefinition(prop.returnType.getReferenceSlug())
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleProperty(prop: KProperty<*>, cache: MutableMap<String, JsonSchema>): JsonSchema =
|
||||
SchemaGenerator.fromTypeToSchema(prop.returnType, cache).let {
|
||||
if (it is TypeDefinition && it.type == "object") {
|
||||
cache[prop.returnType.getSimpleSlug()] = it
|
||||
ReferenceDefinition(prop.returnType.getReferenceSlug())
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package io.bkbn.kompendium.json.schema.util
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
|
||||
object Helpers {
|
||||
|
||||
private const val COMPONENT_SLUG = "#/components/schemas"
|
||||
|
||||
fun KType.getSimpleSlug(): String = when {
|
||||
this.arguments.isNotEmpty() -> genericNameAdapter(this, classifier as KClass<*>)
|
||||
else -> (classifier as KClass<*>).simpleName ?: error("Could not determine simple name for $this")
|
||||
}
|
||||
|
||||
fun KType.getReferenceSlug(): String = when {
|
||||
arguments.isNotEmpty() -> "$COMPONENT_SLUG/${genericNameAdapter(this, classifier as KClass<*>)}"
|
||||
else -> "$COMPONENT_SLUG/${(classifier as KClass<*>).simpleName}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts a class with type parameters into a reference friendly string
|
||||
*/
|
||||
private fun genericNameAdapter(type: KType, clazz: KClass<*>): String {
|
||||
val classNames = type.arguments
|
||||
.map { it.type?.classifier as KClass<*> }
|
||||
.map { it.simpleName }
|
||||
return classNames.joinToString(separator = "-", prefix = "${clazz.simpleName}-")
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package io.bkbn.kompendium.json.schema.util
|
||||
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import kotlin.reflect.KType
|
||||
|
||||
data class ReferenceCache(
|
||||
val referenceRootPath: String = "#/",
|
||||
val cache: MutableMap<KType, JsonSchema> = mutableMapOf()
|
||||
)
|
@ -0,0 +1,98 @@
|
||||
package io.bkbn.kompendium.json.schema
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.ComplexRequest
|
||||
import io.bkbn.kompendium.core.fixtures.FlibbityGibbit
|
||||
import io.bkbn.kompendium.core.fixtures.SimpleEnum
|
||||
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.TestSimpleRequest
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class SchemaGeneratorTest : DescribeSpec({
|
||||
describe("Scalars") {
|
||||
it("Can generate the schema for an Int") {
|
||||
jsonSchemaTest<Int>("T0001__scalar_int.json")
|
||||
}
|
||||
it("Can generate the schema for a Boolean") {
|
||||
jsonSchemaTest<Boolean>("T0002__scalar_bool.json")
|
||||
}
|
||||
it("Can generate the schema for a String") {
|
||||
jsonSchemaTest<String>("T0003__scalar_string.json")
|
||||
}
|
||||
}
|
||||
describe("Objects") {
|
||||
it("Can generate the schema for a simple object") {
|
||||
jsonSchemaTest<TestSimpleRequest>("T0004__simple_object.json")
|
||||
}
|
||||
it("Can generate the schema for a complex object") {
|
||||
jsonSchemaTest<ComplexRequest>("T0005__complex_object.json")
|
||||
}
|
||||
it("Can generate the schema for a nullable object") {
|
||||
jsonSchemaTest<TestSimpleRequest?>("T0006__nullable_object.json")
|
||||
}
|
||||
it("Can generate the schema for a polymorphic object") {
|
||||
jsonSchemaTest<FlibbityGibbit>("T0015__polymorphic_object.json")
|
||||
}
|
||||
xit("Can generate the schema for a recursive type") {
|
||||
// TODO jsonSchemaTest<SlammaJamma>("T0016__recursive_object.json")
|
||||
}
|
||||
}
|
||||
describe("Enums") {
|
||||
it("Can generate the schema for a simple enum") {
|
||||
jsonSchemaTest<SimpleEnum>("T0007__simple_enum.json")
|
||||
}
|
||||
it("Can generate the schema for a nullable enum") {
|
||||
jsonSchemaTest<SimpleEnum?>("T0008__nullable_enum.json")
|
||||
}
|
||||
}
|
||||
describe("Arrays") {
|
||||
it("Can generate the schema for an array of scalars") {
|
||||
jsonSchemaTest<List<Int>>("T0009__scalar_array.json")
|
||||
}
|
||||
it("Can generate the schema for an array of objects") {
|
||||
jsonSchemaTest<List<TestResponse>>("T0010__object_array.json")
|
||||
}
|
||||
it("Can generate the schema for a nullable array") {
|
||||
jsonSchemaTest<List<Int>?>("T0011__nullable_array.json")
|
||||
}
|
||||
}
|
||||
describe("Maps") {
|
||||
it("Can generate the schema for a map of scalars") {
|
||||
jsonSchemaTest<Map<String, Int>>("T0012__scalar_map.json")
|
||||
}
|
||||
it("Throws an error when map keys are not strings") {
|
||||
shouldThrow<IllegalArgumentException> { SchemaGenerator.fromTypeToSchema<Map<Int, Int>>() }
|
||||
}
|
||||
it("Can generate the schema for a map of objects") {
|
||||
jsonSchemaTest<Map<String, TestResponse>>("T0013__object_map.json")
|
||||
}
|
||||
it("Can generate the schema for a nullable map") {
|
||||
jsonSchemaTest<Map<String, Int>?>("T0014__nullable_map.json")
|
||||
}
|
||||
}
|
||||
}) {
|
||||
companion object {
|
||||
private val json = Json {
|
||||
encodeDefaults = true
|
||||
explicitNulls = false
|
||||
prettyPrint = true
|
||||
}
|
||||
|
||||
private fun JsonSchema.serialize() = json.encodeToString(JsonSchema.serializer(), this)
|
||||
|
||||
private inline fun <reified T> jsonSchemaTest(snapshotName: String) {
|
||||
// act
|
||||
val schema = SchemaGenerator.fromTypeToSchema<T>()
|
||||
|
||||
// todo add cache assertions!!!
|
||||
|
||||
// assert
|
||||
schema.serialize() shouldEqualJson getFileSnapshot(snapshotName)
|
||||
}
|
||||
}
|
||||
}
|
4
json-schema/src/test/resources/T0001__scalar_int.json
Normal file
4
json-schema/src/test/resources/T0001__scalar_int.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
3
json-schema/src/test/resources/T0002__scalar_bool.json
Normal file
3
json-schema/src/test/resources/T0002__scalar_bool.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
3
json-schema/src/test/resources/T0003__scalar_string.json
Normal file
3
json-schema/src/test/resources/T0003__scalar_string.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "string"
|
||||
}
|
16
json-schema/src/test/resources/T0004__simple_object.json
Normal file
16
json-schema/src/test/resources/T0004__simple_object.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
22
json-schema/src/test/resources/T0005__complex_object.json
Normal file
22
json-schema/src/test/resources/T0005__complex_object.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amazingField": {
|
||||
"type": "string"
|
||||
},
|
||||
"org": {
|
||||
"type": "string"
|
||||
},
|
||||
"tables": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NestedComplexItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"amazingField",
|
||||
"org",
|
||||
"tables"
|
||||
]
|
||||
}
|
23
json-schema/src/test/resources/T0006__nullable_object.json
Normal file
23
json-schema/src/test/resources/T0006__nullable_object.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
3
json-schema/src/test/resources/T0007__simple_enum.json
Normal file
3
json-schema/src/test/resources/T0007__simple_enum.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"enum": [ "ONE", "TWO" ]
|
||||
}
|
13
json-schema/src/test/resources/T0008__nullable_enum.json
Normal file
13
json-schema/src/test/resources/T0008__nullable_enum.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"ONE",
|
||||
"TWO"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
7
json-schema/src/test/resources/T0009__scalar_array.json
Normal file
7
json-schema/src/test/resources/T0009__scalar_array.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"items": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
6
json-schema/src/test/resources/T0010__object_array.json
Normal file
6
json-schema/src/test/resources/T0010__object_array.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
14
json-schema/src/test/resources/T0011__nullable_array.json
Normal file
14
json-schema/src/test/resources/T0011__nullable_array.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"items": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
]
|
||||
}
|
7
json-schema/src/test/resources/T0012__scalar_map.json
Normal file
7
json-schema/src/test/resources/T0012__scalar_map.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"additionalProperties": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"type": "object"
|
||||
}
|
6
json-schema/src/test/resources/T0013__object_map.json
Normal file
6
json-schema/src/test/resources/T0013__object_map.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"additionalProperties": {
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
},
|
||||
"type": "object"
|
||||
}
|
14
json-schema/src/test/resources/T0014__nullable_map.json
Normal file
14
json-schema/src/test/resources/T0014__nullable_map.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"additionalProperties": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ComplexGibbit"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
}
|
||||
]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user