diff --git a/CHANGELOG.md b/CHANGELOG.md index 9908c26d3..70acc1d2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Changed - Can now put redoc route behind authentication - Fixed issue where type erasure was breaking nested generics +- Fix bug with null references not displaying properly on properties ### Remove diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumTest.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumTest.kt index 67de2b46d..84b8a3531 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumTest.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumTest.kt @@ -29,6 +29,7 @@ import io.bkbn.kompendium.core.util.TestModules.notarizedPut import io.bkbn.kompendium.core.util.TestModules.nullableEnumField import io.bkbn.kompendium.core.util.TestModules.nullableField import io.bkbn.kompendium.core.util.TestModules.nullableNestedObject +import io.bkbn.kompendium.core.util.TestModules.nullableReference import io.bkbn.kompendium.core.util.TestModules.polymorphicCollectionResponse import io.bkbn.kompendium.core.util.TestModules.polymorphicMapResponse import io.bkbn.kompendium.core.util.TestModules.polymorphicResponse @@ -191,6 +192,9 @@ class KompendiumTest : DescribeSpec({ it("Can have a nullable enum as a member field") { openApiTestAllSerializers("T0037__nullable_enum_field.json") { nullableEnumField() } } + it("Can have a nullable reference without impacting base type") { + openApiTestAllSerializers("T0041__nullable_reference.json") { nullableReference() } + } } describe("Constraints") { // TODO Assess strategies here diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/TestModules.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/TestModules.kt index e6c44f41b..324bd6cee 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/TestModules.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/TestModules.kt @@ -8,6 +8,7 @@ import io.bkbn.kompendium.core.fixtures.ExceptionResponse import io.bkbn.kompendium.core.fixtures.Flibbity import io.bkbn.kompendium.core.fixtures.FlibbityGibbit import io.bkbn.kompendium.core.fixtures.Gibbity +import io.bkbn.kompendium.core.fixtures.ManyThings import io.bkbn.kompendium.core.fixtures.MultiNestedGenerics import io.bkbn.kompendium.core.fixtures.NullableEnum import io.bkbn.kompendium.core.fixtures.NullableField @@ -579,6 +580,8 @@ object TestModules { fun Routing.nullableEnumField() = basicGetGenerator() + fun Routing.nullableReference() = basicGetGenerator() + fun Routing.dateTimeString() = basicGetGenerator() fun Routing.headerParameter() = basicGetGenerator( diff --git a/core/src/test/resources/T0041__nullable_reference.json b/core/src/test/resources/T0041__nullable_reference.json new file mode 100644 index 000000000..1c797c702 --- /dev/null +++ b/core/src/test/resources/T0041__nullable_reference.json @@ -0,0 +1,98 @@ +{ + "openapi": "3.1.0", + "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema", + "info": { + "title": "Test API", + "version": "1.33.7", + "description": "An amazing, fully-ish 😉 generated API spec", + "termsOfService": "https://example.com", + "contact": { + "name": "Homer Simpson", + "url": "https://gph.is/1NPUDiM", + "email": "chunkylover53@aol.com" + }, + "license": { + "name": "MIT", + "url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE" + } + }, + "servers": [ + { + "url": "https://myawesomeapi.com", + "description": "Production instance of my API" + }, + { + "url": "https://staging.myawesomeapi.com", + "description": "Where the fun stuff happens" + } + ], + "paths": { + "/": { + "get": { + "tags": [], + "summary": "Great Summary!", + "description": "testing more", + "parameters": [], + "responses": { + "200": { + "description": "A Successful Endeavor", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ManyThings" + } + } + } + } + }, + "deprecated": false + }, + "parameters": [] + } + }, + "webhooks": {}, + "components": { + "schemas": { + "Something": { + "type": "object", + "properties": { + "a": { + "type": "string" + }, + "b": { + "type": "number", + "format": "int32" + } + }, + "required": [ + "a", + "b" + ] + }, + "ManyThings": { + "type": "object", + "properties": { + "someA": { + "$ref": "#/components/schemas/Something" + }, + "someB": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/Something" + } + ] + } + }, + "required": [ + "someA" + ] + } + }, + "securitySchemes": {} + }, + "security": [], + "tags": [] +} diff --git a/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestModels.kt b/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestModels.kt index 4e8ce5d1d..5e506ba4b 100644 --- a/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestModels.kt +++ b/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestModels.kt @@ -131,3 +131,10 @@ data class Page( data class MultiNestedGenerics( val content: Map ) + +data class Something(val a: String, val b: Int) + +data class ManyThings( + val someA: Something, + val someB: Something? +) diff --git a/json-schema/src/main/kotlin/io/bkbn/kompendium/json/schema/handler/SimpleObjectHandler.kt b/json-schema/src/main/kotlin/io/bkbn/kompendium/json/schema/handler/SimpleObjectHandler.kt index 64f9fc287..d9d162c05 100644 --- a/json-schema/src/main/kotlin/io/bkbn/kompendium/json/schema/handler/SimpleObjectHandler.kt +++ b/json-schema/src/main/kotlin/io/bkbn/kompendium/json/schema/handler/SimpleObjectHandler.kt @@ -31,7 +31,13 @@ object SimpleObjectHandler { } } - prop.name to schema + // TODO This is kinda hacky 👀 And might break in certain edge cases? + val nullCheckSchema = when (prop.returnType.isMarkedNullable && schema !is OneOfDefinition) { + true -> OneOfDefinition(NullableDefinition(), schema) + false -> schema + } + + prop.name to nullCheckSchema } val required = clazz.memberProperties.filterNot { prop -> prop.returnType.isMarkedNullable }