diff --git a/gradle.properties b/gradle.properties index 20a960e..8a1f7d7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ kotlin.code.style=official # project id projectGroupId=io.github.smiley4 projectArtifactIdBase=schema-kenerator -projectVersion=1.0.0 +projectVersion=1.0.1 # publishing information projectNameBase=Schema-Kenerator diff --git a/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps.kt b/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps.kt index e67d3f2..a504233 100644 --- a/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps.kt +++ b/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps.kt @@ -8,7 +8,9 @@ import io.github.smiley4.schemakenerator.jsonschema.data.RefType import io.github.smiley4.schemakenerator.jsonschema.data.TitleType import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaAnnotationTypeHintStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaAutoTitleStep -import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileInlineStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileReferenceRootStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileReferenceStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationDefaultStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationDeprecatedStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationDescriptionStep @@ -56,24 +58,24 @@ fun Bundle.handleJsonSchemaAnnotations(): Bundle { /** - * See [JsonSchemaCompileStep] + * See [JsonSchemaCompileInlineStep] */ fun Bundle.compileInlining(): CompiledJsonSchema { - return JsonSchemaCompileStep().compileInlining(this) + return JsonSchemaCompileInlineStep().compile(this) } /** - * See [JsonSchemaCompileStep] + * See [JsonSchemaCompileReferenceStep] */ fun Bundle.compileReferencing(pathType: RefType = RefType.FULL): CompiledJsonSchema { - return JsonSchemaCompileStep(pathType).compileReferencing(this) + return JsonSchemaCompileReferenceStep(pathType).compile(this) } /** - * See [JsonSchemaCompileStep] + * See [JsonSchemaCompileReferenceRootStep] */ fun Bundle.compileReferencingRoot(pathType: RefType = RefType.FULL): CompiledJsonSchema { - return JsonSchemaCompileStep(pathType).compileReferencingRoot(this) + return JsonSchemaCompileReferenceRootStep(pathType).compile(this) } diff --git a/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileInlineStep.kt b/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileInlineStep.kt new file mode 100644 index 0000000..ba69e10 --- /dev/null +++ b/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileInlineStep.kt @@ -0,0 +1,49 @@ +package io.github.smiley4.schemakenerator.jsonschema.steps + +import io.github.smiley4.schemakenerator.core.data.Bundle +import io.github.smiley4.schemakenerator.core.data.TypeId +import io.github.smiley4.schemakenerator.core.data.flatten +import io.github.smiley4.schemakenerator.jsonschema.data.CompiledJsonSchema +import io.github.smiley4.schemakenerator.jsonschema.data.JsonSchema +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonObject +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonTextValue +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileUtils.resolveReferences + +/** + * Resolves references in prepared json-schemas by inlining them. + */ +class JsonSchemaCompileInlineStep { + + /** + * Inline all referenced schema + */ + fun compile(bundle: Bundle): CompiledJsonSchema { + val schemaList = bundle.flatten() + val root = resolveReferences(bundle.data.json) { refObj -> + val referencedId = TypeId.parse((refObj.properties["\$ref"] as JsonTextValue).value) + val referencedSchema = schemaList.find(referencedId) + if (referencedSchema != null) { + referencedSchema.json.also { + if (it is JsonObject) { + it.properties.putAll(buildMap { + this.putAll(refObj.properties) + this.remove("\$ref") + }) + } + } + } else { + refObj + } + } + return CompiledJsonSchema( + json = root, + typeData = bundle.data.typeData, + definitions = emptyMap() + ) + } + + private fun Collection.find(id: TypeId): JsonSchema? { + return this.find { it.typeData.id == id } + } + +} diff --git a/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileReferenceRootStep.kt b/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileReferenceRootStep.kt new file mode 100644 index 0000000..ebc01e5 --- /dev/null +++ b/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileReferenceRootStep.kt @@ -0,0 +1,43 @@ +package io.github.smiley4.schemakenerator.jsonschema.steps + +import io.github.smiley4.schemakenerator.core.data.Bundle +import io.github.smiley4.schemakenerator.jsonschema.data.CompiledJsonSchema +import io.github.smiley4.schemakenerator.jsonschema.data.JsonSchema +import io.github.smiley4.schemakenerator.jsonschema.data.RefType +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileUtils.getRefPath +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileUtils.shouldReference + +/** + * Resolves references in prepared json-schemas by collecting them in the definitions-section and referencing them. + * @param pathType how to reference the type, i.e. which name to use + */ +class JsonSchemaCompileReferenceRootStep(private val pathType: RefType = RefType.FULL) { + + private val schemaUtils = JsonSchemaUtils() + + + /** + * Put referenced schemas into definitions and reference them + */ + fun compile(bundle: Bundle): CompiledJsonSchema { + val result = JsonSchemaCompileReferenceStep(pathType).compile(bundle) + if (shouldReference(result.json)) { + val refPath = getRefPath(pathType, result.typeData, bundle.buildTypeDataMap()) + return CompiledJsonSchema( + typeData = result.typeData, + json = schemaUtils.referenceSchema(refPath, true), + definitions = buildMap { + this.putAll(result.definitions) + this[refPath] = result.json + } + ) + } else { + return CompiledJsonSchema( + typeData = result.typeData, + json = result.json, + definitions = result.definitions + ) + } + } + +} diff --git a/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileReferenceStep.kt b/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileReferenceStep.kt new file mode 100644 index 0000000..5a0622d --- /dev/null +++ b/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileReferenceStep.kt @@ -0,0 +1,82 @@ +package io.github.smiley4.schemakenerator.jsonschema.steps + +import io.github.smiley4.schemakenerator.core.data.BaseTypeData +import io.github.smiley4.schemakenerator.core.data.Bundle +import io.github.smiley4.schemakenerator.core.data.TypeId +import io.github.smiley4.schemakenerator.core.data.flatten +import io.github.smiley4.schemakenerator.jsonschema.data.CompiledJsonSchema +import io.github.smiley4.schemakenerator.jsonschema.data.JsonSchema +import io.github.smiley4.schemakenerator.jsonschema.data.RefType +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonNode +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonObject +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonTextValue +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.obj +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileUtils.getRefPath +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileUtils.resolveReferences +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileUtils.shouldReference + +/** + * Resolves references in prepared json-schemas by collecting them in the definitions-section and referencing them. + * @param pathType how to reference the type, i.e. which name to use + */ +class JsonSchemaCompileReferenceStep(private val pathType: RefType = RefType.FULL) { + + private val schemaUtils = JsonSchemaUtils() + + /** + * Put referenced schemas into definitions and reference them + */ + fun compile(bundle: Bundle): CompiledJsonSchema { + val schemaList = bundle.flatten() + val typeDataMap = bundle.buildTypeDataMap() + val definitions = mutableMapOf() + + val root = resolveReferences(bundle.data.json) { refObj -> + resolve(refObj, schemaList, typeDataMap, definitions) + } + + return CompiledJsonSchema( + typeData = bundle.data.typeData, + json = root, + definitions = definitions + ) + } + + private fun resolve( + refObj: JsonObject, + schemaList: List, + typeDataMap: Map, + definitions: MutableMap + ): JsonNode { + val referencedId = TypeId.parse((refObj.properties["\$ref"] as JsonTextValue).value) + val referencedSchema = schemaList.find(referencedId) + return if (referencedSchema != null) { + if (shouldReference(referencedSchema.json)) { + val refPath = getRefPath(pathType, referencedSchema.typeData, typeDataMap) + if(!definitions.containsKey(refPath)) { + definitions[refPath] = placeholder() // break out of infinite loops + definitions[refPath] = resolveReferences(referencedSchema.json) { resolve(it, schemaList, typeDataMap, definitions)} + } + schemaUtils.referenceSchema(refPath, true) + } else { + referencedSchema.json.also { + if (it is JsonObject) { + it.properties.putAll(buildMap { + this.putAll(refObj.properties) + this.remove("\$ref") + }) + } + } + } + } else { + refObj + } + } + + private fun placeholder() = obj { } + + private fun Collection.find(id: TypeId): JsonSchema? { + return this.find { it.typeData.id == id } + } + +} diff --git a/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileStep.kt b/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileStep.kt deleted file mode 100644 index 4310aaa..0000000 --- a/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileStep.kt +++ /dev/null @@ -1,194 +0,0 @@ -package io.github.smiley4.schemakenerator.jsonschema.steps - -import io.github.smiley4.schemakenerator.core.data.BaseTypeData -import io.github.smiley4.schemakenerator.core.data.Bundle -import io.github.smiley4.schemakenerator.core.data.TypeId -import io.github.smiley4.schemakenerator.core.data.WildcardTypeData -import io.github.smiley4.schemakenerator.jsonschema.data.CompiledJsonSchema -import io.github.smiley4.schemakenerator.jsonschema.data.JsonSchema -import io.github.smiley4.schemakenerator.jsonschema.data.RefType -import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonArray -import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonNode -import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonObject -import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonTextValue -import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonValue -import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.array -import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.obj - -/** - * Resolves references in prepared json-schemas - either inlining them or collecting them in the definitions-section and referencing them. - * @param pathType how to reference the type, i.e. which name to use - */ -class JsonSchemaCompileStep(private val pathType: RefType = RefType.FULL) { - - private val schemaUtils = JsonSchemaUtils() - - - /** - * Inline all referenced schema - */ - fun compileInlining(bundle: Bundle): CompiledJsonSchema { - return CompiledJsonSchema( - json = inlineReferences(bundle.data.json, bundle.supporting), - typeData = bundle.data.typeData, - definitions = emptyMap() - ) - } - - private fun inlineReferences(node: JsonNode, schemaList: Collection): JsonNode { - return replaceReferences(node) { refObj -> - val referencedId = TypeId.parse((refObj.properties["\$ref"] as JsonTextValue).value) - val referencedSchema = schemaList.find { it.typeData.id == referencedId }!! - inlineReferences(referencedSchema.json, schemaList).also { - if (it is JsonObject) { - it.properties.putAll(buildMap { - this.putAll(refObj.properties) - this.remove("\$ref") - }) - } - } - } - } - - - /** - * Put referenced schemas into definitions and reference them - */ - fun compileReferencing(bundle: Bundle): CompiledJsonSchema { - val result = referenceDefinitionsReferences(bundle, bundle.data.json, bundle.supporting) - return CompiledJsonSchema( - typeData = bundle.data.typeData, - json = result.json, - definitions = result.definitions - ) - } - - - /** - * Put referenced schemas and root-schema into definitions and reference them - */ - fun compileReferencingRoot(bundle: Bundle): CompiledJsonSchema { - val result = compileReferencing(bundle) - return if (shouldReference(bundle.data.json)) { - CompiledJsonSchema( - typeData = result.typeData, - json = schemaUtils.referenceSchema(getRefPath(result.typeData, bundle.buildTypeDataMap()), true), - definitions = buildMap { - this.putAll(result.definitions) - this[getRefPath(result.typeData, bundle.buildTypeDataMap())] = result.json - } - ) - } else { - result - } - } - - private fun referenceDefinitionsReferences( - bundle: Bundle, - node: JsonNode, - schemaList: Collection - ): CompiledJsonSchema { - val definitions = mutableMapOf() - val json = replaceReferences(node) { refObj -> - val referencedId = TypeId.parse((refObj.properties["\$ref"] as JsonTextValue).value) - val referencedSchema = schemaList.find { it.typeData.id == referencedId }!! - val procReferencedSchema = referenceDefinitionsReferences(bundle, referencedSchema.json, schemaList).also { - if (it.json is JsonObject) { - it.json.properties.putAll(buildMap { - this.putAll(refObj.properties) - this.remove("\$ref") - }) - } - } - if (shouldReference(referencedSchema.json)) { - definitions[getRefPath(referencedSchema.typeData, bundle.buildTypeDataMap())] = procReferencedSchema.json - definitions.putAll(procReferencedSchema.definitions) - schemaUtils.referenceSchema(getRefPath(referencedSchema.typeData, bundle.buildTypeDataMap()), true) - } else { - definitions.putAll(procReferencedSchema.definitions) - procReferencedSchema.json - } - } - return CompiledJsonSchema( - typeData = WildcardTypeData(), - json = json, - definitions = definitions - ) - } - - private fun shouldReference(json: JsonNode): Boolean { - val complexProperties = setOf( - "required", - "properties" - ) - return if (json is JsonObject) { - (getTextProperty(json, "type") == "object" && json.properties.keys.any { complexProperties.contains(it) } && !existsProperty( - json, - "additionalProperties" - )) - || existsProperty(json, "enum") - || existsProperty(json, "anyOf") - } else { - false - } - - } - - private fun getTextProperty(node: JsonNode, key: String): String? { - if (node is JsonObject) { - val type = node.properties[key] - if (type is JsonTextValue) { - return type.value - } - } - return null - } - - private fun existsProperty(node: JsonNode, key: String): Boolean { - if (node is JsonObject) { - return node.properties[key] != null - } - return false - } - - private fun replaceReferences(node: JsonNode, mapping: (refObj: JsonObject) -> JsonNode): JsonNode { - if (node is JsonObject && node.properties.containsKey("\$ref")) { - return mapping(node) - } else { - return when (node) { - is JsonArray -> array { - node.items - .map { replaceReferences(it, mapping) } - .forEach { item(it) } - } - is JsonObject -> { - obj { - node.properties.forEach { (key, value) -> - key to replaceReferences(value, mapping) - } - } - } - is JsonValue<*> -> node - } - } - } - - private fun getRefPath(typeData: BaseTypeData, typeDataMap: Map): String { - return when (pathType) { - RefType.FULL -> typeData.qualifiedName - RefType.SIMPLE -> typeData.simpleName - }.let { - if (typeData.typeParameters.isNotEmpty()) { - val paramString = typeData.typeParameters - .map { (_, param) -> getRefPath(typeDataMap[param.type]!!, typeDataMap) } - .joinToString(",") - "$it<$paramString>" - } else { - it - } - }.let { - it + (typeData.id.additionalId?.let { a -> "#$a" } ?: "") - } - } - -} diff --git a/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileUtils.kt b/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileUtils.kt new file mode 100644 index 0000000..7945881 --- /dev/null +++ b/schema-kenerator-jsonschema/src/main/kotlin/io/github/smiley4/schemakenerator/jsonschema/steps/JsonSchemaCompileUtils.kt @@ -0,0 +1,100 @@ +package io.github.smiley4.schemakenerator.jsonschema.steps + +import io.github.smiley4.schemakenerator.core.data.BaseTypeData +import io.github.smiley4.schemakenerator.core.data.TypeId +import io.github.smiley4.schemakenerator.jsonschema.data.RefType +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonArray +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonNode +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonObject +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonTextValue +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonValue +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.array +import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.obj + +object JsonSchemaCompileUtils { + + private const val MAX_RESOLVE_REFS_DEPTH = 64; + + fun resolveReferences(node: JsonNode, depth: Int = 0, resolver: (refObj: JsonObject) -> JsonNode): JsonNode { + if (depth > MAX_RESOLVE_REFS_DEPTH) { + return obj { } + } + if (node is JsonObject && node.properties.containsKey("\$ref")) { + val resolved = resolver(node) + return if (resolved == node) { + resolved + } else { + resolveReferences(resolved, depth + 1, resolver) + } + } else { + return when (node) { + is JsonArray -> array { + node.items + .map { resolveReferences(it, depth + 1, resolver) } + .forEach { item(it) } + } + is JsonObject -> { + obj { + node.properties.forEach { (key, value) -> + key to resolveReferences(value, depth + 1, resolver) + } + } + } + is JsonValue<*> -> node + } + } + } + + fun shouldReference(json: JsonNode): Boolean { + val complexProperties = setOf( + "required", + "properties" + ) + return if (json is JsonObject) { + val isComplexObject = getTextProperty(json, "type") == "object" + && json.properties.keys.any { complexProperties.contains(it) } + && !existsProperty(json, "additionalProperties") + isComplexObject || existsProperty(json, "enum") || existsProperty(json, "anyOf") + } else { + false + } + + } + + fun getTextProperty(node: JsonNode, key: String): String? { + if (node is JsonObject) { + val type = node.properties[key] + if (type is JsonTextValue) { + return type.value + } + } + return null + } + + fun existsProperty(node: JsonNode, key: String): Boolean { + if (node is JsonObject) { + return node.properties[key] != null + } + return false + } + + + fun getRefPath(pathType: RefType, typeData: BaseTypeData, typeDataMap: Map): String { + return when (pathType) { + RefType.FULL -> typeData.qualifiedName + RefType.SIMPLE -> typeData.simpleName + }.let { + if (typeData.typeParameters.isNotEmpty()) { + val paramString = typeData.typeParameters + .map { (_, param) -> getRefPath(pathType, typeDataMap[param.type]!!, typeDataMap) } + .joinToString(",") + "$it<$paramString>" + } else { + it + } + }.let { + it + (typeData.id.additionalId?.let { a -> "#$a" } ?: "") + } + } + +} diff --git a/schema-kenerator-serialization/src/main/kotlin/io/github/smiley4/schemakenerator/serialization/steps/KotlinxSerializationTypeProcessingStep.kt b/schema-kenerator-serialization/src/main/kotlin/io/github/smiley4/schemakenerator/serialization/steps/KotlinxSerializationTypeProcessingStep.kt index 49aa1c2..574a6ee 100644 --- a/schema-kenerator-serialization/src/main/kotlin/io/github/smiley4/schemakenerator/serialization/steps/KotlinxSerializationTypeProcessingStep.kt +++ b/schema-kenerator-serialization/src/main/kotlin/io/github/smiley4/schemakenerator/serialization/steps/KotlinxSerializationTypeProcessingStep.kt @@ -8,6 +8,7 @@ import io.github.smiley4.schemakenerator.core.data.CollectionTypeData import io.github.smiley4.schemakenerator.core.data.EnumTypeData import io.github.smiley4.schemakenerator.core.data.MapTypeData import io.github.smiley4.schemakenerator.core.data.ObjectTypeData +import io.github.smiley4.schemakenerator.core.data.PlaceholderTypeData import io.github.smiley4.schemakenerator.core.data.PrimitiveTypeData import io.github.smiley4.schemakenerator.core.data.PropertyData import io.github.smiley4.schemakenerator.core.data.PropertyType @@ -60,7 +61,7 @@ class KotlinxSerializationTypeProcessingStep( private fun process(type: KType, typeData: MutableList): BaseTypeData { if (type.classifier is KClass<*>) { return (type.classifier as KClass<*>).serializerOrNull() - ?.let { parse(it.descriptor, typeData) } + ?.let { parse(it.descriptor, typeData, mutableMapOf()) } ?: parseWildcard(typeData) } else { throw IllegalArgumentException("Type is not a class.") @@ -69,19 +70,35 @@ class KotlinxSerializationTypeProcessingStep( @Suppress("CyclomaticComplexMethod") - private fun parse(descriptor: SerialDescriptor, typeData: MutableList): BaseTypeData { + private fun parse( + descriptor: SerialDescriptor, + typeData: MutableList, + processed: MutableMap + ): BaseTypeData { + + // break out of infinite loops + if (processed.containsKey(descriptor)) { + return processed[descriptor]!! + } + processed[descriptor] = PlaceholderTypeData(TypeId.wildcard()) // check redirects - if(typeRedirects.containsKey(descriptor.cleanSerialName())) { - return process(typeRedirects[descriptor.cleanSerialName()]!!, typeData) + if (typeRedirects.containsKey(descriptor.cleanSerialName())) { + return process(typeRedirects[descriptor.cleanSerialName()]!!, typeData).also { + processed[descriptor] = it + } } // check custom processors if (customProcessors.containsKey(descriptor.cleanSerialName())) { - return customProcessors[descriptor.cleanSerialName()]!!.invoke().also { result -> - typeData.removeIf { it.id == result.id } - typeData.add(result) - } + return customProcessors[descriptor.cleanSerialName()]!!.invoke() + .also { result -> + typeData.removeIf { it.id == result.id } + typeData.add(result) + } + .also { + processed[descriptor] = it + } } // process @@ -92,12 +109,12 @@ class KotlinxSerializationTypeProcessingStep( UInt::class.qualifiedName -> parsePrimitive(descriptor, typeData) ULong::class.qualifiedName -> parsePrimitive(descriptor, typeData) else -> when (descriptor.kind) { - StructureKind.LIST -> parseList(descriptor, typeData) - StructureKind.MAP -> parseMap(descriptor, typeData) - StructureKind.CLASS -> parseClass(descriptor, typeData) + StructureKind.LIST -> parseList(descriptor, typeData, processed) + StructureKind.MAP -> parseMap(descriptor, typeData, processed) + StructureKind.CLASS -> parseClass(descriptor, typeData, processed) StructureKind.OBJECT -> parseObject(descriptor, typeData) - PolymorphicKind.OPEN -> parseSealed(descriptor, typeData) - PolymorphicKind.SEALED -> parseSealed(descriptor, typeData) + PolymorphicKind.OPEN -> parseSealed(descriptor, typeData, processed) + PolymorphicKind.SEALED -> parseSealed(descriptor, typeData, processed) PrimitiveKind.BOOLEAN -> parsePrimitive(descriptor, typeData) PrimitiveKind.BYTE -> parsePrimitive(descriptor, typeData) PrimitiveKind.CHAR -> parsePrimitive(descriptor, typeData) @@ -108,8 +125,10 @@ class KotlinxSerializationTypeProcessingStep( PrimitiveKind.SHORT -> parsePrimitive(descriptor, typeData) PrimitiveKind.STRING -> parsePrimitive(descriptor, typeData) SerialKind.ENUM -> parseEnum(descriptor, typeData) - SerialKind.CONTEXTUAL -> parseClass(descriptor, typeData) + SerialKind.CONTEXTUAL -> parseClass(descriptor, typeData, processed) } + }.also { + processed[descriptor] = it } } @@ -131,10 +150,14 @@ class KotlinxSerializationTypeProcessingStep( } } - private fun parseList(descriptor: SerialDescriptor, typeData: MutableList): BaseTypeData { + private fun parseList( + descriptor: SerialDescriptor, + typeData: MutableList, + processed: MutableMap + ): BaseTypeData { val itemDescriptor = descriptor.getElementDescriptor(0) val itemName = descriptor.getElementName(0) - val itemType = parse(itemDescriptor, typeData) + val itemType = parse(itemDescriptor, typeData, processed) val id = TypeId.build(descriptor.cleanSerialName(), listOf(itemType.id)) return typeData.find(id) ?: CollectionTypeData( @@ -162,14 +185,18 @@ class KotlinxSerializationTypeProcessingStep( } } - private fun parseMap(descriptor: SerialDescriptor, typeData: MutableList): BaseTypeData { + private fun parseMap( + descriptor: SerialDescriptor, + typeData: MutableList, + processed: MutableMap + ): BaseTypeData { val keyDescriptor = descriptor.getElementDescriptor(0) val keyName = descriptor.getElementName(0) - val keyType = parse(keyDescriptor, typeData) + val keyType = parse(keyDescriptor, typeData, processed) val valueDescriptor = descriptor.getElementDescriptor(1) val valueName = descriptor.getElementName(1) - val valueType = parse(valueDescriptor, typeData) + val valueType = parse(valueDescriptor, typeData, processed) val id = TypeId.build(descriptor.cleanSerialName(), listOf(keyType.id, valueType.id)) return typeData.find(id) @@ -210,9 +237,12 @@ class KotlinxSerializationTypeProcessingStep( } } - private fun parseClass(descriptor: SerialDescriptor, typeData: MutableList): BaseTypeData { - val id = - getUniqueId(descriptor, emptyList(), typeData) // generate unique for each object since generic types cannot be respected in id + private fun parseClass( + descriptor: SerialDescriptor, + typeData: MutableList, + processed: MutableMap + ): BaseTypeData { + val id = getUniqueId(descriptor, emptyList(), typeData) // unique for each object since generic types cannot be respected in id return typeData.find(id) ?: ObjectTypeData( id = id, @@ -222,7 +252,7 @@ class KotlinxSerializationTypeProcessingStep( for (i in 0..): BaseTypeData { - val id = - getUniqueId(descriptor, emptyList(), typeData) // generate unique for each object since generic types cannot be respected in id + private fun parseSealed( + descriptor: SerialDescriptor, + typeData: MutableList, + processed: MutableMap + ): BaseTypeData { + val id = getUniqueId(descriptor, emptyList(), typeData) // unique for each object since generic types cannot be respected in id return typeData.find(id) ?: ObjectTypeData( id = id, @@ -250,7 +283,7 @@ class KotlinxSerializationTypeProcessingStep( qualifiedName = descriptor.qualifiedName(), subtypes = descriptor.elementDescriptors .toList()[1].elementDescriptors - .map { parse(it, typeData).id } + .map { parse(it, typeData, processed).id } .toMutableList(), ).also { result -> typeData.removeIf { it.id == result.id } diff --git a/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps.kt b/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps.kt index 56b339f..9a23172 100644 --- a/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps.kt +++ b/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps.kt @@ -10,7 +10,9 @@ import io.github.smiley4.schemakenerator.swagger.steps.SwaggerArraySchemaAnnotat import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaAnnotationStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaAnnotationTypeHintStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaAutoTitleStep -import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileStep +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileInlineStep +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileReferenceRootStep +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileReferenceStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationDefaultStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationDeprecatedStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationDescriptionStep @@ -67,24 +69,24 @@ fun Bundle.handleSchemaAnnotations(): Bundle { /** - * See [SwaggerSchemaCompileStep] + * See [SwaggerSchemaCompileInlineStep] */ fun Bundle.compileInlining(): CompiledSwaggerSchema { - return SwaggerSchemaCompileStep().compileInlining(this) + return SwaggerSchemaCompileInlineStep().compile(this) } /** - * See [SwaggerSchemaCompileStep] + * See [SwaggerSchemaCompileReferenceStep] */ fun Bundle.compileReferencing(pathType: RefType = RefType.FULL): CompiledSwaggerSchema { - return SwaggerSchemaCompileStep(pathType).compileReferencing(this) + return SwaggerSchemaCompileReferenceStep(pathType).compile(this) } /** - * See [SwaggerSchemaCompileStep] + * See [SwaggerSchemaCompileReferenceRootStep] */ fun Bundle.compileReferencingRoot(pathType: RefType = RefType.FULL): CompiledSwaggerSchema { - return SwaggerSchemaCompileStep(pathType).compileReferencingRoot(this) + return SwaggerSchemaCompileReferenceRootStep(pathType).compile(this) } diff --git a/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileInlineStep.kt b/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileInlineStep.kt new file mode 100644 index 0000000..afa14f1 --- /dev/null +++ b/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileInlineStep.kt @@ -0,0 +1,41 @@ +package io.github.smiley4.schemakenerator.swagger.steps + +import io.github.smiley4.schemakenerator.core.data.Bundle +import io.github.smiley4.schemakenerator.core.data.TypeId +import io.github.smiley4.schemakenerator.core.data.flatten +import io.github.smiley4.schemakenerator.swagger.data.CompiledSwaggerSchema +import io.github.smiley4.schemakenerator.swagger.data.SwaggerSchema +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileUtils.merge +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileUtils.resolveReferences + +/** + * Resolves references in prepared swagger-schemas by inlining them. + */ +class SwaggerSchemaCompileInlineStep { + + /** + * Inline all referenced schema + */ + fun compile(bundle: Bundle): CompiledSwaggerSchema { + val schemaList = bundle.flatten() + val root = resolveReferences(bundle.data.swagger) { refObj -> + val referencedId = TypeId.parse(refObj.`$ref`) + val referencedSchema = schemaList.find(referencedId) + if(referencedSchema != null) { + merge(refObj, referencedSchema.swagger) + } else { + refObj + } + } + return CompiledSwaggerSchema( + swagger = root, + typeData = bundle.data.typeData, + componentSchemas = emptyMap() + ) + } + + private fun Collection.find(id: TypeId): SwaggerSchema? { + return this.find { it.typeData.id == id } + } + +} diff --git a/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileReferenceRootStep.kt b/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileReferenceRootStep.kt new file mode 100644 index 0000000..2a875c9 --- /dev/null +++ b/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileReferenceRootStep.kt @@ -0,0 +1,45 @@ +package io.github.smiley4.schemakenerator.swagger.steps + +import io.github.smiley4.schemakenerator.core.data.Bundle +import io.github.smiley4.schemakenerator.swagger.data.CompiledSwaggerSchema +import io.github.smiley4.schemakenerator.swagger.data.RefType +import io.github.smiley4.schemakenerator.swagger.data.SwaggerSchema +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileUtils.getRefPath +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileUtils.shouldReference + +/** + * Resolves references in prepared swagger-schemas by collecting them in the components-section and referencing them. + * @param pathType how to reference the type, i.e. which name to use + */ +class SwaggerSchemaCompileReferenceRootStep(private val pathType: RefType = RefType.FULL) { + + private val schemaUtils = SwaggerSchemaUtils() + + + /** + * Put referenced schemas into definitions and reference them + */ + fun compile(bundle: Bundle): CompiledSwaggerSchema { + val result = SwaggerSchemaCompileReferenceStep(pathType).compile(bundle) + if (shouldReference(result.swagger)) { + val refPath = getRefPath(pathType, result.typeData, bundle.buildTypeDataMap()) + return CompiledSwaggerSchema( + typeData = result.typeData, + swagger = schemaUtils.referenceSchema(refPath, true), + componentSchemas = buildMap { + this.putAll(result.componentSchemas) + this[refPath] = result.swagger + } + ) + } else { + return CompiledSwaggerSchema( + typeData = result.typeData, + swagger = result.swagger, + componentSchemas = result.componentSchemas + ) + } + + } + + +} diff --git a/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileReferenceStep.kt b/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileReferenceStep.kt new file mode 100644 index 0000000..9f101ef --- /dev/null +++ b/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileReferenceStep.kt @@ -0,0 +1,74 @@ +package io.github.smiley4.schemakenerator.swagger.steps + +import io.github.smiley4.schemakenerator.core.data.BaseTypeData +import io.github.smiley4.schemakenerator.core.data.Bundle +import io.github.smiley4.schemakenerator.core.data.TypeId +import io.github.smiley4.schemakenerator.core.data.flatten +import io.github.smiley4.schemakenerator.swagger.data.CompiledSwaggerSchema +import io.github.smiley4.schemakenerator.swagger.data.RefType +import io.github.smiley4.schemakenerator.swagger.data.SwaggerSchema +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileUtils.getRefPath +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileUtils.merge +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileUtils.resolveReferences +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileUtils.shouldReference +import io.swagger.v3.oas.models.media.Schema + +/** + * Resolves references in prepared swagger-schemas by collecting them in the components-section and referencing them. + * @param pathType how to reference the type, i.e. which name to use + */ +class SwaggerSchemaCompileReferenceStep(private val pathType: RefType = RefType.FULL) { + + private val schemaUtils = SwaggerSchemaUtils() + + + /** + * Put referenced schemas into definitions and reference them + */ + fun compile(bundle: Bundle): CompiledSwaggerSchema { + val schemaList = bundle.flatten() + val typeDataMap = bundle.buildTypeDataMap() + val components = mutableMapOf>() + + val root = resolveReferences(bundle.data.swagger) { refObj -> + resolve(refObj, schemaList, typeDataMap, components) + } + + return CompiledSwaggerSchema( + typeData = bundle.data.typeData, + swagger = root, + componentSchemas = components + ) + } + + private fun resolve( + refObj: Schema<*>, + schemaList: List, + typeDataMap: Map, + components: MutableMap> + ): Schema<*> { + val referencedId = TypeId.parse(refObj.`$ref`) + val referencedSchema = schemaList.find(referencedId) + return if (referencedSchema != null) { + if (shouldReference(referencedSchema.swagger)) { + val refPath = getRefPath(pathType, referencedSchema.typeData, typeDataMap) + if(!components.containsKey(refPath)) { + components[refPath] = placeholder() // break out of infinite loops + components[refPath] = resolveReferences(referencedSchema.swagger) { resolve(it, schemaList, typeDataMap, components)} + } + schemaUtils.referenceSchema(refPath, true) + } else { + merge(refObj, referencedSchema.swagger) + } + } else { + refObj + } + } + + private fun placeholder() = Schema() + + private fun Collection.find(id: TypeId): SwaggerSchema? { + return this.find { it.typeData.id == id } + } + +} diff --git a/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileStep.kt b/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileStep.kt deleted file mode 100644 index 5caec74..0000000 --- a/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileStep.kt +++ /dev/null @@ -1,190 +0,0 @@ -package io.github.smiley4.schemakenerator.swagger.steps - -import io.github.smiley4.schemakenerator.core.data.BaseTypeData -import io.github.smiley4.schemakenerator.core.data.Bundle -import io.github.smiley4.schemakenerator.core.data.TypeId -import io.github.smiley4.schemakenerator.core.data.WildcardTypeData -import io.github.smiley4.schemakenerator.swagger.data.CompiledSwaggerSchema -import io.github.smiley4.schemakenerator.swagger.data.RefType -import io.github.smiley4.schemakenerator.swagger.data.SwaggerSchema -import io.swagger.v3.oas.models.media.Schema - -/** - * Resolves references in prepared swagger-schemas - either inlining them or collecting them in the components-section and referencing them. - * @param pathType how to reference the type, i.e. which name to use - */ -class SwaggerSchemaCompileStep(private val pathType: RefType = RefType.FULL) { - - private val schemaUtils = SwaggerSchemaUtils() - - - /** - * Inline all referenced schema - */ - fun compileInlining(bundle: Bundle): CompiledSwaggerSchema { - return CompiledSwaggerSchema( - swagger = inlineReferences(bundle.data.swagger, bundle.supporting), - typeData = bundle.data.typeData, - componentSchemas = emptyMap() - ) - } - - private fun inlineReferences(node: Schema<*>, schemaList: Collection): Schema<*> { - return replaceReferences(node) { refObj -> - val referencedId = TypeId.parse(refObj.`$ref`) - val referencedSchema = schemaList.find { it.typeData.id == referencedId }!! - inlineReferences(referencedSchema.swagger, schemaList).also { - mergeInto(refObj, it) - } - } - } - - - /** - * Put referenced schemas into definitions and reference them - */ - fun compileReferencing(bundle: Bundle): CompiledSwaggerSchema { - val result = referenceDefinitionsReferences(bundle, bundle.data.swagger, bundle.supporting) - return CompiledSwaggerSchema( - typeData = bundle.data.typeData, - swagger = result.swagger, - componentSchemas = result.componentSchemas - ) - } - - - /** - * Put referenced schemas and root-schema into definitions and reference them - */ - fun compileReferencingRoot(bundle: Bundle): CompiledSwaggerSchema { - val result = compileReferencing(bundle) - return if (shouldReference(bundle.data.swagger)) { - CompiledSwaggerSchema( - typeData = result.typeData, - swagger = schemaUtils.referenceSchema(getRefPath(result.typeData, bundle.buildTypeDataMap()), true), - componentSchemas = buildMap { - this.putAll(result.componentSchemas) - this[getRefPath(result.typeData, bundle.buildTypeDataMap())] = result.swagger - } - ) - } else { - result - } - } - - - private fun referenceDefinitionsReferences( - bundle: Bundle, - node: Schema<*>, - schemaList: Collection - ): CompiledSwaggerSchema { - val definitions = mutableMapOf>() - val json = replaceReferences(node) { refObj -> - val referencedId = TypeId.parse(refObj.`$ref`.replace("#/components/schemas/", "")) - val referencedSchema = schemaList.find { it.typeData.id == referencedId }!! - val procReferencedSchema = referenceDefinitionsReferences(bundle, referencedSchema.swagger, schemaList).also { - mergeInto(refObj, it.swagger) - } - if (shouldReference(referencedSchema.swagger)) { - definitions[getRefPath(referencedSchema.typeData, bundle.buildTypeDataMap())] = procReferencedSchema.swagger - definitions.putAll(procReferencedSchema.componentSchemas) - schemaUtils.referenceSchema(getRefPath(referencedSchema.typeData, bundle.buildTypeDataMap()), true) - } else { - definitions.putAll(procReferencedSchema.componentSchemas) - procReferencedSchema.swagger - } - } - return CompiledSwaggerSchema( - typeData = WildcardTypeData(), - swagger = json, - componentSchemas = definitions - ) - } - - - @Suppress("CyclomaticComplexMethod") - private fun mergeInto(other: Schema<*>, dst: Schema<*>) { - other.default?.also { dst.setDefault(it) } - other.title?.also { dst.title = it } - other.maximum?.also { dst.maximum = it } - other.exclusiveMaximum?.also { dst.exclusiveMaximum = it } - other.minimum?.also { dst.minimum = it } - other.exclusiveMinimum?.also { dst.exclusiveMinimum = it } - other.maxLength?.also { dst.maxLength = it } - other.minLength?.also { dst.minLength = it } - other.pattern?.also { dst.pattern = it } - other.maxItems?.also { dst.maxItems = it } - other.minItems?.also { dst.minItems = it } - other.enum?.also { - @Suppress("TYPE_MISMATCH_WARNING") - dst.enum = it - } - other.const?.also { dst.setConst(it) } - other.uniqueItems?.also { dst.uniqueItems = it } - other.maxProperties?.also { dst.maxProperties = it } - other.minProperties?.also { dst.minProperties = it } - other.required?.also { dst.required = it } - other.type?.also { dst.type = it } - other.not?.also { dst.not = it } - other.description?.also { dst.description = it } - other.format?.also { dst.format = it } - other.nullable?.also { dst.nullable = it } - other.readOnly?.also { dst.readOnly = it } - other.writeOnly?.also { dst.writeOnly = it } - other.example?.also { dst.example = it } - other.externalDocs?.also { dst.externalDocs = it } - other.deprecated?.also { dst.deprecated = it } - other.specVersion?.also { dst.specVersion = it } - other.exclusiveMaximumValue?.also { dst.exclusiveMaximumValue = it } - other.exclusiveMinimumValue?.also { dst.exclusiveMinimumValue = it } - other.contains?.also { dst.contains = it } - other.maxContains?.also { dst.maxContains = it } - other.minContains?.also { dst.minContains = it } - other.examples?.also { - @Suppress("UNCHECKED_CAST") - (dst as Schema).examples = it - } - } - - private fun shouldReference(schema: Schema<*>): Boolean { - return (schema.type == "object" && schema.properties != null) - || schema.enum != null - || schema.anyOf != null - - } - - private fun replaceReferences(node: Schema<*>, mapping: (refObj: Schema<*>) -> Schema<*>): Schema<*> { - if (node.`$ref` != null) { - return mapping(node) - } else { - return node.also { n -> - n.items = n.items?.let { replaceReferences(it, mapping) } - n.properties = n.properties?.let { it.mapValues { e -> replaceReferences(e.value, mapping) } } - n.additionalItems = n.additionalItems?.let { replaceReferences(it, mapping) } - n.additionalProperties = n.additionalProperties?.let { replaceReferences(it as Schema<*>, mapping) } - n.allOf = n.allOf?.let { it.map { e -> replaceReferences(e, mapping) } } - n.anyOf = n.anyOf?.let { it.map { e -> replaceReferences(e, mapping) } } - n.oneOf = n.oneOf?.let { it.map { e -> replaceReferences(e, mapping) } } - } - } - } - - private fun getRefPath(typeData: BaseTypeData, typeDataMap: Map): String { - return when (pathType) { - RefType.FULL -> typeData.qualifiedName - RefType.SIMPLE -> typeData.simpleName - }.let { - if (typeData.typeParameters.isNotEmpty()) { - val paramString = typeData.typeParameters - .map { (_, param) -> getRefPath(typeDataMap[param.type]!!, typeDataMap) } - .joinToString(",") - "$it<$paramString>" - } else { - it - } - }.let { - it + (typeData.id.additionalId?.let { a -> "#$a" } ?: "") - } - } - -} diff --git a/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileUtils.kt b/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileUtils.kt new file mode 100644 index 0000000..4f5354c --- /dev/null +++ b/schema-kenerator-swagger/src/main/kotlin/io/github/smiley4/schemakenerator/swagger/steps/SwaggerSchemaCompileUtils.kt @@ -0,0 +1,193 @@ +package io.github.smiley4.schemakenerator.swagger.steps + +import io.github.smiley4.schemakenerator.core.data.BaseTypeData +import io.github.smiley4.schemakenerator.core.data.TypeId +import io.github.smiley4.schemakenerator.swagger.data.RefType +import io.swagger.v3.oas.models.media.Schema + +object SwaggerSchemaCompileUtils { + + /** + * Whether the given schema should be referenced or inlined + */ + fun shouldReference(schema: Schema<*>): Boolean { + return (schema.type == "object" && schema.properties != null) + || schema.enum != null + || schema.anyOf != null + + } + + + /** + * Create a name or path as key into the schema components for the given type + */ + fun getRefPath(pathType: RefType, typeData: BaseTypeData, typeDataMap: Map): String { + return when (pathType) { + RefType.FULL -> typeData.qualifiedName + RefType.SIMPLE -> typeData.simpleName + }.let { + if (typeData.typeParameters.isNotEmpty()) { + val paramString = typeData.typeParameters + .map { (_, param) -> getRefPath(pathType, typeDataMap[param.type]!!, typeDataMap) } + .joinToString(",") + "$it<$paramString>" + } else { + it + } + }.let { + it + (typeData.id.additionalId?.let { a -> "#$a" } ?: "") + } + } + + private const val MAX_RESOLVE_REFS_DEPTH = 64; + + + /** + * Iterates over the schema and calls the given resolver-function for all objects with a set reference. + * The object is replaced with the object returned by the function and checked for further nested references. + */ + fun resolveReferences(node: Schema<*>, depth: Int = 0, resolver: (refObj: Schema<*>) -> Schema<*>): Schema<*> { + if (depth > MAX_RESOLVE_REFS_DEPTH) { + return Schema() + } + if (node.`$ref` != null) { + val resolved = resolver(node) + return if(resolved == node) { + resolved + } else { + resolveReferences(resolved, depth + 1, resolver) + } + } else { + node.items = node.items?.let { resolveReferences(it, depth + 1, resolver) } + node.properties = node.properties?.let { it.mapValues { prop -> resolveReferences(prop.value, depth + 1, resolver) } } + node.additionalItems = node.additionalItems?.let { resolveReferences(it, depth + 1, resolver) } + node.additionalProperties = node.additionalProperties?.let { resolveReferences(it as Schema<*>, depth + 1, resolver) } + node.allOf = node.allOf?.let { it.map { e -> resolveReferences(e, depth + 1, resolver) } } + node.anyOf = node.anyOf?.let { it.map { e -> resolveReferences(e, depth + 1, resolver) } } + node.oneOf = node.oneOf?.let { it.map { e -> resolveReferences(e, depth + 1, resolver) } } + return node + } + } + + + /** + * Merges the present properties of "source" with the properties of "target" and returns the result as a new schema. + */ + fun merge(source: Schema<*>, target: Schema<*>): Schema<*> { + return copy(target).also { mergeInto(source, it) } + } + + + /** + * Merges the given schemas by modifying the given target schema. Copies all present values from the source to the target. + */ + @Suppress("CyclomaticComplexMethod") + fun mergeInto(source: Schema<*>, target: Schema<*>) { + source.additionalProperties?.also { target.additionalProperties = it } + source.allOf?.also { target.allOf = it } + source.anyOf?.also { target.anyOf = it } + source.const?.also { target.setConst(it) } + source.contains?.also { target.contains = it } + source.default?.also { target.setDefault(it) } + source.deprecated?.also { target.deprecated = it } + source.description?.also { target.description = it } + source.discriminator?.also { target.discriminator = it } + source.enum?.also { + @Suppress("TYPE_MISMATCH_WARNING") + target.enum = it + } + source.example?.also { target.example = it } + source.examples?.also { + @Suppress("UNCHECKED_CAST") + (target as Schema).examples = it + } + source.exclusiveMaximum?.also { target.exclusiveMaximum = it } + source.exclusiveMaximumValue?.also { target.exclusiveMaximumValue = it } + source.exclusiveMinimum?.also { target.exclusiveMinimum = it } + source.exclusiveMinimumValue?.also { target.exclusiveMinimumValue = it } + source.externalDocs?.also { target.externalDocs = it } + source.format?.also { target.format = it } + source.items?.also { target.items = it } + source.maxContains?.also { target.maxContains = it } + source.maxItems?.also { target.maxItems = it } + source.maxLength?.also { target.maxLength = it } + source.maxProperties?.also { target.maxProperties = it } + source.maximum?.also { target.maximum = it } + source.minContains?.also { target.minContains = it } + source.minItems?.also { target.minItems = it } + source.minLength?.also { target.minLength = it } + source.minProperties?.also { target.minProperties = it } + source.minimum?.also { target.minimum = it } + source.multipleOf?.also { target.multipleOf = it } + source.name?.also { target.name = it } + source.not?.also { target.not = it } + source.nullable?.also { target.nullable = it } + source.oneOf?.also { target.oneOf = it } + source.pattern?.also { target.pattern = it } + source.properties?.also { target.properties.putAll(it) } + source.readOnly?.also { target.readOnly = it } + source.required?.also { target.required = it } + source.specVersion?.also { target.specVersion = it } + source.title?.also { target.title = it } + source.type?.also { target.type = it } + source.uniqueItems?.also { target.uniqueItems = it } + source.writeOnly?.also { target.writeOnly = it } + source.xml?.also { target.xml = it } + } + + + /** + * Creates a shallow copy of the given schema. + */ + @Suppress("CyclomaticComplexMethod") + private fun copy(source: Schema<*>): Schema<*> { + return Schema().also { copy -> + copy.additionalProperties = source.additionalProperties + copy.allOf = source.allOf + copy.anyOf = source.anyOf + copy.contains = source.contains + copy.deprecated = source.deprecated + copy.description = source.description + copy.discriminator = source.discriminator + copy.enum = source.enum + copy.example = source.example + copy.exampleSetFlag = source.exampleSetFlag + copy.examples = source.examples + copy.exclusiveMaximum = source.exclusiveMaximum + copy.exclusiveMaximumValue = source.exclusiveMaximumValue + copy.exclusiveMinimum = source.exclusiveMinimum + copy.exclusiveMinimumValue = source.exclusiveMinimumValue + copy.externalDocs = source.externalDocs + copy.format = source.format + copy.items = source.items + copy.maxContains = source.maxContains + copy.maxItems = source.maxItems + copy.maxLength = source.maxLength + copy.maxProperties = source.maxProperties + copy.maximum = source.maximum + copy.minContains = source.minContains + copy.minItems = source.minItems + copy.minLength = source.minLength + copy.minProperties = source.minProperties + copy.minimum = source.minimum + copy.multipleOf = source.multipleOf + copy.name = source.name + copy.not = source.not + copy.nullable = source.nullable + copy.oneOf = source.oneOf + copy.pattern = source.pattern + copy.properties = source.properties + copy.readOnly = source.readOnly + copy.required = source.required + copy.setConst(source.const) + copy.setDefault(source.default) + copy.specVersion = source.specVersion + copy.title = source.title + copy.type = source.type + copy.uniqueItems = source.uniqueItems + copy.writeOnly = source.writeOnly + copy.xml = source.xml + } + } + +} diff --git a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/CustomLocalDateTimeTypeProcessorTest.kt b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/CustomLocalDateTimeTypeProcessorTest.kt index 213d75b..6dff8e9 100644 --- a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/CustomLocalDateTimeTypeProcessorTest.kt +++ b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/CustomLocalDateTimeTypeProcessorTest.kt @@ -9,14 +9,14 @@ import io.github.smiley4.schemakenerator.jsonschema.data.JsonTypeHint import io.github.smiley4.schemakenerator.jsonschema.data.TitleType import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaAnnotationTypeHintStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaAutoTitleStep -import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileInlineStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaGenerationStep import io.github.smiley4.schemakenerator.reflection.steps.ReflectionTypeProcessingStep import io.github.smiley4.schemakenerator.serialization.steps.KotlinxSerializationTypeProcessingStep import io.github.smiley4.schemakenerator.swagger.data.SwaggerTypeHint import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaAnnotationTypeHintStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaAutoTitleStep -import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileStep +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileInlineStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaGenerationStep import io.github.smiley4.schemakenerator.test.models.reflection.ClassWithLocalDateTime import io.kotest.assertions.json.ArrayOrder @@ -38,7 +38,7 @@ class CustomLocalDateTimeTypeProcessorTest : StringSpec({ .let { JsonSchemaGenerationStep().generate(it) } .let { JsonSchemaAnnotationTypeHintStep().process(it) } .let { JsonSchemaAutoTitleStep(TitleType.FULL).process(it) } - .let { JsonSchemaCompileStep().compileInlining(it) } + .let { JsonSchemaCompileInlineStep().compile(it) } result.json.prettyPrint().shouldEqualJson { propertyOrder = PropertyOrder.Lenient @@ -93,7 +93,7 @@ class CustomLocalDateTimeTypeProcessorTest : StringSpec({ .let { JsonSchemaGenerationStep().generate(it) } .let { JsonSchemaAnnotationTypeHintStep().process(it) } .let { JsonSchemaAutoTitleStep(TitleType.FULL).process(it) } - .let { JsonSchemaCompileStep().compileInlining(it) } + .let { JsonSchemaCompileInlineStep().compile(it) } result.json.prettyPrint().shouldEqualJson { propertyOrder = PropertyOrder.Lenient @@ -129,7 +129,7 @@ class CustomLocalDateTimeTypeProcessorTest : StringSpec({ .let { SwaggerSchemaGenerationStep().generate(it) } .let { SwaggerSchemaAnnotationTypeHintStep().process(it) } .let { SwaggerSchemaAutoTitleStep(io.github.smiley4.schemakenerator.swagger.data.TitleType.FULL).process(it) } - .let { SwaggerSchemaCompileStep().compileInlining(it) } + .let { SwaggerSchemaCompileInlineStep().compile(it) } json.writeValueAsString(result.swagger).shouldEqualJson { propertyOrder = PropertyOrder.Lenient @@ -185,7 +185,7 @@ class CustomLocalDateTimeTypeProcessorTest : StringSpec({ .let { SwaggerSchemaGenerationStep().generate(it) } .let { SwaggerSchemaAnnotationTypeHintStep().process(it) } .let { SwaggerSchemaAutoTitleStep(io.github.smiley4.schemakenerator.swagger.data.TitleType.FULL).process(it) } - .let { SwaggerSchemaCompileStep().compileInlining(it) } + .let { SwaggerSchemaCompileInlineStep().compile(it) } json.writeValueAsString(result.swagger).shouldEqualJson { propertyOrder = PropertyOrder.Lenient diff --git a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/KotlinxSerializationParser_JsonGenerator_Tests.kt b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/KotlinxSerializationParser_JsonGenerator_Tests.kt index 4b0b64b..c79336d 100644 --- a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/KotlinxSerializationParser_JsonGenerator_Tests.kt +++ b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/KotlinxSerializationParser_JsonGenerator_Tests.kt @@ -1,12 +1,15 @@ package io.github.smiley4.schemakenerator.test -import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileStep -import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaGenerationStep +import io.github.smiley4.schemakenerator.jsonschema.data.TitleType import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonObject import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.obj import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaAutoTitleStep -import io.github.smiley4.schemakenerator.jsonschema.data.TitleType +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileInlineStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileReferenceRootStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileReferenceStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaGenerationStep import io.github.smiley4.schemakenerator.serialization.steps.KotlinxSerializationTypeProcessingStep +import io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing import io.github.smiley4.schemakenerator.test.models.kotlinx.ClassWIthDifferentGenerics import io.github.smiley4.schemakenerator.test.models.kotlinx.ClassWithCollections import io.github.smiley4.schemakenerator.test.models.kotlinx.ClassWithDeepGeneric @@ -45,7 +48,7 @@ class KotlinxSerializationParser_JsonGenerator_Tests : FunSpec({ list } } - .let { JsonSchemaCompileStep().compileInlining(it) } + .let { JsonSchemaCompileInlineStep().compile(it) } schema.json.prettyPrint().shouldEqualJson { propertyOrder = PropertyOrder.Lenient @@ -85,7 +88,7 @@ class KotlinxSerializationParser_JsonGenerator_Tests : FunSpec({ list } } - .let { JsonSchemaCompileStep().compileReferencing(it) } + .let { JsonSchemaCompileReferenceStep().compile(it) } .also { if (it.definitions.isNotEmpty()) { (it.json as JsonObject).properties["definitions"] = obj { @@ -142,7 +145,7 @@ class KotlinxSerializationParser_JsonGenerator_Tests : FunSpec({ list } } - .let { JsonSchemaCompileStep().compileReferencingRoot(it) } + .let { JsonSchemaCompileReferenceRootStep().compile(it) } .also { if (it.definitions.isNotEmpty()) { (it.json as JsonObject).properties["definitions"] = obj { @@ -1147,6 +1150,183 @@ class KotlinxSerializationParser_JsonGenerator_Tests : FunSpec({ } """.trimIndent(), ), + TestData( + type = typeOf(), + testName = "class with direct self reference", + expectedResultInlining = """ + { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": {} + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + """.trimIndent(), + expectedResultReferencing = """ + { + "type": "object", + "required": [], + "properties": { + "self": { + "${'$'}ref": "#/definitions/io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing" + } + }, + "definitions": { + "io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing": { + "type": "object", + "required": [], + "properties": { + "self": { + "${'$'}ref": "#/definitions/io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing" + } + } + } + } + } + """.trimIndent(), + expectedResultReferencingRoot = """ + { + "${'$'}ref": "#/definitions/io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing", + "definitions": { + "io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing": { + "type": "object", + "required": [], + "properties": { + "self": { + "${'$'}ref": "#/definitions/io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing" + } + } + } + } + } + """.trimIndent(), + ), ) } diff --git a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/KotlinxSerializationParser_SwaggerGenerator_Tests.kt b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/KotlinxSerializationParser_SwaggerGenerator_Tests.kt index 50fde17..feff0ed 100644 --- a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/KotlinxSerializationParser_SwaggerGenerator_Tests.kt +++ b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/KotlinxSerializationParser_SwaggerGenerator_Tests.kt @@ -3,10 +3,13 @@ package io.github.smiley4.schemakenerator.test import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.github.smiley4.schemakenerator.serialization.steps.KotlinxSerializationTypeProcessingStep +import io.github.smiley4.schemakenerator.swagger.data.TitleType import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaAutoTitleStep -import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileStep +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileInlineStep +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileReferenceRootStep +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileReferenceStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaGenerationStep -import io.github.smiley4.schemakenerator.swagger.data.TitleType +import io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing import io.github.smiley4.schemakenerator.test.models.kotlinx.ClassWithCollections import io.github.smiley4.schemakenerator.test.models.kotlinx.ClassWithDeepGeneric import io.github.smiley4.schemakenerator.test.models.kotlinx.ClassWithGenericField @@ -45,7 +48,7 @@ class KotlinxSerializationParser_SwaggerGenerator_Tests : FunSpec({ list } } - .let { SwaggerSchemaCompileStep().compileInlining(it) } + .let { SwaggerSchemaCompileInlineStep().compile(it) } json.writeValueAsString(schema.swagger).shouldEqualJson { propertyOrder = PropertyOrder.Lenient @@ -72,7 +75,7 @@ class KotlinxSerializationParser_SwaggerGenerator_Tests : FunSpec({ list } } - .let { SwaggerSchemaCompileStep().compileReferencing(it) } + .let { SwaggerSchemaCompileReferenceStep().compile(it) } .let { Result( schema = it.swagger, @@ -105,7 +108,7 @@ class KotlinxSerializationParser_SwaggerGenerator_Tests : FunSpec({ list } } - .let { SwaggerSchemaCompileStep().compileReferencingRoot(it) } + .let { SwaggerSchemaCompileReferenceRootStep().compile(it) } .let { Result( schema = it.swagger, @@ -1271,6 +1274,259 @@ class KotlinxSerializationParser_SwaggerGenerator_Tests : FunSpec({ } """.trimIndent(), ), + TestData( + type = typeOf(), + testName = "class with direct self reference", + expectedResultInlining = """ + { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + """.trimIndent(), + expectedResultReferencing = """ + { + "schema": { + "type": "object", + "properties": { + "self": { + "${'$'}ref": "#/components/schemas/io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing", + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + }, + "definitions": { + "io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing": { + "type": "object", + "properties": { + "self": { + "${'$'}ref": "#/components/schemas/io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing", + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + } + } + """.trimIndent(), + expectedResultReferencingRoot = """ + { + "schema": { + "${'$'}ref": "#/components/schemas/io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing", + "exampleSetFlag": false + }, + "definitions": { + "io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing": { + "type": "object", + "properties": { + "self": { + "${'$'}ref": "#/components/schemas/io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing", + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + } + } + """.trimIndent(), + ), ) } diff --git a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/Kotlinx_JsonSchema_TitleAppender_Tests.kt b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/Kotlinx_JsonSchema_TitleAppender_Tests.kt index c67eb43..1d0597d 100644 --- a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/Kotlinx_JsonSchema_TitleAppender_Tests.kt +++ b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/Kotlinx_JsonSchema_TitleAppender_Tests.kt @@ -5,7 +5,8 @@ import io.github.smiley4.schemakenerator.jsonschema.data.TitleType import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonObject import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.obj import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaAutoTitleStep -import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileInlineStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileReferenceRootStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaGenerationStep import io.github.smiley4.schemakenerator.serialization.steps.KotlinxSerializationTypeProcessingStep import io.github.smiley4.schemakenerator.test.models.kotlinx.ClassWIthDifferentGenerics @@ -46,7 +47,7 @@ class Kotlinx_JsonSchema_TitleAppender_Tests : FunSpec({ } .let { JsonSchemaGenerationStep().generate(it) } .let { JsonSchemaAutoTitleStep(TitleType.FULL).process(it) } - .let { JsonSchemaCompileStep().compileInlining(it) } + .let { JsonSchemaCompileInlineStep().compile(it) } schema.json.prettyPrint() .let { @@ -86,7 +87,7 @@ class Kotlinx_JsonSchema_TitleAppender_Tests : FunSpec({ } .let { JsonSchemaGenerationStep().generate(it) } .let { JsonSchemaAutoTitleStep(TitleType.SIMPLE).process(it) } - .let { JsonSchemaCompileStep().compileInlining(it) } + .let { JsonSchemaCompileInlineStep().compile(it) } schema.json.prettyPrint() .let { @@ -126,7 +127,7 @@ class Kotlinx_JsonSchema_TitleAppender_Tests : FunSpec({ } .let { JsonSchemaGenerationStep().generate(it) } .let { JsonSchemaAutoTitleStep(TitleType.SIMPLE).process(it) } - .let { JsonSchemaCompileStep(RefType.SIMPLE).compileReferencingRoot(it) } + .let { JsonSchemaCompileReferenceRootStep(RefType.SIMPLE).compile(it) } .also { if (it.definitions.isNotEmpty()) { (it.json as JsonObject).properties["definitions"] = obj { diff --git a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_JsonGenerator_Tests.kt b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_JsonGenerator_Tests.kt index ef589a2..b57d85f 100644 --- a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_JsonGenerator_Tests.kt +++ b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_JsonGenerator_Tests.kt @@ -4,7 +4,9 @@ import io.github.smiley4.schemakenerator.jsonschema.data.TitleType import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonObject import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.obj import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaAutoTitleStep -import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileInlineStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileReferenceRootStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileReferenceStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationDefaultStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationDeprecatedStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationDescriptionStep @@ -12,6 +14,7 @@ import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotati import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationTitleStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaGenerationStep import io.github.smiley4.schemakenerator.reflection.steps.ReflectionTypeProcessingStep +import io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing import io.github.smiley4.schemakenerator.test.models.reflection.ClassWithCollections import io.github.smiley4.schemakenerator.test.models.reflection.ClassWithDeepGeneric import io.github.smiley4.schemakenerator.test.models.reflection.ClassWithGenericField @@ -62,7 +65,7 @@ class ReflectionParser_JsonGenerator_Tests : FunSpec({ list } } - .let { JsonSchemaCompileStep().compileInlining(it) } + .let { JsonSchemaCompileInlineStep().compile(it) } schema.json.prettyPrint().shouldEqualJson { propertyOrder = PropertyOrder.Lenient @@ -102,7 +105,7 @@ class ReflectionParser_JsonGenerator_Tests : FunSpec({ list } } - .let { JsonSchemaCompileStep().compileReferencing(it) } + .let { JsonSchemaCompileReferenceStep().compile(it) } .also { if (it.definitions.isNotEmpty()) { (it.json as JsonObject).properties["definitions"] = obj { @@ -151,7 +154,7 @@ class ReflectionParser_JsonGenerator_Tests : FunSpec({ list } } - .let { JsonSchemaCompileStep().compileReferencingRoot(it) } + .let { JsonSchemaCompileReferenceRootStep().compile(it) } .also { if (it.definitions.isNotEmpty()) { (it.json as JsonObject).properties["definitions"] = obj { @@ -1182,6 +1185,183 @@ class ReflectionParser_JsonGenerator_Tests : FunSpec({ } """.trimIndent(), ), + TestData( + type = typeOf(), + testName = "class with direct self reference", + expectedResultInlining = """ + { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": { + "type": "object", + "required": [], + "properties": { + "self": {} + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + """.trimIndent(), + expectedResultReferencing = """ + { + "type": "object", + "required": [], + "properties": { + "self": { + "${'$'}ref": "#/definitions/io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing" + } + }, + "definitions": { + "io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing": { + "type": "object", + "required": [], + "properties": { + "self": { + "${'$'}ref": "#/definitions/io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing" + } + } + } + } + } + """.trimIndent(), + expectedResultReferencingRoot = """ + { + "${'$'}ref": "#/definitions/io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing", + "definitions": { + "io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing": { + "type": "object", + "required": [], + "properties": { + "self": { + "${'$'}ref": "#/definitions/io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing" + } + } + } + } + } + """.trimIndent(), + ), ) } diff --git a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_PropertyFilterTests.kt b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_PropertyFilterTests.kt index 9cfe198..43ae3d8 100644 --- a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_PropertyFilterTests.kt +++ b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_PropertyFilterTests.kt @@ -1,8 +1,8 @@ package io.github.smiley4.schemakenerator.test -import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaGenerationStep import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonObject +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileInlineStep import io.github.smiley4.schemakenerator.reflection.steps.ReflectionTypeProcessingStep import io.kotest.core.spec.style.FunSpec import io.kotest.datatest.WithDataTestName @@ -25,7 +25,7 @@ class ReflectionParser_PropertyFilterTests : FunSpec({ includeHidden = data.includeHidden ).process(it) } .let { JsonSchemaGenerationStep().generate(it) } - .let { JsonSchemaCompileStep().compileInlining(it) } + .let { JsonSchemaCompileInlineStep().compile(it) } ((schema.json as JsonObject).properties["properties"] as JsonObject).properties.keys shouldContainExactlyInAnyOrder data.expectedProperties } diff --git a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_SwaggerGenerator_Tests.kt b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_SwaggerGenerator_Tests.kt index d3ac8f7..f1b9868 100644 --- a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_SwaggerGenerator_Tests.kt +++ b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/ReflectionParser_SwaggerGenerator_Tests.kt @@ -5,13 +5,16 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.github.smiley4.schemakenerator.reflection.steps.ReflectionTypeProcessingStep import io.github.smiley4.schemakenerator.swagger.data.TitleType import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaAutoTitleStep -import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileStep +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileInlineStep +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileReferenceRootStep +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileReferenceStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationDefaultStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationDeprecatedStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationDescriptionStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationExamplesStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationTitleStep import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaGenerationStep +import io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing import io.github.smiley4.schemakenerator.test.models.reflection.ClassWithCollections import io.github.smiley4.schemakenerator.test.models.reflection.ClassWithDeepGeneric import io.github.smiley4.schemakenerator.test.models.reflection.ClassWithGenericField @@ -63,7 +66,7 @@ class ReflectionParser_SwaggerGenerator_Tests : FunSpec({ list } } - .let { SwaggerSchemaCompileStep().compileInlining(it) } + .let { SwaggerSchemaCompileInlineStep().compile(it) } .let { Result( schema = it.swagger, @@ -108,7 +111,7 @@ class ReflectionParser_SwaggerGenerator_Tests : FunSpec({ list } } - .let { SwaggerSchemaCompileStep().compileReferencing(it) } + .let { SwaggerSchemaCompileReferenceStep().compile(it) } .let { Result( schema = it.swagger, @@ -153,7 +156,7 @@ class ReflectionParser_SwaggerGenerator_Tests : FunSpec({ list } } - .let { SwaggerSchemaCompileStep().compileReferencingRoot(it) } + .let { SwaggerSchemaCompileReferenceRootStep().compile(it) } .let { Result( schema = it.swagger, @@ -1514,6 +1517,262 @@ class ReflectionParser_SwaggerGenerator_Tests : FunSpec({ } """.trimIndent(), ), + TestData( + type = typeOf(), + testName = "class with direct self reference", + expectedResultInlining = """ + { + "schema": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "self": { + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + }, + "definitions": {} + } + """.trimIndent(), + expectedResultReferencing = """ + { + "schema": { + "type": "object", + "properties": { + "self": { + "${'$'}ref": "#/components/schemas/io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing", + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + }, + "definitions": { + "io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing": { + "type": "object", + "properties": { + "self": { + "${'$'}ref": "#/components/schemas/io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing", + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + } + } + """.trimIndent(), + expectedResultReferencingRoot = """ + { + "schema": { + "${'$'}ref": "#/components/schemas/io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing", + "exampleSetFlag": false + }, + "definitions": { + "io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing": { + "type": "object", + "properties": { + "self": { + "${'$'}ref": "#/components/schemas/io.github.smiley4.schemakenerator.test.models.reflection.ClassDirectSelfReferencing", + "exampleSetFlag": false + } + }, + "exampleSetFlag": false + } + } + } + """.trimIndent(), + ), ) } diff --git a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/Reflection_JsonSchema_TitleAppender_Tests.kt b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/Reflection_JsonSchema_TitleAppender_Tests.kt index 04de9ca..e20d240 100644 --- a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/Reflection_JsonSchema_TitleAppender_Tests.kt +++ b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/Reflection_JsonSchema_TitleAppender_Tests.kt @@ -6,7 +6,8 @@ import io.github.smiley4.schemakenerator.jsonschema.data.TitleType import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonObject import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.obj import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaAutoTitleStep -import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileInlineStep +import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCompileReferenceRootStep import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaGenerationStep import io.github.smiley4.schemakenerator.reflection.steps.ReflectionTypeProcessingStep import io.github.smiley4.schemakenerator.test.models.kotlinx.ClassWIthDifferentGenerics @@ -49,7 +50,7 @@ class Reflection_JsonSchema_TitleAppender_Tests : FunSpec({ } .let { JsonSchemaGenerationStep().generate(it) } .let { JsonSchemaAutoTitleStep(TitleType.FULL).process(it) } - .let { JsonSchemaCompileStep().compileInlining(it) } + .let { JsonSchemaCompileInlineStep().compile(it) } schema.json.prettyPrint() .let { @@ -90,7 +91,7 @@ class Reflection_JsonSchema_TitleAppender_Tests : FunSpec({ } .let { JsonSchemaGenerationStep().generate(it) } .let { JsonSchemaAutoTitleStep(TitleType.SIMPLE).process(it) } - .let { JsonSchemaCompileStep().compileInlining(it) } + .let { JsonSchemaCompileInlineStep().compile(it) } schema.json.prettyPrint() .let { @@ -131,7 +132,7 @@ class Reflection_JsonSchema_TitleAppender_Tests : FunSpec({ } .let { JsonSchemaGenerationStep().generate(it) } .let { JsonSchemaAutoTitleStep(TitleType.SIMPLE).process(it) } - .let { JsonSchemaCompileStep(RefType.SIMPLE).compileReferencingRoot(it) } + .let { JsonSchemaCompileReferenceRootStep(RefType.SIMPLE).compile(it) } .also { if (it.definitions.isNotEmpty()) { (it.json as JsonObject).properties["definitions"] = obj { diff --git a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/Test.kt b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/Test.kt new file mode 100644 index 0000000..ee9e487 --- /dev/null +++ b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/Test.kt @@ -0,0 +1,41 @@ +package io.github.smiley4.schemakenerator.test + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import io.github.smiley4.schemakenerator.core.connectSubTypes +import io.github.smiley4.schemakenerator.core.handleNameAnnotation +import io.github.smiley4.schemakenerator.jsonschema.compileReferencingRoot +import io.github.smiley4.schemakenerator.jsonschema.generateJsonSchema +import io.github.smiley4.schemakenerator.jsonschema.handleCoreAnnotations +import io.github.smiley4.schemakenerator.reflection.collectSubTypes +import io.github.smiley4.schemakenerator.reflection.processReflection +import io.github.smiley4.schemakenerator.serialization.steps.KotlinxSerializationTypeProcessingStep +import io.github.smiley4.schemakenerator.swagger.compileReferencingRoot +import io.github.smiley4.schemakenerator.swagger.generateSwaggerSchema +import io.github.smiley4.schemakenerator.swagger.handleCoreAnnotations +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCompileReferenceRootStep +import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaGenerationStep +import io.github.smiley4.schemakenerator.test.models.kotlinx.ClassDirectSelfReferencing +import io.kotest.core.spec.style.StringSpec +import kotlin.reflect.typeOf + +class Test : StringSpec({ + + "kotlinx processing infinite loop" { + + val result = typeOf() + .let { KotlinxSerializationTypeProcessingStep().process(it) } + .let { SwaggerSchemaGenerationStep().generate(it) } + .let { SwaggerSchemaCompileReferenceRootStep().compile(it) } + + println(json.writeValueAsString(result.swagger)) + result.componentSchemas.forEach { (name, schema) -> + println("$name: ${json.writeValueAsString(schema)}") + } + } + +}) { + companion object { + private val json = jacksonObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL).writerWithDefaultPrettyPrinter()!! + } +} \ No newline at end of file diff --git a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/models/kotlinx/ClassDirectSelfReferencing.kt b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/models/kotlinx/ClassDirectSelfReferencing.kt new file mode 100644 index 0000000..d7e50e6 --- /dev/null +++ b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/models/kotlinx/ClassDirectSelfReferencing.kt @@ -0,0 +1,8 @@ +package io.github.smiley4.schemakenerator.test.models.kotlinx + +import kotlinx.serialization.Serializable + +@Serializable +class ClassDirectSelfReferencing( + val self: ClassDirectSelfReferencing? +) \ No newline at end of file diff --git a/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/models/reflection/ClassDirectSelfReferencing.kt b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/models/reflection/ClassDirectSelfReferencing.kt new file mode 100644 index 0000000..95783fc --- /dev/null +++ b/schema-kenerator-test/src/test/kotlin/io/github/smiley4/schemakenerator/test/models/reflection/ClassDirectSelfReferencing.kt @@ -0,0 +1,6 @@ +package io.github.smiley4.schemakenerator.test.models.reflection + + +class ClassDirectSelfReferencing( + val self: ClassDirectSelfReferencing? +) \ No newline at end of file