Skip to content

Commit 79bb843

Browse files
committed
[Bugfix] Adjust $ref handling logic in DefaultJsonSchemaFactory to avoid replacing nested type name if it shares the prefix of the root class name
When you have a type that shares the name of it's parent type (eg: Task & TaskType), with the way the current `fixRefsPrefix` works (recurses through all subtypes to handle nested types) you end up replacing the def path of the subtype and it becomes unserialisable and crashes at runtime, and you have to use custom serialisers or rename your class. This checks that the root definition is followed by a / (to indicate nesting) before replacing it).
1 parent ec4e244 commit 79bb843

2 files changed

Lines changed: 70 additions & 8 deletions

File tree

sdk-serde-kotlinx/src/main/kotlin/dev/restate/serde/kotlinx/DefaultJsonSchemaFactory.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,10 @@ import io.github.smiley4.schemakenerator.jsonschema.JsonSchemaSteps.generateJson
1515
import io.github.smiley4.schemakenerator.jsonschema.TitleBuilder
1616
import io.github.smiley4.schemakenerator.jsonschema.data.IntermediateJsonSchemaData
1717
import io.github.smiley4.schemakenerator.jsonschema.data.RefType
18-
import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonArray
19-
import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonNode
20-
import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonObject
21-
import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonTextValue
22-
import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.array
18+
import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.*
2319
import io.github.smiley4.schemakenerator.serialization.SerializationSteps.analyzeTypeUsingKotlinxSerialization
2420
import io.github.smiley4.schemakenerator.serialization.SerializationSteps.initial
2521
import io.github.smiley4.schemakenerator.serialization.SerializationSteps.renameMembers
26-
import kotlin.collections.set
2722
import kotlinx.serialization.ExperimentalSerializationApi
2823
import kotlinx.serialization.KSerializer
2924
import kotlinx.serialization.json.Json
@@ -126,8 +121,10 @@ object DefaultJsonSchemaFactory : KotlinSerializationSerdeFactory.JsonSchemaFact
126121
private fun JsonObject.fixRefsPrefix(rootDefinition: String) {
127122
this.properties.computeIfPresent("\$ref") { key, node ->
128123
if (node is JsonTextValue) {
129-
if (node.value.startsWith(rootDefinition)) {
130-
JsonTextValue("#/" + node.value.removePrefix(rootDefinition))
124+
if (node.value == rootDefinition) {
125+
JsonTextValue("#/")
126+
} else if (node.value.startsWith("$rootDefinition/")) {
127+
JsonTextValue("#/" + node.value.removePrefix("$rootDefinition/"))
131128
} else {
132129
JsonTextValue("#/\$defs/" + node.value.removePrefix("#/definitions/"))
133130
}

sdk-serde-kotlinx/src/test/kotlin/dev/restate/serde/kotlinx/KotlinxSerdeTest.kt

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,71 @@ class KotlinxSerdeTest {
176176
)
177177
}
178178

179+
@Serializable
180+
enum class TaskStatus {
181+
TODO,
182+
IN_PROGRESS,
183+
DONE,
184+
}
185+
186+
@Serializable
187+
enum class PriorityOrder {
188+
HIGH,
189+
MID,
190+
LOW,
191+
}
192+
193+
@Serializable
194+
data class Task(val title: String, val status: TaskStatus, val priority: PriorityOrder)
195+
196+
@Test
197+
fun schemaGenWithExternalEnum() {
198+
testSchemaGen<Task>(
199+
$$"""
200+
{
201+
"type": "object",
202+
"required": [
203+
"title",
204+
"status",
205+
"priority"
206+
],
207+
"properties": {
208+
"title": {
209+
"type": "string"
210+
},
211+
"priority": {
212+
"$ref": "#/$defs/PriorityOrder"
213+
},
214+
"status": {
215+
"$ref": "#/$defs/TaskStatus"
216+
}
217+
},
218+
"title": "Task",
219+
"$schema": "https://json-schema.org/draft/2020-12/schema",
220+
"$defs": {
221+
"TaskStatus": {
222+
"enum": [
223+
"TODO",
224+
"IN_PROGRESS",
225+
"DONE"
226+
],
227+
"title": "TaskStatus"
228+
},
229+
"PriorityOrder": {
230+
"enum": [
231+
"HIGH",
232+
"MID",
233+
"LOW"
234+
],
235+
"title": "PriorityOrder"
236+
}
237+
}
238+
}
239+
"""
240+
.trimIndent()
241+
)
242+
}
243+
179244
inline fun <reified T : Any?> testSchemaGen(expectedSchema: String) {
180245
val expectedJsonElement = Json.decodeFromString<JsonElement>(expectedSchema)
181246
val actualSchema =

0 commit comments

Comments
 (0)