
committed by
GitHub

parent
f919a6a4b1
commit
31fa5e40c8
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
- Fixed support Location classes located in other non-location classes
|
- Fixed support Location classes located in other non-location classes
|
||||||
|
- Fixed formatting of a custom SimpleSchema
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
@ -82,22 +82,18 @@ fun FormattedSchema.scanForConstraints(prop: KProperty1<*, *>): FormattedSchema
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun SimpleSchema.scanForConstraints(prop: KProperty1<*, *>): SimpleSchema {
|
fun SimpleSchema.scanForConstraints(prop: KProperty1<*, *>): SimpleSchema {
|
||||||
val minLength = prop.findAnnotation<MinLength>()
|
val minLength = prop.findAnnotation<MinLength>()?.length ?: this.minLength
|
||||||
val maxLength = prop.findAnnotation<MaxLength>()
|
val maxLength = prop.findAnnotation<MaxLength>()?.length ?: this.maxLength
|
||||||
val pattern = prop.findAnnotation<Pattern>()
|
val pattern = prop.findAnnotation<Pattern>()?.pattern ?: this.pattern
|
||||||
val format = prop.findAnnotation<Format>()
|
val format = prop.findAnnotation<Format>()?.format ?: this.format
|
||||||
|
val nullable = if (prop.returnType.isMarkedNullable) true else this.nullable
|
||||||
|
|
||||||
var schema = this
|
return copy(
|
||||||
|
nullable = nullable,
|
||||||
if (prop.returnType.isMarkedNullable) {
|
minLength = minLength,
|
||||||
schema = schema.copy(nullable = true)
|
maxLength = maxLength,
|
||||||
}
|
pattern = pattern,
|
||||||
|
format = format
|
||||||
return schema.copy(
|
|
||||||
minLength = minLength?.length,
|
|
||||||
maxLength = maxLength?.length,
|
|
||||||
pattern = pattern?.pattern,
|
|
||||||
format = format?.format
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import io.bkbn.kompendium.core.fixtures.docs
|
|||||||
import io.bkbn.kompendium.core.util.complexType
|
import io.bkbn.kompendium.core.util.complexType
|
||||||
import io.bkbn.kompendium.core.util.constrainedDoubleInfo
|
import io.bkbn.kompendium.core.util.constrainedDoubleInfo
|
||||||
import io.bkbn.kompendium.core.util.constrainedIntInfo
|
import io.bkbn.kompendium.core.util.constrainedIntInfo
|
||||||
|
import io.bkbn.kompendium.core.util.dateTimeString
|
||||||
import io.bkbn.kompendium.core.util.defaultField
|
import io.bkbn.kompendium.core.util.defaultField
|
||||||
import io.bkbn.kompendium.core.util.defaultParameter
|
import io.bkbn.kompendium.core.util.defaultParameter
|
||||||
import io.bkbn.kompendium.core.util.exampleParams
|
import io.bkbn.kompendium.core.util.exampleParams
|
||||||
@ -59,6 +60,8 @@ import io.bkbn.kompendium.core.util.uniqueArray
|
|||||||
import io.bkbn.kompendium.core.util.withDefaultParameter
|
import io.bkbn.kompendium.core.util.withDefaultParameter
|
||||||
import io.bkbn.kompendium.core.util.withExamples
|
import io.bkbn.kompendium.core.util.withExamples
|
||||||
import io.bkbn.kompendium.core.util.withOperationId
|
import io.bkbn.kompendium.core.util.withOperationId
|
||||||
|
import io.bkbn.kompendium.oas.schema.FormattedSchema
|
||||||
|
import io.bkbn.kompendium.oas.schema.SimpleSchema
|
||||||
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
||||||
import io.kotest.core.spec.style.DescribeSpec
|
import io.kotest.core.spec.style.DescribeSpec
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
@ -75,6 +78,7 @@ import io.ktor.serialization.json
|
|||||||
import io.ktor.server.testing.withTestApplication
|
import io.ktor.server.testing.withTestApplication
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
class KompendiumTest : DescribeSpec({
|
class KompendiumTest : DescribeSpec({
|
||||||
describe("Notarized Open API Metadata Tests") {
|
describe("Notarized Open API Metadata Tests") {
|
||||||
@ -275,6 +279,23 @@ class KompendiumTest : DescribeSpec({
|
|||||||
openApiTestAllSerializers("min_max_free_form.json") { minMaxFreeForm() }
|
openApiTestAllSerializers("min_max_free_form.json") { minMaxFreeForm() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
describe("Formats") {
|
||||||
|
it("Can set a format on a simple type schema") {
|
||||||
|
openApiTestAllSerializers("formatted_date_time_string.json", { dateTimeString() }) {
|
||||||
|
addCustomTypeSchema(Instant::class, SimpleSchema("string", format = "date-time"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it("Can set a format on formatted type schema") {
|
||||||
|
openApiTestAllSerializers("formatted_date_time_string.json", { dateTimeString() }) {
|
||||||
|
addCustomTypeSchema(Instant::class, FormattedSchema("date-time", "string"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it("Can bypass a format on a simple type schema") {
|
||||||
|
openApiTestAllSerializers("formatted_no_format_string.json", { dateTimeString() }) {
|
||||||
|
addCustomTypeSchema(Instant::class, SimpleSchema("string"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
describe("Free Form") {
|
describe("Free Form") {
|
||||||
it("Can create a free-form field") {
|
it("Can create a free-form field") {
|
||||||
openApiTestAllSerializers("free_form_object.json") { freeFormObject() }
|
openApiTestAllSerializers("free_form_object.json") { freeFormObject() }
|
||||||
|
@ -9,6 +9,7 @@ import io.bkbn.kompendium.core.Notarized.notarizedPost
|
|||||||
import io.bkbn.kompendium.core.Notarized.notarizedPut
|
import io.bkbn.kompendium.core.Notarized.notarizedPut
|
||||||
import io.bkbn.kompendium.core.fixtures.Bibbity
|
import io.bkbn.kompendium.core.fixtures.Bibbity
|
||||||
import io.bkbn.kompendium.core.fixtures.ComplexGibbit
|
import io.bkbn.kompendium.core.fixtures.ComplexGibbit
|
||||||
|
import io.bkbn.kompendium.core.fixtures.DateTimeString
|
||||||
import io.bkbn.kompendium.core.fixtures.DefaultParameter
|
import io.bkbn.kompendium.core.fixtures.DefaultParameter
|
||||||
import io.bkbn.kompendium.core.fixtures.Gibbity
|
import io.bkbn.kompendium.core.fixtures.Gibbity
|
||||||
import io.bkbn.kompendium.core.fixtures.Mysterious
|
import io.bkbn.kompendium.core.fixtures.Mysterious
|
||||||
@ -19,6 +20,7 @@ import io.bkbn.kompendium.core.fixtures.TestNested
|
|||||||
import io.bkbn.kompendium.core.fixtures.TestRequest
|
import io.bkbn.kompendium.core.fixtures.TestRequest
|
||||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo
|
import io.bkbn.kompendium.core.fixtures.TestResponseInfo
|
||||||
|
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.dateTimeString
|
||||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.defaultField
|
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.defaultField
|
||||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.defaultParam
|
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.defaultParam
|
||||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.formattedParam
|
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.formattedParam
|
||||||
@ -38,6 +40,7 @@ import io.ktor.response.respond
|
|||||||
import io.ktor.response.respondText
|
import io.ktor.response.respondText
|
||||||
import io.ktor.routing.route
|
import io.ktor.routing.route
|
||||||
import io.ktor.routing.routing
|
import io.ktor.routing.routing
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
fun Application.notarizedGetWithNotarizedException() {
|
fun Application.notarizedGetWithNotarizedException() {
|
||||||
routing {
|
routing {
|
||||||
@ -526,6 +529,16 @@ fun Application.regexString() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Application.dateTimeString() {
|
||||||
|
routing {
|
||||||
|
route("/test/date_time_format") {
|
||||||
|
notarizedGet(dateTimeString) {
|
||||||
|
call.respond(HttpStatusCode.OK, DateTimeString(Instant.now()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Application.minMaxArray() {
|
fun Application.minMaxArray() {
|
||||||
routing {
|
routing {
|
||||||
route("/test/required_param") {
|
route("/test/required_param") {
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.0.3",
|
||||||
|
"info": {
|
||||||
|
"title": "Test API",
|
||||||
|
"version": "1.33.7",
|
||||||
|
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||||
|
"termsOfService": "https://example.com",
|
||||||
|
"contact": {
|
||||||
|
"name": "Homer Simpson",
|
||||||
|
"url": "https://gph.is/1NPUDiM",
|
||||||
|
"email": "chunkylover53@aol.com"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"name": "MIT",
|
||||||
|
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "https://myawesomeapi.com",
|
||||||
|
"description": "Production instance of my API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://staging.myawesomeapi.com",
|
||||||
|
"description": "Where the fun stuff happens"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/test/date_time_format": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Date time string test",
|
||||||
|
"description": "Cool stuff",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A successful endeavor",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/DateTimeString"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"DateTimeString": {
|
||||||
|
"properties": {
|
||||||
|
"a": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"a"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.0.3",
|
||||||
|
"info": {
|
||||||
|
"title": "Test API",
|
||||||
|
"version": "1.33.7",
|
||||||
|
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||||
|
"termsOfService": "https://example.com",
|
||||||
|
"contact": {
|
||||||
|
"name": "Homer Simpson",
|
||||||
|
"url": "https://gph.is/1NPUDiM",
|
||||||
|
"email": "chunkylover53@aol.com"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"name": "MIT",
|
||||||
|
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "https://myawesomeapi.com",
|
||||||
|
"description": "Production instance of my API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://staging.myawesomeapi.com",
|
||||||
|
"description": "Where the fun stuff happens"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/test/date_time_format": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Date time string test",
|
||||||
|
"description": "Cool stuff",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A successful endeavor",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/DateTimeString"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"DateTimeString": {
|
||||||
|
"properties": {
|
||||||
|
"a": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"a"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package io.bkbn.kompendium.core.fixtures
|
package io.bkbn.kompendium.core.fixtures
|
||||||
|
|
||||||
|
import io.bkbn.kompendium.core.Kompendium
|
||||||
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
||||||
import io.kotest.assertions.json.shouldEqualJson
|
import io.kotest.assertions.json.shouldEqualJson
|
||||||
import io.kotest.assertions.ktor.shouldHaveStatus
|
import io.kotest.assertions.ktor.shouldHaveStatus
|
||||||
@ -53,19 +54,27 @@ object TestHelpers {
|
|||||||
* @param moduleFunction Initializer for the application to allow tests to pass the required Ktor modules
|
* @param moduleFunction Initializer for the application to allow tests to pass the required Ktor modules
|
||||||
*/
|
*/
|
||||||
fun openApiTestAllSerializers(snapshotName: String, moduleFunction: Application.() -> Unit) {
|
fun openApiTestAllSerializers(snapshotName: String, moduleFunction: Application.() -> Unit) {
|
||||||
openApiTest(snapshotName, SupportedSerializer.KOTLINX, moduleFunction)
|
openApiTestAllSerializers(snapshotName, moduleFunction) {}
|
||||||
openApiTest(snapshotName, SupportedSerializer.JACKSON, moduleFunction)
|
}
|
||||||
openApiTest(snapshotName, SupportedSerializer.GSON, moduleFunction)
|
|
||||||
|
fun openApiTestAllSerializers(
|
||||||
|
snapshotName: String, moduleFunction: Application.() -> Unit,
|
||||||
|
kompendiumConfigurer: Kompendium.Configuration.() -> Unit
|
||||||
|
) {
|
||||||
|
openApiTest(snapshotName, SupportedSerializer.KOTLINX, moduleFunction, kompendiumConfigurer)
|
||||||
|
openApiTest(snapshotName, SupportedSerializer.JACKSON, moduleFunction, kompendiumConfigurer)
|
||||||
|
openApiTest(snapshotName, SupportedSerializer.GSON, moduleFunction, kompendiumConfigurer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openApiTest(
|
private fun openApiTest(
|
||||||
snapshotName: String,
|
snapshotName: String,
|
||||||
serializer: SupportedSerializer,
|
serializer: SupportedSerializer,
|
||||||
moduleFunction: Application.() -> Unit
|
moduleFunction: Application.() -> Unit,
|
||||||
|
kompendiumConfigurer: Kompendium.Configuration.() -> Unit
|
||||||
) {
|
) {
|
||||||
withApplication(createTestEnvironment()) {
|
withApplication(createTestEnvironment()) {
|
||||||
moduleFunction(application.apply {
|
moduleFunction(application.apply {
|
||||||
kompendium()
|
kompendium(kompendiumConfigurer)
|
||||||
docs()
|
docs()
|
||||||
when (serializer) {
|
when (serializer) {
|
||||||
SupportedSerializer.KOTLINX -> {
|
SupportedSerializer.KOTLINX -> {
|
||||||
|
@ -21,6 +21,7 @@ import kotlinx.serialization.Serializable
|
|||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
|
import java.time.Instant
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
data class TestSimpleModel(val a: String, val b: Int)
|
data class TestSimpleModel(val a: String, val b: Int)
|
||||||
@ -106,6 +107,10 @@ data class RegexString(
|
|||||||
val a: String
|
val a: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class DateTimeString(
|
||||||
|
val a: Instant
|
||||||
|
)
|
||||||
|
|
||||||
data class MinMaxArray(
|
data class MinMaxArray(
|
||||||
@MinItems(1)
|
@MinItems(1)
|
||||||
@MaxItems(10)
|
@MaxItems(10)
|
||||||
|
@ -27,8 +27,9 @@ fun Application.jacksonConfigModule() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Application.kompendium() {
|
fun Application.kompendium(configurer: Kompendium.Configuration.() -> Unit = {}) {
|
||||||
install(Kompendium) {
|
install(Kompendium) {
|
||||||
spec = defaultSpec()
|
spec = defaultSpec()
|
||||||
|
configurer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,6 +226,13 @@ object TestResponseInfo {
|
|||||||
responseInfo = simpleOkResponse()
|
responseInfo = simpleOkResponse()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
val dateTimeString = GetInfo<Unit, DateTimeString>(
|
||||||
|
summary = "Date time string test",
|
||||||
|
description = "Cool stuff",
|
||||||
|
responseInfo = simpleOkResponse()
|
||||||
|
)
|
||||||
|
|
||||||
val minMaxArray = GetInfo<Unit, MinMaxArray>(
|
val minMaxArray = GetInfo<Unit, MinMaxArray>(
|
||||||
summary = "required param",
|
summary = "required param",
|
||||||
description = "Cool stuff",
|
description = "Cool stuff",
|
||||||
|
@ -43,7 +43,7 @@ private fun Application.mainModule() {
|
|||||||
install(Kompendium) {
|
install(Kompendium) {
|
||||||
spec = Util.baseSpec
|
spec = Util.baseSpec
|
||||||
// Tells Kompendium how to handle a specific type
|
// Tells Kompendium how to handle a specific type
|
||||||
addCustomTypeSchema(Instant::class, SimpleSchema("string"))
|
addCustomTypeSchema(Instant::class, SimpleSchema("string", format = "date-time"))
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
redoc(pageTitle = "Custom overridden type Docs")
|
redoc(pageTitle = "Custom overridden type Docs")
|
||||||
|
Reference in New Issue
Block a user