From 000a32210ee7e33e6597d298176a0458b75e17ac Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Fri, 20 Jan 2023 18:38:45 +0100 Subject: [PATCH] fix: correctly generate json-schema for maps --- build.gradle.kts | 2 +- .../ktorswaggerui/SwaggerUIPluginConfig.kt | 1 + .../JsonToOpenApiSchemaConverter.kt | 2 + .../ktorswaggerui/tests/AssertionUtils.kt | 4 +- .../ktorswaggerui/tests/BuilderUtils.kt | 1 - .../tests/JsonSchemaGenerationTests.kt | 37 +++++++++++++++++++ 6 files changed, 44 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d07e2e7..7ff0baf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "io.github.smiley4" -version = "1.0.1" +version = "1.0.2" repositories { mavenCentral() diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt index ec7e041..d30a532 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt @@ -156,5 +156,6 @@ class SwaggerUIPluginConfig { .with(Option.INLINE_ALL_SCHEMAS) .with(Option.EXTRA_OPEN_API_FORMAT_VALUES) .with(Option.ALLOF_CLEANUP_AT_THE_END) + .with(Option.MAP_VALUES_AS_ADDITIONAL_PROPERTIES) } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/JsonToOpenApiSchemaConverter.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/JsonToOpenApiSchemaConverter.kt index aa30478..0d2dd47 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/JsonToOpenApiSchemaConverter.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/JsonToOpenApiSchemaConverter.kt @@ -13,8 +13,10 @@ class JsonToOpenApiSchemaConverter { return Schema().apply { node["\$schema"]?.let { this.`$schema` = it.asText() } node["type"]?.let { this.type = it.asText() } + node["format"]?.let { this.format = it.asText() } node["items"]?.let { this.items = toSchema(it) } node["properties"]?.let { this.properties = it.collectFields().associate { prop -> prop.key to toSchema(prop.value) } } + node["additionalProperties"]?.let { this.additionalProperties = toSchema(it) } node["allOf"]?.let { this.allOf = it.collectElements().map { prop -> toSchema(prop) } } node["anyOf"]?.let { this.anyOf = it.collectElements().map { prop -> toSchema(prop) } } node["required"]?.let { this.required = it.collectElements().map { prop -> prop.asText() } } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/AssertionUtils.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/AssertionUtils.kt index 9828a07..36fb281 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/AssertionUtils.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/AssertionUtils.kt @@ -269,7 +269,9 @@ infix fun Schema<*>.shouldBeSchema(expected: Schema<*>?) { this.properties[key]!! shouldBeSchema expected.properties[key] } } - this.additionalProperties shouldBe expected.additionalProperties + assertNullSafe(this.additionalProperties, expected.additionalProperties) { + (this.additionalProperties as Schema) shouldBeSchema (expected.additionalProperties as Schema) + } this.description shouldBe expected.description this.`$ref` shouldBe expected.`$ref` this.nullable shouldBe expected.nullable diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/BuilderUtils.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/BuilderUtils.kt index dd9e647..5e1b33f 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/BuilderUtils.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/BuilderUtils.kt @@ -1,6 +1,5 @@ package io.github.smiley4.ktorswaggerui.tests -import io.github.smiley4.ktorswaggerui.specbuilder.ApiSpecBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiComponentsBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiContentBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiExampleBuilder diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/JsonSchemaGenerationTests.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/JsonSchemaGenerationTests.kt index 74dede7..3ddc606 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/JsonSchemaGenerationTests.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/JsonSchemaGenerationTests.kt @@ -3,6 +3,7 @@ package io.github.smiley4.ktorswaggerui.tests import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.core.type.TypeReference +import com.github.victools.jsonschema.generator.SchemaGenerator import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.specbuilder.ComponentsContext import io.kotest.core.spec.style.StringSpec @@ -17,6 +18,27 @@ class JsonSchemaGenerationTests : StringSpec({ } } + "generate schema for maps" { + getOApiSchemaBuilder().build(DataClassWithMaps::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { + type = "object" + properties = mapOf( + "mapStringValues" to Schema().apply { + type = "object" + additionalProperties = Schema().apply { + type = "string" + } + }, + "mapLongValues" to Schema().apply { + type = "object" + additionalProperties = Schema().apply { + type = "integer" + format = "int64" + } + }, + ) + } + } + "generate schema for a list of simple classes" { getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" @@ -28,6 +50,7 @@ class JsonSchemaGenerationTests : StringSpec({ }, "value" to Schema().apply { type = "number" + format = "float" } ) } @@ -43,6 +66,7 @@ class JsonSchemaGenerationTests : StringSpec({ }, "value" to Schema().apply { type = "number" + format = "float" } ) } @@ -54,11 +78,13 @@ class JsonSchemaGenerationTests : StringSpec({ properties = mapOf( "primitiveValue" to Schema().apply { type = "integer" + format = "int32" }, "primitiveList" to Schema().apply { type = "array" items = Schema().apply { type = "integer" + format = "int32" } }, "nestedClass" to Schema().apply { @@ -69,6 +95,7 @@ class JsonSchemaGenerationTests : StringSpec({ }, "value" to Schema().apply { type = "number" + format = "float" } ) }, @@ -82,6 +109,7 @@ class JsonSchemaGenerationTests : StringSpec({ }, "value" to Schema().apply { type = "number" + format = "float" } ) } @@ -99,6 +127,7 @@ class JsonSchemaGenerationTests : StringSpec({ }, "subFieldA" to Schema().apply { type = "integer" + format = "int32" }, "_type" to Schema().apply { setConst("io.github.smiley4.ktorswaggerui.tests.JsonSchemaGenerationTests\$Companion\$SubClassA") @@ -119,6 +148,7 @@ class JsonSchemaGenerationTests : StringSpec({ }, "subFieldA" to Schema().apply { type = "integer" + format = "int32" }, "_type" to Schema().apply { setConst("io.github.smiley4.ktorswaggerui.tests.JsonSchemaGenerationTests\$Companion\$SubClassA") @@ -187,6 +217,7 @@ class JsonSchemaGenerationTests : StringSpec({ }, "value" to Schema().apply { type = "number" + format = "float" } ) }, @@ -200,6 +231,7 @@ class JsonSchemaGenerationTests : StringSpec({ }, "value" to Schema().apply { type = "number" + format = "float" } ) } @@ -222,6 +254,11 @@ class JsonSchemaGenerationTests : StringSpec({ val value: Float ) + data class DataClassWithMaps( + val mapStringValues: Map, + val mapLongValues: Map + ) + data class AnotherDataClass( val primitiveValue: Int, val primitiveList: List,