feat: constraints (#409)
This commit is contained in:
@ -12,6 +12,12 @@
|
|||||||
|
|
||||||
## Released
|
## Released
|
||||||
|
|
||||||
|
## [3.11.0] - January 5th, 2023
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Support for type constraints.
|
||||||
|
|
||||||
## [3.10.0] - January 4th, 2023
|
## [3.10.0] - January 4th, 2023
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -2,6 +2,7 @@ package io.bkbn.kompendium.core
|
|||||||
|
|
||||||
import dev.forst.ktor.apikey.apiKey
|
import dev.forst.ktor.apikey.apiKey
|
||||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
||||||
|
import io.bkbn.kompendium.core.util.arrayConstraints
|
||||||
import io.bkbn.kompendium.core.util.complexRequest
|
import io.bkbn.kompendium.core.util.complexRequest
|
||||||
import io.bkbn.kompendium.core.util.customAuthConfig
|
import io.bkbn.kompendium.core.util.customAuthConfig
|
||||||
import io.bkbn.kompendium.core.util.customFieldNameResponse
|
import io.bkbn.kompendium.core.util.customFieldNameResponse
|
||||||
@ -9,6 +10,7 @@ import io.bkbn.kompendium.core.util.dateTimeString
|
|||||||
import io.bkbn.kompendium.core.util.defaultAuthConfig
|
import io.bkbn.kompendium.core.util.defaultAuthConfig
|
||||||
import io.bkbn.kompendium.core.util.defaultField
|
import io.bkbn.kompendium.core.util.defaultField
|
||||||
import io.bkbn.kompendium.core.util.defaultParameter
|
import io.bkbn.kompendium.core.util.defaultParameter
|
||||||
|
import io.bkbn.kompendium.core.util.doubleConstraints
|
||||||
import io.bkbn.kompendium.core.util.enrichedComplexGenericType
|
import io.bkbn.kompendium.core.util.enrichedComplexGenericType
|
||||||
import io.bkbn.kompendium.core.util.enrichedNestedCollection
|
import io.bkbn.kompendium.core.util.enrichedNestedCollection
|
||||||
import io.bkbn.kompendium.core.util.enrichedSimpleRequest
|
import io.bkbn.kompendium.core.util.enrichedSimpleRequest
|
||||||
@ -20,6 +22,7 @@ import io.bkbn.kompendium.core.util.genericPolymorphicResponseMultipleImpls
|
|||||||
import io.bkbn.kompendium.core.util.gnarlyGenericResponse
|
import io.bkbn.kompendium.core.util.gnarlyGenericResponse
|
||||||
import io.bkbn.kompendium.core.util.headerParameter
|
import io.bkbn.kompendium.core.util.headerParameter
|
||||||
import io.bkbn.kompendium.core.util.ignoredFieldsResponse
|
import io.bkbn.kompendium.core.util.ignoredFieldsResponse
|
||||||
|
import io.bkbn.kompendium.core.util.intConstraints
|
||||||
import io.bkbn.kompendium.core.util.multipleAuthStrategies
|
import io.bkbn.kompendium.core.util.multipleAuthStrategies
|
||||||
import io.bkbn.kompendium.core.util.multipleExceptions
|
import io.bkbn.kompendium.core.util.multipleExceptions
|
||||||
import io.bkbn.kompendium.core.util.nestedGenericCollection
|
import io.bkbn.kompendium.core.util.nestedGenericCollection
|
||||||
@ -56,6 +59,9 @@ import io.bkbn.kompendium.core.util.simpleGenericResponse
|
|||||||
import io.bkbn.kompendium.core.util.simplePathParsing
|
import io.bkbn.kompendium.core.util.simplePathParsing
|
||||||
import io.bkbn.kompendium.core.util.simpleRecursive
|
import io.bkbn.kompendium.core.util.simpleRecursive
|
||||||
import io.bkbn.kompendium.core.util.singleException
|
import io.bkbn.kompendium.core.util.singleException
|
||||||
|
import io.bkbn.kompendium.core.util.stringConstraints
|
||||||
|
import io.bkbn.kompendium.core.util.stringContentEncodingConstraints
|
||||||
|
import io.bkbn.kompendium.core.util.stringPatternConstraints
|
||||||
import io.bkbn.kompendium.core.util.topLevelNullable
|
import io.bkbn.kompendium.core.util.topLevelNullable
|
||||||
import io.bkbn.kompendium.core.util.trailingSlash
|
import io.bkbn.kompendium.core.util.trailingSlash
|
||||||
import io.bkbn.kompendium.core.util.unbackedFieldsResponse
|
import io.bkbn.kompendium.core.util.unbackedFieldsResponse
|
||||||
@ -318,9 +324,6 @@ class KompendiumTest : DescribeSpec({
|
|||||||
exception.message should startWith("A route has already been registered for path: /test/{a} and method: GET")
|
exception.message should startWith("A route has already been registered for path: /test/{a} and method: GET")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
describe("Constraints") {
|
|
||||||
// TODO Assess strategies here
|
|
||||||
}
|
|
||||||
describe("Formats") {
|
describe("Formats") {
|
||||||
it("Can set a format for a simple type schema") {
|
it("Can set a format for a simple type schema") {
|
||||||
openApiTestAllSerializers(
|
openApiTestAllSerializers(
|
||||||
@ -442,4 +445,26 @@ class KompendiumTest : DescribeSpec({
|
|||||||
openApiTestAllSerializers("T0057__enriched_complex_generic_type.json") { enrichedComplexGenericType() }
|
openApiTestAllSerializers("T0057__enriched_complex_generic_type.json") { enrichedComplexGenericType() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
describe("Constraints") {
|
||||||
|
it("Can apply constraints to an int field") {
|
||||||
|
openApiTestAllSerializers("T0059__int_constraints.json") { intConstraints() }
|
||||||
|
}
|
||||||
|
it("Can apply constraints to a double field") {
|
||||||
|
openApiTestAllSerializers("T0060__double_constraints.json") { doubleConstraints() }
|
||||||
|
}
|
||||||
|
it("Can apply a min and max length to a string field") {
|
||||||
|
openApiTestAllSerializers("T0061__string_min_max_constraints.json") { stringConstraints() }
|
||||||
|
}
|
||||||
|
it("Can apply a pattern to a string field") {
|
||||||
|
openApiTestAllSerializers("T0062__string_pattern_constraints.json") { stringPatternConstraints() }
|
||||||
|
}
|
||||||
|
it("Can apply a content encoding and media type to a string field") {
|
||||||
|
openApiTestAllSerializers("T0063__string_content_encoding_constraints.json") {
|
||||||
|
stringContentEncodingConstraints()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it("Can apply constraints to an array field") {
|
||||||
|
openApiTestAllSerializers("T0064__array_constraints.json") { arrayConstraints() }
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
160
core/src/test/kotlin/io/bkbn/kompendium/core/util/Constraints.kt
Normal file
160
core/src/test/kotlin/io/bkbn/kompendium/core/util/Constraints.kt
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package io.bkbn.kompendium.core.util
|
||||||
|
|
||||||
|
import io.bkbn.kompendium.core.fixtures.DoubleResponse
|
||||||
|
import io.bkbn.kompendium.core.fixtures.Page
|
||||||
|
import io.bkbn.kompendium.core.fixtures.TestCreatedResponse
|
||||||
|
import io.bkbn.kompendium.core.fixtures.TestNested
|
||||||
|
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||||
|
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||||
|
import io.bkbn.kompendium.core.util.TestModules.defaultPath
|
||||||
|
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.server.application.install
|
||||||
|
import io.ktor.server.routing.Routing
|
||||||
|
import io.ktor.server.routing.route
|
||||||
|
|
||||||
|
fun Routing.intConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get an int")
|
||||||
|
description("Get an int")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("An int")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
TestCreatedResponse::id {
|
||||||
|
minimum = 2
|
||||||
|
maximum = 100
|
||||||
|
multipleOf = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Routing.doubleConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get a double")
|
||||||
|
description("Get a double")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("A double")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
DoubleResponse::payload {
|
||||||
|
minimum = 2.0
|
||||||
|
maximum = 100.0
|
||||||
|
multipleOf = 2.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Routing.stringConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get a string")
|
||||||
|
description("Get a string with constraints")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("A string")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
TestNested::nesty {
|
||||||
|
maxLength = 10
|
||||||
|
minLength = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Routing.stringPatternConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get a string")
|
||||||
|
description("This is a description")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("A string")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
TestNested::nesty {
|
||||||
|
pattern = "[a-z]+"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Routing.stringContentEncodingConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get a string")
|
||||||
|
description("This is a description")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("A string")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
TestNested::nesty {
|
||||||
|
contentEncoding = "base64"
|
||||||
|
contentMediaType = "image/png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Routing.arrayConstraints() {
|
||||||
|
route(defaultPath) {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
get = GetInfo.builder {
|
||||||
|
summary("Get an array")
|
||||||
|
description("Get an array of strings")
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
description("An array")
|
||||||
|
responseType(
|
||||||
|
enrichment = TypeEnrichment("example") {
|
||||||
|
Page<String>::content {
|
||||||
|
minItems = 2
|
||||||
|
maxItems = 10
|
||||||
|
uniqueItems = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
responseCode(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
80
core/src/test/resources/T0059__int_constraints.json
Normal file
80
core/src/test/resources/T0059__int_constraints.json
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"info": {
|
||||||
|
"title": "Test API",
|
||||||
|
"version": "1.33.7",
|
||||||
|
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||||
|
"termsOfService": "https://example.com",
|
||||||
|
"contact": {
|
||||||
|
"name": "Homer Simpson",
|
||||||
|
"url": "https://gph.is/1NPUDiM",
|
||||||
|
"email": "chunkylover53@aol.com"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"name": "MIT",
|
||||||
|
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "https://myawesomeapi.com",
|
||||||
|
"description": "Production instance of my API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://staging.myawesomeapi.com",
|
||||||
|
"description": "Where the fun stuff happens"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/test/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get an int",
|
||||||
|
"description": "Get an int",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "An int",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestCreatedResponse-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestCreatedResponse-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"c": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32",
|
||||||
|
"multipleOf": 2,
|
||||||
|
"maximum": 100,
|
||||||
|
"minimum": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"c",
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
76
core/src/test/resources/T0060__double_constraints.json
Normal file
76
core/src/test/resources/T0060__double_constraints.json
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"info": {
|
||||||
|
"title": "Test API",
|
||||||
|
"version": "1.33.7",
|
||||||
|
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||||
|
"termsOfService": "https://example.com",
|
||||||
|
"contact": {
|
||||||
|
"name": "Homer Simpson",
|
||||||
|
"url": "https://gph.is/1NPUDiM",
|
||||||
|
"email": "chunkylover53@aol.com"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"name": "MIT",
|
||||||
|
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "https://myawesomeapi.com",
|
||||||
|
"description": "Production instance of my API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://staging.myawesomeapi.com",
|
||||||
|
"description": "Where the fun stuff happens"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/test/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get a double",
|
||||||
|
"description": "Get a double",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A double",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/DoubleResponse-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"DoubleResponse-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"payload": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double",
|
||||||
|
"multipleOf": 2.0,
|
||||||
|
"maximum": 100.0,
|
||||||
|
"minimum": 2.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"payload"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"info": {
|
||||||
|
"title": "Test API",
|
||||||
|
"version": "1.33.7",
|
||||||
|
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||||
|
"termsOfService": "https://example.com",
|
||||||
|
"contact": {
|
||||||
|
"name": "Homer Simpson",
|
||||||
|
"url": "https://gph.is/1NPUDiM",
|
||||||
|
"email": "chunkylover53@aol.com"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"name": "MIT",
|
||||||
|
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "https://myawesomeapi.com",
|
||||||
|
"description": "Production instance of my API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://staging.myawesomeapi.com",
|
||||||
|
"description": "Where the fun stuff happens"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/test/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get a string",
|
||||||
|
"description": "Get a string with constraints",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A string",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestNested-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestNested-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"nesty": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 10,
|
||||||
|
"minLength": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"nesty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"info": {
|
||||||
|
"title": "Test API",
|
||||||
|
"version": "1.33.7",
|
||||||
|
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||||
|
"termsOfService": "https://example.com",
|
||||||
|
"contact": {
|
||||||
|
"name": "Homer Simpson",
|
||||||
|
"url": "https://gph.is/1NPUDiM",
|
||||||
|
"email": "chunkylover53@aol.com"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"name": "MIT",
|
||||||
|
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "https://myawesomeapi.com",
|
||||||
|
"description": "Production instance of my API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://staging.myawesomeapi.com",
|
||||||
|
"description": "Where the fun stuff happens"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/test/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get a string",
|
||||||
|
"description": "This is a description",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A string",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestNested-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestNested-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"nesty": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "[a-z]+"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"nesty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"info": {
|
||||||
|
"title": "Test API",
|
||||||
|
"version": "1.33.7",
|
||||||
|
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||||
|
"termsOfService": "https://example.com",
|
||||||
|
"contact": {
|
||||||
|
"name": "Homer Simpson",
|
||||||
|
"url": "https://gph.is/1NPUDiM",
|
||||||
|
"email": "chunkylover53@aol.com"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"name": "MIT",
|
||||||
|
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "https://myawesomeapi.com",
|
||||||
|
"description": "Production instance of my API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://staging.myawesomeapi.com",
|
||||||
|
"description": "Where the fun stuff happens"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/test/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get a string",
|
||||||
|
"description": "This is a description",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A string",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TestNested-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"TestNested-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"nesty": {
|
||||||
|
"type": "string",
|
||||||
|
"contentEncoding": "base64",
|
||||||
|
"contentMediaType": "image/png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"nesty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
103
core/src/test/resources/T0064__array_constraints.json
Normal file
103
core/src/test/resources/T0064__array_constraints.json
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"info": {
|
||||||
|
"title": "Test API",
|
||||||
|
"version": "1.33.7",
|
||||||
|
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||||
|
"termsOfService": "https://example.com",
|
||||||
|
"contact": {
|
||||||
|
"name": "Homer Simpson",
|
||||||
|
"url": "https://gph.is/1NPUDiM",
|
||||||
|
"email": "chunkylover53@aol.com"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"name": "MIT",
|
||||||
|
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "https://myawesomeapi.com",
|
||||||
|
"description": "Production instance of my API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://staging.myawesomeapi.com",
|
||||||
|
"description": "Where the fun stuff happens"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/test/{a}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [],
|
||||||
|
"summary": "Get an array",
|
||||||
|
"description": "Get an array of strings",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "An array",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Page-String-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
},
|
||||||
|
"parameters": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webhooks": {},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Page-String-example": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"content": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"maxItems": 10,
|
||||||
|
"minItems": 2,
|
||||||
|
"uniqueItems": true,
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"number": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"numberOfElements": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"totalElements": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"totalPages": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"content",
|
||||||
|
"number",
|
||||||
|
"numberOfElements",
|
||||||
|
"size",
|
||||||
|
"totalElements",
|
||||||
|
"totalPages"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"security": [],
|
||||||
|
"tags": []
|
||||||
|
}
|
@ -12,6 +12,9 @@ import java.time.Instant
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class TestNested(val nesty: String)
|
data class TestNested(val nesty: String)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class DoubleResponse(val payload: Double)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TestRequest(
|
data class TestRequest(
|
||||||
val fieldName: TestNested,
|
val fieldName: TestNested,
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
* [Introduction](index.md)
|
* [Introduction](index.md)
|
||||||
* [Helpers](helpers/index.md)
|
* [Helpers](helpers/index.md)
|
||||||
* [Protobuf java converter](helpers/protobuf_java_converter.md)
|
* [Protobuf java converter](helpers/protobuf_java_converter.md)
|
||||||
|
* [Concepts](concepts/index.md)
|
||||||
|
* [Enrichment](concepts/enrichment.md)
|
||||||
* [Plugins](plugins/index.md)
|
* [Plugins](plugins/index.md)
|
||||||
* [Notarized Application](plugins/notarized_application.md)
|
* [Notarized Application](plugins/notarized_application.md)
|
||||||
* [Notarized Route](plugins/notarized_route.md)
|
* [Notarized Route](plugins/notarized_route.md)
|
||||||
|
107
docs/concepts/enrichment.md
Normal file
107
docs/concepts/enrichment.md
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
Kompendium allows users to enrich their data types with additional information. This can be done by defining a
|
||||||
|
`TypeEnrichment` object and passing it to the `enrich` function on the `NotarizedRoute` builder. Enrichments
|
||||||
|
can be added to any request or response.
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
data class SimpleData(val a: String, val b: Int? = null)
|
||||||
|
|
||||||
|
val myEnrichment = TypeEnrichment<SimpleData>(id = "simple-enrichment") {
|
||||||
|
SimpleData::a {
|
||||||
|
description = "This will update the field description"
|
||||||
|
}
|
||||||
|
SimpleData::b {
|
||||||
|
// Will indicate in the UI that the field will be removed soon
|
||||||
|
deprecated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In your route documentation
|
||||||
|
fun Routing.enrichedSimpleRequest() {
|
||||||
|
route("/example") {
|
||||||
|
install(NotarizedRoute()) {
|
||||||
|
parameters = TestModules.defaultParams
|
||||||
|
post = PostInfo.builder {
|
||||||
|
summary(TestModules.defaultPathSummary)
|
||||||
|
description(TestModules.defaultPathDescription)
|
||||||
|
request {
|
||||||
|
requestType<SimpleData>(enrichment = myEnrichment) // Simply attach the enrichment to the request
|
||||||
|
description("A test request")
|
||||||
|
}
|
||||||
|
response {
|
||||||
|
responseCode(HttpStatusCode.Created)
|
||||||
|
responseType<TestCreatedResponse>()
|
||||||
|
description(TestModules.defaultResponseDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{% hint style="warning" %}
|
||||||
|
An enrichment must provide an `id` field that is unique to the data class that is being enriched. This is because
|
||||||
|
under the hood, Kompendium appends this id to the data class identifier in order to support multiple different
|
||||||
|
enrichments
|
||||||
|
on the same data class.
|
||||||
|
|
||||||
|
If you provide duplicate ids, all but the first enrichment will be ignored, as Kompendium will view that as a cache hit,
|
||||||
|
and skip analyzing the new enrichment.
|
||||||
|
{% endhint %}
|
||||||
|
|
||||||
|
### Nested Enrichments
|
||||||
|
|
||||||
|
Enrichments are portable and composable, meaning that we can take an enrichment for a child data class
|
||||||
|
and apply it inside a parent data class using the `typeEnrichment` property.
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
data class ParentData(val a: String, val b: ChildData)
|
||||||
|
data class ChildData(val c: String, val d: Int? = null)
|
||||||
|
|
||||||
|
val childEnrichment = TypeEnrichment<ChildData>(id = "child-enrichment") {
|
||||||
|
ChildData::c {
|
||||||
|
description = "This will update the field description of field c on child data"
|
||||||
|
}
|
||||||
|
ChildData::d {
|
||||||
|
description = "This will update the field description of field d on child data"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val parentEnrichment = TypeEnrichment<ParentData>(id = "parent-enrichment") {
|
||||||
|
ParentData::a {
|
||||||
|
description = "This will update the field description"
|
||||||
|
}
|
||||||
|
ParentData::b {
|
||||||
|
description = "This will update the field description of field b on parent data"
|
||||||
|
typeEnrichment = childEnrichment // Will apply the child enrichment to the internals of field b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Enrichments
|
||||||
|
|
||||||
|
All enrichments support the following properties:
|
||||||
|
|
||||||
|
- description -> Provides a reader friendly description of the field in the object
|
||||||
|
- deprecated -> Indicates that the field is deprecated and should not be used
|
||||||
|
|
||||||
|
### String
|
||||||
|
|
||||||
|
- minLength -> The minimum length of the string
|
||||||
|
- maxLength -> The maximum length of the string
|
||||||
|
- pattern -> A regex pattern that the string must match
|
||||||
|
- contentEncoding -> The encoding of the string
|
||||||
|
- contentMediaType -> The media type of the string
|
||||||
|
|
||||||
|
### Numbers
|
||||||
|
|
||||||
|
- minimum -> The minimum value of the number
|
||||||
|
- maximum -> The maximum value of the number
|
||||||
|
- exclusiveMinimum -> Indicates that the minimum value is exclusive
|
||||||
|
- exclusiveMaximum -> Indicates that the maximum value is exclusive
|
||||||
|
- multipleOf -> Indicates that the number must be a multiple of the provided value
|
||||||
|
|
||||||
|
### Arrays
|
||||||
|
|
||||||
|
- minItems -> The minimum number of items in the array
|
||||||
|
- maxItems -> The maximum number of items in the array
|
||||||
|
- uniqueItems -> Indicates that the array must contain unique items
|
2
docs/concepts/index.md
Normal file
2
docs/concepts/index.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Various concepts that are core to Kompendium but not necessarily exclusive
|
||||||
|
to any given module or plugin
|
@ -204,89 +204,3 @@ route("/user/{id}") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Enrichment
|
|
||||||
|
|
||||||
Kompendium allows users to enrich their data types with additional information. This can be done by defining a
|
|
||||||
`TypeEnrichment` object and passing it to the `enrich` function on the `NotarizedRoute` builder. Enrichments
|
|
||||||
can be added to any request or response.
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
data class SimpleData(val a: String, val b: Int? = null)
|
|
||||||
|
|
||||||
val myEnrichment = TypeEnrichment<SimpleData>(id = "simple-enrichment") {
|
|
||||||
SimpleData::a {
|
|
||||||
description = "This will update the field description"
|
|
||||||
}
|
|
||||||
SimpleData::b {
|
|
||||||
// Will indicate in the UI that the field will be removed soon
|
|
||||||
deprecated = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In your route documentation
|
|
||||||
fun Routing.enrichedSimpleRequest() {
|
|
||||||
route("/example") {
|
|
||||||
install(NotarizedRoute()) {
|
|
||||||
parameters = TestModules.defaultParams
|
|
||||||
post = PostInfo.builder {
|
|
||||||
summary(TestModules.defaultPathSummary)
|
|
||||||
description(TestModules.defaultPathDescription)
|
|
||||||
request {
|
|
||||||
requestType<SimpleData>(enrichment = myEnrichment) // Simply attach the enrichment to the request
|
|
||||||
description("A test request")
|
|
||||||
}
|
|
||||||
response {
|
|
||||||
responseCode(HttpStatusCode.Created)
|
|
||||||
responseType<TestCreatedResponse>()
|
|
||||||
description(TestModules.defaultResponseDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
{% hint style="warning" %}
|
|
||||||
An enrichment must provide an `id` field that is unique to the data class that is being enriched. This is because
|
|
||||||
under the hood, Kompendium appends this id to the data class identifier in order to support multiple different
|
|
||||||
enrichments
|
|
||||||
on the same data class.
|
|
||||||
|
|
||||||
If you provide duplicate ids, all but the first enrichment will be ignored, as Kompendium will view that as a cache hit,
|
|
||||||
and skip analyzing the new enrichment.
|
|
||||||
{% endhint %}
|
|
||||||
|
|
||||||
At the moment, the only available enrichments are the following
|
|
||||||
|
|
||||||
- description -> Provides a reader friendly description of the field in the object
|
|
||||||
- deprecated -> Indicates that the field is deprecated and should not be used
|
|
||||||
|
|
||||||
### Nested Enrichments
|
|
||||||
|
|
||||||
Enrichments are portable and composable, meaning that we can take an enrichment for a child data class
|
|
||||||
and apply it inside a parent data class using the `typeEnrichment` property.
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
data class ParentData(val a: String, val b: ChildData)
|
|
||||||
data class ChildData(val c: String, val d: Int? = null)
|
|
||||||
|
|
||||||
val childEnrichment = TypeEnrichment<ChildData>(id = "child-enrichment") {
|
|
||||||
ChildData::c {
|
|
||||||
description = "This will update the field description of field c on child data"
|
|
||||||
}
|
|
||||||
ChildData::d {
|
|
||||||
description = "This will update the field description of field d on child data"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val parentEnrichment = TypeEnrichment<ParentData>(id = "parent-enrichment") {
|
|
||||||
ParentData::a {
|
|
||||||
description = "This will update the field description"
|
|
||||||
}
|
|
||||||
ParentData::b {
|
|
||||||
description = "This will update the field description of field b on parent data"
|
|
||||||
typeEnrichment = childEnrichment // Will apply the child enrichment to the internals of field b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
@ -1,7 +1,36 @@
|
|||||||
package io.bkbn.kompendium.enrichment
|
package io.bkbn.kompendium.enrichment
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference https://json-schema.org/draft/2020-12/json-schema-validation.html#name-multipleof
|
||||||
|
*/
|
||||||
class PropertyEnrichment : Enrichment {
|
class PropertyEnrichment : Enrichment {
|
||||||
|
// Metadata
|
||||||
var deprecated: Boolean? = null
|
var deprecated: Boolean? = null
|
||||||
var description: String? = null
|
var description: String? = null
|
||||||
var typeEnrichment: TypeEnrichment<*>? = null
|
var typeEnrichment: TypeEnrichment<*>? = null
|
||||||
|
|
||||||
|
// Number and Integer Constraints
|
||||||
|
var multipleOf: Number? = null
|
||||||
|
var maximum: Number? = null
|
||||||
|
var exclusiveMaximum: Number? = null
|
||||||
|
var minimum: Number? = null
|
||||||
|
var exclusiveMinimum: Number? = null
|
||||||
|
|
||||||
|
// String constraints
|
||||||
|
var maxLength: Int? = null
|
||||||
|
var minLength: Int? = null
|
||||||
|
var pattern: String? = null
|
||||||
|
var contentEncoding: String? = null
|
||||||
|
var contentMediaType: String? = null
|
||||||
|
// TODO how to handle contentSchema?
|
||||||
|
|
||||||
|
// Array constraints
|
||||||
|
var maxItems: Int? = null
|
||||||
|
var minItems: Int? = null
|
||||||
|
var uniqueItems: Boolean? = null
|
||||||
|
// TODO How to handle contains, minContains, maxContains?
|
||||||
|
|
||||||
|
// Object constraints
|
||||||
|
var maxProperties: Int? = null
|
||||||
|
var minProperties: Int? = null
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Kompendium
|
# Kompendium
|
||||||
project.version=3.10.0
|
project.version=3.11.0
|
||||||
# Kotlin
|
# Kotlin
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
# Gradle
|
# Gradle
|
||||||
|
@ -7,6 +7,11 @@ data class ArrayDefinition(
|
|||||||
val items: JsonSchema,
|
val items: JsonSchema,
|
||||||
override val deprecated: Boolean? = null,
|
override val deprecated: Boolean? = null,
|
||||||
override val description: String? = null,
|
override val description: String? = null,
|
||||||
|
|
||||||
|
// Constraints
|
||||||
|
val maxItems: Int? = null,
|
||||||
|
val minItems: Int? = null,
|
||||||
|
val uniqueItems: Boolean? = null,
|
||||||
) : JsonSchema {
|
) : JsonSchema {
|
||||||
val type: String = "array"
|
val type: String = "array"
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.bkbn.kompendium.json.schema.definition
|
package io.bkbn.kompendium.json.schema.definition
|
||||||
|
|
||||||
|
import io.bkbn.kompendium.json.schema.util.Serializers
|
||||||
import kotlinx.serialization.Contextual
|
import kotlinx.serialization.Contextual
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@ -12,6 +13,30 @@ data class TypeDefinition(
|
|||||||
@Contextual val default: Any? = null,
|
@Contextual val default: Any? = null,
|
||||||
override val deprecated: Boolean? = null,
|
override val deprecated: Boolean? = null,
|
||||||
override val description: String? = null,
|
override val description: String? = null,
|
||||||
|
// Constraints
|
||||||
|
|
||||||
|
// Number
|
||||||
|
@Serializable(with = Serializers.Number::class)
|
||||||
|
val multipleOf: Number? = null,
|
||||||
|
@Serializable(with = Serializers.Number::class)
|
||||||
|
val maximum: Number? = null,
|
||||||
|
@Serializable(with = Serializers.Number::class)
|
||||||
|
val exclusiveMaximum: Number? = null,
|
||||||
|
@Serializable(with = Serializers.Number::class)
|
||||||
|
val minimum: Number? = null,
|
||||||
|
@Serializable(with = Serializers.Number::class)
|
||||||
|
val exclusiveMinimum: Number? = null,
|
||||||
|
|
||||||
|
// String
|
||||||
|
val maxLength: Int? = null,
|
||||||
|
val minLength: Int? = null,
|
||||||
|
val pattern: String? = null,
|
||||||
|
val contentEncoding: String? = null,
|
||||||
|
val contentMediaType: String? = null,
|
||||||
|
|
||||||
|
// Object
|
||||||
|
val maxProperties: Int? = null,
|
||||||
|
val minProperties: Int? = null,
|
||||||
) : JsonSchema {
|
) : JsonSchema {
|
||||||
|
|
||||||
fun withDefault(default: Any): TypeDefinition = this.copy(default = default)
|
fun withDefault(default: Any): TypeDefinition = this.copy(default = default)
|
||||||
|
@ -169,12 +169,33 @@ object SimpleObjectHandler {
|
|||||||
|
|
||||||
private fun PropertyEnrichment.applyToSchema(schema: JsonSchema): JsonSchema = when (schema) {
|
private fun PropertyEnrichment.applyToSchema(schema: JsonSchema): JsonSchema = when (schema) {
|
||||||
is AnyOfDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is AnyOfDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is ArrayDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is ArrayDefinition -> schema.copy(
|
||||||
|
deprecated = deprecated,
|
||||||
|
description = description,
|
||||||
|
minItems = minItems,
|
||||||
|
maxItems = maxItems,
|
||||||
|
uniqueItems = uniqueItems,
|
||||||
|
)
|
||||||
is EnumDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is EnumDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is MapDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is MapDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is NullableDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is NullableDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is OneOfDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is OneOfDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is ReferenceDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is ReferenceDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||||
is TypeDefinition -> schema.copy(deprecated = deprecated, description = description)
|
is TypeDefinition -> schema.copy(
|
||||||
|
deprecated = deprecated,
|
||||||
|
description = description,
|
||||||
|
multipleOf = multipleOf,
|
||||||
|
maximum = maximum,
|
||||||
|
exclusiveMaximum = exclusiveMaximum,
|
||||||
|
minimum = minimum,
|
||||||
|
exclusiveMinimum = exclusiveMinimum,
|
||||||
|
maxLength = maxLength,
|
||||||
|
minLength = minLength,
|
||||||
|
pattern = pattern,
|
||||||
|
contentEncoding = contentEncoding,
|
||||||
|
contentMediaType = contentMediaType,
|
||||||
|
maxProperties = maxProperties,
|
||||||
|
minProperties = minProperties,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package io.bkbn.kompendium.json.schema.util
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import java.util.UUID
|
||||||
|
import kotlin.Number as KNumber
|
||||||
|
|
||||||
|
object Serializers {
|
||||||
|
|
||||||
|
object Uuid : KSerializer<UUID> {
|
||||||
|
override val descriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): UUID {
|
||||||
|
return UUID.fromString(decoder.decodeString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: UUID) {
|
||||||
|
encoder.encodeString(value.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Number : KSerializer<KNumber> {
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Number", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): KNumber {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: KNumber) {
|
||||||
|
when (value) {
|
||||||
|
is Int -> encoder.encodeInt(value)
|
||||||
|
is Long -> encoder.encodeLong(value)
|
||||||
|
is Double -> encoder.encodeDouble(value)
|
||||||
|
is Float -> encoder.encodeFloat(value)
|
||||||
|
else -> throw IllegalArgumentException("Number is not a valid type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -70,6 +70,8 @@ private val testEnrichment = TypeEnrichment("testerino") {
|
|||||||
description = "A good but old field"
|
description = "A good but old field"
|
||||||
typeEnrichment = TypeEnrichment("big-tings") {
|
typeEnrichment = TypeEnrichment("big-tings") {
|
||||||
InnerRequest::d {
|
InnerRequest::d {
|
||||||
|
exclusiveMaximum = 10.0
|
||||||
|
exclusiveMinimum = 1.1
|
||||||
description = "THE BIG D"
|
description = "THE BIG D"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user