Repository: bkbnio/kompendium (kotlin)

Issue:
Nullability is broken by move to reference schemas 
**Describe the bug**
The switch to reference schemas completely broken Kompendiums ability to handle null fields.  Since each type gets stored only once, but nullability is stored on the object schema level, this leads to a situation where defining one field of Type A that _is_ nullable and another field of type A that _is not_ nullable cannot be handled. 

**To Reproduce**
```kotlin
data class Something(val a: String, val b: Int)

data class ManyThings(
  val someA: Something,
  val someB: Something?
)
```

This will not display the nullability correctly in Kompendium

**Expected behavior**
It should display properly

Pull request:
# Description

Fixes bug causing null references on properties to not display properly 

Closes #201 

## Type of change

Please delete options that are not relevant.

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation 
- [ ] Chore

# How Has This Been Tested?

Added test

# Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation
- [x] I have updated the CHANGELOG in the `Unreleased` section
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged and published in downstream modules

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9908c26d38..70acc1d2d2 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/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 64f9fc287c..d9d162c054 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 }


Test patch:
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 67de2b46d7..84b8a3531c 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 e6c44f41bd..324bd6cee3 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<NullableEnum>()
 
+  fun Routing.nullableReference() = basicGetGenerator<ManyThings>()
+
   fun Routing.dateTimeString() = basicGetGenerator<DateTimeString>()
 
   fun Routing.headerParameter() = basicGetGenerator<TestResponse>(
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 0000000000..1c797c702a
--- /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 4e8ce5d1da..5e506ba4bc 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<T>(
 data class MultiNestedGenerics<T, E>(
   val content: Map<T, E>
 )
+
+data class Something(val a: String, val b: Int)
+
+data class ManyThings(
+  val someA: Something,
+  val someB: Something?
+)