Compare commits

..

7 Commits

Author SHA1 Message Date
a5376cfa82 feat: allow media type overrides (#369) 2022-11-05 16:45:06 -04:00
2492661f1f chore: prep for release 2022-11-05 16:11:38 -04:00
a7b52ec114 feat: create schema reference for enum types (#368) 2022-11-05 16:09:06 -04:00
8ebab04a83 fix(deps): update kotestversion to v5.5.4 (#363)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-04 06:00:41 +00:00
921f6f9691 fix(deps): update dependency dev.forst:ktor-api-key to v2.1.3 (#361)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-31 22:25:14 +00:00
558b9fea62 fix(deps): update ktor to v2.1.3 (#360)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-31 17:01:30 +00:00
752cb238d3 fix(deps): update dependency joda-time:joda-time to v2.12.1 (#359)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-30 06:12:11 +00:00
30 changed files with 431 additions and 123 deletions

View File

@ -12,6 +12,19 @@
## Released
## [3.7.0] - November 5th, 2022
### Added
- Allow users to override media type in request and response
## [3.6.0] - November 5th, 2022
### Changed
- Schemas for types in nullable properties are no longer nullable themselves
- Enums are now generated as references, which makes it possible to generate types for them
## [3.5.0] - October 29th, 2022
### Added

View File

@ -58,7 +58,7 @@ dependencies {
testFixturesApi("io.ktor:ktor-client:$ktorVersion")
testFixturesApi("io.ktor:ktor-client-cio:$ktorVersion")
testFixturesApi("dev.forst:ktor-api-key:2.1.2")
testFixturesApi("dev.forst:ktor-api-key:2.1.3")
testFixturesApi("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
}

View File

@ -7,7 +7,8 @@ import kotlin.reflect.typeOf
class RequestInfo private constructor(
val requestType: KType,
val description: String,
val examples: Map<String, MediaType.Example>?
val examples: Map<String, MediaType.Example>?,
val mediaTypes: Set<String>
) {
companion object {
@ -22,6 +23,7 @@ class RequestInfo private constructor(
private var requestType: KType? = null
private var description: String? = null
private var examples: Map<String, MediaType.Example>? = null
private var mediaTypes: Set<String>? = null
fun requestType(t: KType) = apply {
this.requestType = t
@ -35,10 +37,15 @@ class RequestInfo private constructor(
this.examples = e.toMap().mapValues { (_, v) -> MediaType.Example(v) }
}
fun mediaTypes(vararg m: String) = apply {
this.mediaTypes = m.toSet()
}
fun build() = RequestInfo(
requestType = requestType ?: error("Request type must be present"),
description = description ?: error("Description must be present"),
examples = examples
examples = examples,
mediaTypes = mediaTypes ?: setOf("application/json")
)
}
}

View File

@ -9,7 +9,8 @@ class ResponseInfo private constructor(
val responseCode: HttpStatusCode,
val responseType: KType,
val description: String,
val examples: Map<String, MediaType.Example>?
val examples: Map<String, MediaType.Example>?,
val mediaTypes: Set<String>
) {
companion object {
@ -25,6 +26,7 @@ class ResponseInfo private constructor(
private var responseType: KType? = null
private var description: String? = null
private var examples: Map<String, MediaType.Example>? = null
private var mediaTypes: Set<String>? = null
fun responseCode(code: HttpStatusCode) = apply {
this.responseCode = code
@ -42,11 +44,16 @@ class ResponseInfo private constructor(
this.examples = e.toMap().mapValues { (_, v) -> MediaType.Example(v) }
}
fun mediaTypes(vararg m: String) = apply {
this.mediaTypes = m.toSet()
}
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
examples = examples,
mediaTypes = mediaTypes ?: setOf("application/json")
)
}
}

View File

@ -12,6 +12,8 @@ import io.bkbn.kompendium.core.metadata.PutInfo
import io.bkbn.kompendium.core.metadata.ResponseInfo
import io.bkbn.kompendium.json.schema.SchemaConfigurator
import io.bkbn.kompendium.json.schema.SchemaGenerator
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.util.Helpers.getReferenceSlug
import io.bkbn.kompendium.json.schema.util.Helpers.getSimpleSlug
@ -82,7 +84,7 @@ object Helpers {
requestBody = when (this) {
is MethodInfoWithRequest -> Request(
description = this.request.description,
content = this.request.requestType.toReferenceContent(this.request.examples),
content = this.request.requestType.toReferenceContent(this.request.examples, this.request.mediaTypes),
required = true
)
@ -91,7 +93,7 @@ object Helpers {
responses = mapOf(
this.response.responseCode.value to Response(
description = this.response.description,
content = this.response.responseType.toReferenceContent(this.response.examples)
content = this.response.responseType.toReferenceContent(this.response.examples, this.response.mediaTypes)
)
).plus(this.errors.toResponseMap())
)
@ -99,18 +101,24 @@ object Helpers {
private fun List<ResponseInfo>.toResponseMap(): Map<Int, Response> = associate { error ->
error.responseCode.value to Response(
description = error.description,
content = error.responseType.toReferenceContent(error.examples)
content = error.responseType.toReferenceContent(error.examples, error.mediaTypes)
)
}
private fun KType.toReferenceContent(examples: Map<String, MediaType.Example>?): Map<String, MediaType>? =
private fun KType.toReferenceContent(
examples: Map<String, MediaType.Example>?,
mediaTypes: Set<String>
): Map<String, MediaType>? =
when (this.classifier as KClass<*>) {
Unit::class -> null
else -> mapOf(
"application/json" to MediaType(
schema = ReferenceDefinition(this.getReferenceSlug()),
else -> mediaTypes.associateWith {
MediaType(
schema = if (this.isMarkedNullable) OneOfDefinition(
NullableDefinition(),
ReferenceDefinition(this.getReferenceSlug())
) else ReferenceDefinition(this.getReferenceSlug()),
examples = examples
)
)
}
}
}

View File

@ -36,6 +36,7 @@ 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.nullableReference
import io.bkbn.kompendium.core.util.TestModules.overrideMediaTypes
import io.bkbn.kompendium.core.util.TestModules.polymorphicCollectionResponse
import io.bkbn.kompendium.core.util.TestModules.polymorphicException
import io.bkbn.kompendium.core.util.TestModules.polymorphicMapResponse
@ -49,6 +50,7 @@ import io.bkbn.kompendium.core.util.TestModules.simpleGenericResponse
import io.bkbn.kompendium.core.util.TestModules.simplePathParsing
import io.bkbn.kompendium.core.util.TestModules.simpleRecursive
import io.bkbn.kompendium.core.util.TestModules.singleException
import io.bkbn.kompendium.core.util.TestModules.topLevelNullable
import io.bkbn.kompendium.core.util.TestModules.trailingSlash
import io.bkbn.kompendium.core.util.TestModules.unbackedFieldsResponse
import io.bkbn.kompendium.core.util.TestModules.withOperationId
@ -111,6 +113,9 @@ class KompendiumTest : DescribeSpec({
it("Can notarize a route with non-required params") {
openApiTestAllSerializers("T0011__non_required_params.json") { nonRequiredParams() }
}
it("Can override media types") {
openApiTestAllSerializers("T0052__override_media_types.json") { overrideMediaTypes() }
}
}
describe("Route Parsing") {
it("Can parse a simple path and store it under the expected route") {
@ -243,6 +248,9 @@ class KompendiumTest : DescribeSpec({
it("Can handle nested type names") {
openApiTestAllSerializers("T0044__nested_type_name.json") { nestedTypeName() }
}
it("Can handle top level nullable types") {
openApiTestAllSerializers("T0051__top_level_nullable.json") { topLevelNullable() }
}
}
describe("Error Handling") {
it("Throws a clear exception when an unidentified type is encountered") {

View File

@ -24,7 +24,7 @@ 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.fixtures.TransientObject
import io.bkbn.kompendium.core.fixtures.UnbakcedObject
import io.bkbn.kompendium.core.fixtures.UnbackedObject
import io.bkbn.kompendium.core.metadata.DeleteInfo
import io.bkbn.kompendium.core.metadata.GetInfo
import io.bkbn.kompendium.core.metadata.HeadInfo
@ -315,6 +315,28 @@ object TestModules {
}
}
fun Routing.overrideMediaTypes() {
route("/media_types") {
install(NotarizedRoute()) {
put = PutInfo.builder {
summary(defaultPathSummary)
description(defaultPathDescription)
request {
mediaTypes("multipart/form-data", "application/json")
requestType<TestRequest>()
description("A cool request")
}
response {
mediaTypes("application/xml")
responseType<TestResponse>()
description("A good response")
responseCode(HttpStatusCode.Created)
}
}
}
}
}
fun Routing.simplePathParsing() {
route("/this") {
route("/is") {
@ -568,7 +590,7 @@ object TestModules {
fun Routing.ignoredFieldsResponse() = basicGetGenerator<TransientObject>()
fun Routing.unbackedFieldsResponse() = basicGetGenerator<UnbakcedObject>()
fun Routing.unbackedFieldsResponse() = basicGetGenerator<UnbackedObject>()
fun Routing.customFieldNameResponse() = basicGetGenerator<SerialNameObject>()
@ -613,6 +635,8 @@ object TestModules {
fun Routing.nestedTypeName() = basicGetGenerator<Nested.Response>()
fun Routing.topLevelNullable() = basicGetGenerator<TestResponse?>()
fun Routing.simpleRecursive() = basicGetGenerator<ColumnSchema>()
fun Routing.defaultAuthConfig() {

View File

@ -124,15 +124,19 @@
"type": "object",
"properties": {
"enumeration": {
"enum": [
"ONE",
"TWO"
]
"$ref": "#/components/schemas/SimpleEnum"
}
},
"required": [
"enumeration"
]
},
"SimpleEnum": {
"type": "string",
"enum": [
"ONE",
"TWO"
]
}
},
"securitySchemes": {}

View File

@ -91,11 +91,6 @@
"required": []
},
"ProfileMetadataUpdateRequest": {
"oneOf": [
{
"type": "null"
},
{
"type": "object",
"properties": {
"isPrivate": {
@ -121,8 +116,6 @@
},
"required": []
}
]
}
},
"securitySchemes": {}
},

View File

@ -62,15 +62,20 @@
"type": "null"
},
{
"enum": [
"YES",
"NO"
]
"$ref": "#/components/schemas/TestEnum"
}
]
}
},
"required": []
},
"TestEnum":
{
"type": "string",
"enum": [
"YES",
"NO"
]
}
},
"securitySchemes": {}

View File

@ -57,10 +57,7 @@
"type": "object",
"properties": {
"enumeration": {
"enum": [
"ONE",
"TWO"
]
"$ref": "#/components/schemas/SimpleEnum"
}
},
"required": [
@ -120,6 +117,13 @@
"required": [
"content"
]
},
"SimpleEnum": {
"type": "string",
"enum": [
"ONE",
"TWO"
]
}
},
"securitySchemes": {}

View File

@ -53,6 +53,14 @@
"webhooks": {},
"components": {
"schemas": {
"ColumnMode": {
"type": "string",
"enum": [
"NULLABLE",
"REQUIRED",
"REPEATED"
]
},
"ColumnSchema": {
"type": "object",
"properties": {
@ -60,11 +68,7 @@
"type": "string"
},
"mode": {
"enum": [
"NULLABLE",
"REQUIRED",
"REPEATED"
]
"$ref": "#/components/schemas/ColumnMode"
},
"name": {
"type": "string"

View File

@ -39,7 +39,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnbakcedObject"
"$ref": "#/components/schemas/UnbackedObject"
}
}
}
@ -53,7 +53,7 @@
"webhooks": {},
"components": {
"schemas": {
"UnbakcedObject": {
"UnbackedObject": {
"type": "object",
"properties": {
"backed": {

View File

@ -0,0 +1,79 @@
{
"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": {
"oneOf": [
{
"type": "null"
},
{
"$ref": "#/components/schemas/TestResponse"
}
]
}
}
}
}
},
"deprecated": false
},
"parameters": []
}
},
"webhooks": {},
"components": {
"schemas": {
"TestResponse": {
"type": "object",
"properties": {
"c": {
"type": "string"
}
},
"required": [
"c"
]
}
},
"securitySchemes": {}
},
"security": [],
"tags": []
}

View File

@ -0,0 +1,123 @@
{
"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": {
"/media_types": {
"put": {
"tags": [],
"summary": "Great Summary!",
"description": "testing more",
"parameters": [],
"requestBody": {
"description": "A cool request",
"content": {
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/TestRequest"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/TestRequest"
}
}
},
"required": true
},
"responses": {
"201": {
"description": "A good response",
"content": {
"application/xml": {
"schema": {
"$ref": "#/components/schemas/TestResponse"
}
}
}
}
},
"deprecated": false
},
"parameters": []
}
},
"webhooks": {},
"components": {
"schemas": {
"TestResponse": {
"type": "object",
"properties": {
"c": {
"type": "string"
}
},
"required": [
"c"
]
},
"TestRequest": {
"type": "object",
"properties": {
"aaa": {
"items": {
"type": "number",
"format": "int64"
},
"type": "array"
},
"b": {
"type": "number",
"format": "double"
},
"fieldName": {
"$ref": "#/components/schemas/TestNested"
}
},
"required": [
"aaa",
"b",
"fieldName"
]
},
"TestNested": {
"type": "object",
"properties": {
"nesty": {
"type": "string"
}
},
"required": [
"nesty"
]
}
},
"securitySchemes": {}
},
"security": [],
"tags": []
}

View File

@ -163,7 +163,7 @@ data class TransientObject(
)
@Serializable
data class UnbakcedObject(
data class UnbackedObject(
val backed: String
) {
val unbacked: String get() = "unbacked"
@ -176,3 +176,14 @@ data class SerialNameObject(
@SerialName("snake_case_name")
val camelCaseName: String
)
enum class Color {
RED,
GREEN,
BLUE
}
@Serializable
data class ObjectWithEnum(
val color: Color
)

View File

@ -165,3 +165,21 @@ get = GetInfo.builder {
}
}
```
## Media Types
By default, Kompendium will set the only media type to "application/json". If you would like to override the media type
for a specific request or response (including errors), you can do so with the `mediaTypes` method
```kotlin
get = GetInfo.builder {
summary("Get user by id")
description("A very neat endpoint!")
response {
mediaTypes("application/xml")
responseCode(HttpStatusCode.OK)
responseType<ExampleResponse>()
description("Will return whether or not the user is real 😱")
}
}
```

View File

@ -1,5 +1,5 @@
# Kompendium
project.version=3.5.0
project.version=3.6.0
# Kotlin
kotlin.code.style=official
# Gradle
@ -9,5 +9,5 @@ org.gradle.jvmargs=-Xmx2000m
# Dependencies
ktorVersion=2.1.3
kotestVersion=5.5.3
kotestVersion=5.5.4
detektVersion=1.21.0

View File

@ -48,7 +48,7 @@ object SchemaGenerator {
Boolean::class -> checkForNull(type, TypeDefinition.BOOLEAN)
UUID::class -> checkForNull(type, TypeDefinition.UUID)
else -> when {
clazz.isSubclassOf(Enum::class) -> EnumHandler.handle(type, clazz)
clazz.isSubclassOf(Enum::class) -> EnumHandler.handle(type, clazz, cache)
clazz.isSubclassOf(Collection::class) -> CollectionHandler.handle(type, cache, schemaConfigurator)
clazz.isSubclassOf(Map::class) -> MapHandler.handle(type, cache, schemaConfigurator)
else -> {

View File

@ -4,5 +4,6 @@ import kotlinx.serialization.Serializable
@Serializable
data class EnumDefinition(
val type: String,
val enum: Set<String>
) : JsonSchema

View File

@ -2,18 +2,17 @@ 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 io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
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 EnumHandler {
fun handle(type: KType, clazz: KClass<*>): JsonSchema {
fun handle(type: KType, clazz: KClass<*>, cache: MutableMap<String, JsonSchema>): JsonSchema {
cache[type.getSimpleSlug()] = ReferenceDefinition(type.getReferenceSlug())
val options = clazz.java.enumConstants.map { it.toString() }.toSet()
val definition = EnumDefinition(enum = options)
return when (type.isMarkedNullable) {
true -> OneOfDefinition(NullableDefinition(), definition)
false -> definition
}
return EnumDefinition(type = "string", enum = options)
}
}

View File

@ -2,6 +2,7 @@ package io.bkbn.kompendium.json.schema.handler
import io.bkbn.kompendium.json.schema.SchemaConfigurator
import io.bkbn.kompendium.json.schema.SchemaGenerator
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
@ -71,16 +72,11 @@ object SimpleObjectHandler {
.map { schemaConfigurator.serializableName(it) }
.toSet()
val definition = TypeDefinition(
return TypeDefinition(
type = "object",
properties = props,
required = required
)
return when (type.isMarkedNullable) {
true -> OneOfDefinition(NullableDefinition(), definition)
false -> definition
}
}
private fun KProperty<*>.needsToInjectGenerics(
@ -103,7 +99,7 @@ object SimpleObjectHandler {
}
val constructedType = propClass.createType(types)
return SchemaGenerator.fromTypeToSchema(constructedType, cache, schemaConfigurator).let {
if (it.isOrContainsObjectDef()) {
if (it.isOrContainsObjectOrEnumDef()) {
cache[constructedType.getSimpleSlug()] = it
ReferenceDefinition(prop.returnType.getReferenceSlug())
} else {
@ -121,7 +117,7 @@ object SimpleObjectHandler {
val type = typeMap[prop.returnType.classifier]?.type
?: error("This indicates a bug in Kompendium, please open a GitHub issue")
return SchemaGenerator.fromTypeToSchema(type, cache, schemaConfigurator).let {
if (it.isOrContainsObjectDef()) {
if (it.isOrContainsObjectOrEnumDef()) {
cache[type.getSimpleSlug()] = it
ReferenceDefinition(type.getReferenceSlug())
} else {
@ -136,7 +132,7 @@ object SimpleObjectHandler {
schemaConfigurator: SchemaConfigurator
): JsonSchema =
SchemaGenerator.fromTypeToSchema(prop.returnType, cache, schemaConfigurator).let {
if (it.isOrContainsObjectDef()) {
if (it.isOrContainsObjectOrEnumDef()) {
cache[prop.returnType.getSimpleSlug()] = it
ReferenceDefinition(prop.returnType.getReferenceSlug())
} else {
@ -144,10 +140,12 @@ object SimpleObjectHandler {
}
}
private fun JsonSchema.isOrContainsObjectDef(): Boolean {
private fun JsonSchema.isOrContainsObjectOrEnumDef(): Boolean {
val isTypeDef = this is TypeDefinition && type == "object"
val isTypeDefOneOf = this is OneOfDefinition && this.oneOf.any { js -> js is TypeDefinition && js.type == "object" }
return isTypeDef || isTypeDefOneOf
val isEnumDef = this is EnumDefinition
val isEnumDefOneOf = this is OneOfDefinition && this.oneOf.any { js -> js is EnumDefinition }
return isTypeDef || isTypeDefOneOf || isEnumDef || isEnumDefOneOf
}
private fun JsonSchema.isNullable(): Boolean = this is OneOfDefinition && this.oneOf.any { it is NullableDefinition }

View File

@ -2,6 +2,7 @@ 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.ObjectWithEnum
import io.bkbn.kompendium.core.fixtures.SerialNameObject
import io.bkbn.kompendium.core.fixtures.SimpleEnum
import io.bkbn.kompendium.core.fixtures.SlammaJamma
@ -9,7 +10,7 @@ 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.core.fixtures.TransientObject
import io.bkbn.kompendium.core.fixtures.UnbakcedObject
import io.bkbn.kompendium.core.fixtures.UnbackedObject
import io.bkbn.kompendium.json.schema.definition.JsonSchema
import io.kotest.assertions.json.shouldEqualJson
import io.kotest.assertions.throwables.shouldThrow
@ -40,6 +41,7 @@ class SchemaGeneratorTest : DescribeSpec({
jsonSchemaTest<ComplexRequest>("T0005__complex_object.json")
}
it("Can generate the schema for a nullable object") {
// Same schema as a non-nullable type, since the nullability will be handled on the property
jsonSchemaTest<TestSimpleRequest?>("T0006__nullable_object.json")
}
it("Can generate the schema for a polymorphic object") {
@ -52,7 +54,7 @@ class SchemaGeneratorTest : DescribeSpec({
jsonSchemaTest<TransientObject>("T0018__transient_object.json")
}
it("Can generate the schema for object with unbacked property") {
jsonSchemaTest<UnbakcedObject>("T0019__unbacked_object.json")
jsonSchemaTest<UnbackedObject>("T0019__unbacked_object.json")
}
it("Can generate the schema for object with SerialName annotation") {
jsonSchemaTest<SerialNameObject>("T0020__serial_name_object.json")
@ -63,8 +65,12 @@ class SchemaGeneratorTest : DescribeSpec({
jsonSchemaTest<SimpleEnum>("T0007__simple_enum.json")
}
it("Can generate the schema for a nullable enum") {
// Same schema as a non-nullable enum, since the nullability will be handled on the property
jsonSchemaTest<SimpleEnum?>("T0008__nullable_enum.json")
}
it("Can generate the schema for an object with an enum property") {
jsonSchemaTest<ObjectWithEnum>("T0021__object_with_enum.json")
}
}
describe("Arrays") {
it("Can generate the schema for an array of scalars") {

View File

@ -1,8 +1,3 @@
{
"oneOf": [
{
"type": "null"
},
{
"type": "object",
"properties": {
@ -19,5 +14,3 @@
"b"
]
}
]
}

View File

@ -1,3 +1,4 @@
{
"enum": [ "ONE", "TWO" ]
"enum": [ "ONE", "TWO" ],
"type": "string"
}

View File

@ -1,13 +1,4 @@
{
"oneOf": [
{
"type": "null"
},
{
"enum": [
"ONE",
"TWO"
]
}
]
"enum": [ "ONE", "TWO" ],
"type": "string"
}

View File

@ -0,0 +1,11 @@
{
"type": "object",
"properties": {
"color": {
"$ref": "#/components/schemas/Color"
}
},
"required": [
"color"
]
}

View File

@ -22,8 +22,8 @@ dependencies {
// IMPLEMENTATION
implementation(projects.kompendiumCore)
implementation("io.ktor:ktor-server-core:2.1.2")
implementation("io.ktor:ktor-server-locations:2.1.2")
implementation("io.ktor:ktor-server-core:2.1.3")
implementation("io.ktor:ktor-server-locations:2.1.3")
// TESTING

View File

@ -43,5 +43,5 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
implementation("joda-time:joda-time:2.12.0")
implementation("joda-time:joda-time:2.12.1")
}

View File

@ -22,8 +22,8 @@ dependencies {
// IMPLEMENTATION
implementation(projects.kompendiumCore)
implementation("io.ktor:ktor-server-core:2.1.2")
implementation("io.ktor:ktor-server-resources:2.1.2")
implementation("io.ktor:ktor-server-core:2.1.3")
implementation("io.ktor:ktor-server-resources:2.1.3")
// TESTING