feat: enriched enrichments (#566)
This commit is contained in:
@ -26,7 +26,7 @@ class KotlinXSchemaConfigurator : SchemaConfigurator {
|
||||
.filterIsInstance<SerialName>()
|
||||
.firstOrNull()?.value ?: property.name
|
||||
|
||||
override fun sealedTypeEnrichment(
|
||||
override fun sealedObjectEnrichment(
|
||||
implementationType: KType,
|
||||
implementationSchema: JsonSchema,
|
||||
): JsonSchema {
|
||||
|
@ -9,7 +9,7 @@ interface SchemaConfigurator {
|
||||
fun serializableMemberProperties(clazz: KClass<*>): Collection<KProperty1<out Any, *>>
|
||||
fun serializableName(property: KProperty1<out Any, *>): String
|
||||
|
||||
fun sealedTypeEnrichment(
|
||||
fun sealedObjectEnrichment(
|
||||
implementationType: KType,
|
||||
implementationSchema: JsonSchema
|
||||
): JsonSchema
|
||||
|
@ -1,5 +1,9 @@
|
||||
package io.bkbn.kompendium.json.schema
|
||||
|
||||
import io.bkbn.kompendium.enrichment.CollectionEnrichment
|
||||
import io.bkbn.kompendium.enrichment.Enrichment
|
||||
import io.bkbn.kompendium.enrichment.MapEnrichment
|
||||
import io.bkbn.kompendium.enrichment.ObjectEnrichment
|
||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.definition.NullableDefinition
|
||||
@ -23,7 +27,7 @@ object SchemaGenerator {
|
||||
type: KType,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
enrichment: TypeEnrichment<*>? = null
|
||||
enrichment: Enrichment? = null
|
||||
): JsonSchema {
|
||||
val slug = type.getSlug(enrichment)
|
||||
|
||||
@ -47,18 +51,7 @@ object SchemaGenerator {
|
||||
String::class -> checkForNull(type, TypeDefinition.STRING)
|
||||
Boolean::class -> checkForNull(type, TypeDefinition.BOOLEAN)
|
||||
UUID::class -> checkForNull(type, TypeDefinition.UUID)
|
||||
else -> when {
|
||||
clazz.isSubclassOf(Enum::class) -> EnumHandler.handle(type, clazz, cache, enrichment)
|
||||
clazz.isSubclassOf(Collection::class) -> CollectionHandler.handle(type, cache, schemaConfigurator, enrichment)
|
||||
clazz.isSubclassOf(Map::class) -> MapHandler.handle(type, cache, schemaConfigurator, enrichment)
|
||||
else -> {
|
||||
if (clazz.isSealed) {
|
||||
SealedObjectHandler.handle(type, clazz, cache, schemaConfigurator, enrichment)
|
||||
} else {
|
||||
SimpleObjectHandler.handle(type, clazz, cache, schemaConfigurator, enrichment)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> complexTypeToSchema(clazz, type, cache, schemaConfigurator, enrichment as? TypeEnrichment<*>?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,4 +70,59 @@ object SchemaGenerator {
|
||||
true -> OneOfDefinition(NullableDefinition(), schema)
|
||||
false -> schema
|
||||
}
|
||||
|
||||
private fun complexTypeToSchema(
|
||||
clazz: KClass<*>,
|
||||
type: KType,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
enrichment: Enrichment? = null
|
||||
): JsonSchema = when {
|
||||
clazz.isSubclassOf(Enum::class) -> EnumHandler.handle(type, clazz, cache)
|
||||
clazz.isSubclassOf(Collection::class) -> handleCollection(type, cache, schemaConfigurator, enrichment)
|
||||
clazz.isSubclassOf(Map::class) -> handleMap(type, cache, schemaConfigurator, enrichment)
|
||||
else -> handleObject(type, clazz, cache, schemaConfigurator, enrichment)
|
||||
}
|
||||
|
||||
private fun handleCollection(
|
||||
type: KType,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
enrichment: Enrichment?
|
||||
) = when (enrichment) {
|
||||
is CollectionEnrichment<*> -> CollectionHandler.handle(type, cache, schemaConfigurator, enrichment)
|
||||
null -> CollectionHandler.handle(type, cache, schemaConfigurator, null)
|
||||
else -> error("Incorrect enrichment type for enrichment id: ${enrichment.id}")
|
||||
}
|
||||
|
||||
private fun handleMap(
|
||||
type: KType,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
enrichment: Enrichment?
|
||||
) = when (enrichment) {
|
||||
is MapEnrichment<*> -> MapHandler.handle(type, cache, schemaConfigurator, enrichment)
|
||||
null -> MapHandler.handle(type, cache, schemaConfigurator, null)
|
||||
else -> error("Incorrect enrichment type for enrichment id: ${enrichment.id}")
|
||||
}
|
||||
|
||||
private fun handleObject(
|
||||
type: KType,
|
||||
clazz: KClass<*>,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
enrichment: Enrichment?
|
||||
) = when (clazz.isSealed) {
|
||||
true -> when (enrichment) {
|
||||
is ObjectEnrichment<*> -> SealedObjectHandler.handle(type, clazz, cache, schemaConfigurator, enrichment)
|
||||
null -> SealedObjectHandler.handle(type, clazz, cache, schemaConfigurator, null)
|
||||
else -> error("Incorrect enrichment type for enrichment id: ${enrichment.id}")
|
||||
}
|
||||
|
||||
false -> when (enrichment) {
|
||||
is ObjectEnrichment<*> -> SimpleObjectHandler.handle(type, clazz, cache, schemaConfigurator, enrichment)
|
||||
null -> SimpleObjectHandler.handle(type, clazz, cache, schemaConfigurator, null)
|
||||
else -> error("Incorrect enrichment type for enrichment id: ${enrichment.id}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
data class MapDefinition(
|
||||
val additionalProperties: JsonSchema,
|
||||
val maxProperties: Int? = null,
|
||||
val minProperties: Int? = null,
|
||||
override val deprecated: Boolean? = null,
|
||||
override val description: String? = null,
|
||||
) : JsonSchema {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package io.bkbn.kompendium.json.schema.handler
|
||||
|
||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||
import io.bkbn.kompendium.enrichment.CollectionEnrichment
|
||||
import io.bkbn.kompendium.json.schema.SchemaConfigurator
|
||||
import io.bkbn.kompendium.json.schema.SchemaGenerator
|
||||
import io.bkbn.kompendium.json.schema.definition.ArrayDefinition
|
||||
@ -19,18 +19,23 @@ object CollectionHandler {
|
||||
type: KType,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
enrichment: TypeEnrichment<*>? = null
|
||||
enrichment: CollectionEnrichment<*>? = null
|
||||
): JsonSchema {
|
||||
require(enrichment is CollectionEnrichment<*> || enrichment == null) {
|
||||
"Enrichment for collection must be either null or a CollectionEnrichment"
|
||||
}
|
||||
|
||||
val collectionType = type.arguments.first().type
|
||||
?: error("This indicates a bug in Kompendium, please open a GitHub issue!")
|
||||
val typeSchema = SchemaGenerator.fromTypeToSchema(collectionType, cache, schemaConfigurator, enrichment).let {
|
||||
if ((it is TypeDefinition && it.type == "object") || it is EnumDefinition) {
|
||||
cache[collectionType.getSlug(enrichment)] = it
|
||||
ReferenceDefinition(collectionType.getReferenceSlug(enrichment))
|
||||
} else {
|
||||
it
|
||||
val typeSchema =
|
||||
SchemaGenerator.fromTypeToSchema(collectionType, cache, schemaConfigurator, enrichment?.itemEnrichment).let {
|
||||
if ((it is TypeDefinition && it.type == "object") || it is EnumDefinition) {
|
||||
cache[collectionType.getSlug(enrichment)] = it
|
||||
ReferenceDefinition(collectionType.getReferenceSlug(enrichment))
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
val definition = ArrayDefinition(typeSchema)
|
||||
return when (type.isMarkedNullable) {
|
||||
true -> OneOfDefinition(NullableDefinition(), definition)
|
||||
|
@ -0,0 +1,94 @@
|
||||
package io.bkbn.kompendium.json.schema.handler
|
||||
|
||||
import io.bkbn.kompendium.enrichment.CollectionEnrichment
|
||||
import io.bkbn.kompendium.enrichment.Enrichment
|
||||
import io.bkbn.kompendium.enrichment.MapEnrichment
|
||||
import io.bkbn.kompendium.enrichment.NumberEnrichment
|
||||
import io.bkbn.kompendium.enrichment.ObjectEnrichment
|
||||
import io.bkbn.kompendium.enrichment.StringEnrichment
|
||||
import io.bkbn.kompendium.json.schema.definition.ArrayDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.definition.MapDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
|
||||
object EnrichmentHandler {
|
||||
|
||||
fun Enrichment.applyToSchema(schema: JsonSchema): JsonSchema = when (this) {
|
||||
is NumberEnrichment -> applyToSchema(schema)
|
||||
is StringEnrichment -> applyToSchema(schema)
|
||||
is CollectionEnrichment<*> -> applyToSchema(schema)
|
||||
is MapEnrichment<*> -> applyToSchema(schema)
|
||||
is ObjectEnrichment<*> -> applyToSchema(schema)
|
||||
else -> error("Incorrect enrichment type for enrichment id: ${this.id}")
|
||||
}
|
||||
|
||||
private fun ObjectEnrichment<*>.applyToSchema(schema: JsonSchema): JsonSchema = when (schema) {
|
||||
is TypeDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||
is ReferenceDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||
else -> error("Incorrect enrichment type for enrichment id: ${this.id}")
|
||||
}
|
||||
|
||||
private fun MapEnrichment<*>.applyToSchema(schema: JsonSchema): JsonSchema = when (schema) {
|
||||
is MapDefinition -> schema.copyMapEnrichment(this)
|
||||
else -> error("Incorrect enrichment type for enrichment id: ${this.id}")
|
||||
}
|
||||
|
||||
private fun CollectionEnrichment<*>.applyToSchema(schema: JsonSchema): JsonSchema = when (schema) {
|
||||
is ArrayDefinition -> schema.copyArrayEnrichment(this)
|
||||
else -> error("Incorrect enrichment type for enrichment id: ${this.id}")
|
||||
}
|
||||
|
||||
private fun NumberEnrichment.applyToSchema(schema: JsonSchema): JsonSchema = when (schema) {
|
||||
is TypeDefinition -> schema.copyNumberEnrichment(this)
|
||||
else -> error("Incorrect enrichment type for enrichment id: ${this.id}")
|
||||
}
|
||||
|
||||
private fun StringEnrichment.applyToSchema(schema: JsonSchema): JsonSchema = when (schema) {
|
||||
is TypeDefinition -> schema.copyStringEnrichment(this)
|
||||
else -> error("Incorrect enrichment type for enrichment id: ${this.id}")
|
||||
}
|
||||
|
||||
private fun TypeDefinition.copyNumberEnrichment(
|
||||
enrichment: NumberEnrichment
|
||||
): TypeDefinition = copy(
|
||||
deprecated = enrichment.deprecated,
|
||||
description = enrichment.description,
|
||||
multipleOf = enrichment.multipleOf,
|
||||
maximum = enrichment.maximum,
|
||||
exclusiveMaximum = enrichment.exclusiveMaximum,
|
||||
minimum = enrichment.minimum,
|
||||
exclusiveMinimum = enrichment.exclusiveMinimum,
|
||||
)
|
||||
|
||||
private fun TypeDefinition.copyStringEnrichment(
|
||||
enrichment: StringEnrichment
|
||||
): TypeDefinition = copy(
|
||||
deprecated = enrichment.deprecated,
|
||||
description = enrichment.description,
|
||||
maxLength = enrichment.maxLength,
|
||||
minLength = enrichment.minLength,
|
||||
pattern = enrichment.pattern,
|
||||
contentEncoding = enrichment.contentEncoding,
|
||||
contentMediaType = enrichment.contentMediaType,
|
||||
)
|
||||
|
||||
private fun ArrayDefinition.copyArrayEnrichment(
|
||||
enrichment: CollectionEnrichment<*>
|
||||
): ArrayDefinition = copy(
|
||||
deprecated = enrichment.deprecated,
|
||||
description = enrichment.description,
|
||||
minItems = enrichment.minItems,
|
||||
maxItems = enrichment.maxItems,
|
||||
uniqueItems = enrichment.uniqueItems,
|
||||
)
|
||||
|
||||
private fun MapDefinition.copyMapEnrichment(
|
||||
enrichment: MapEnrichment<*>
|
||||
): MapDefinition = copy(
|
||||
deprecated = enrichment.deprecated,
|
||||
description = enrichment.description,
|
||||
minProperties = enrichment.minProperties,
|
||||
maxProperties = enrichment.maxProperties,
|
||||
)
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package io.bkbn.kompendium.json.schema.handler
|
||||
|
||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||
import io.bkbn.kompendium.json.schema.definition.EnumDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||
@ -14,9 +13,8 @@ object EnumHandler {
|
||||
type: KType,
|
||||
clazz: KClass<*>,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
enrichment: TypeEnrichment<*>? = null
|
||||
): JsonSchema {
|
||||
cache[type.getSlug(enrichment)] = ReferenceDefinition(type.getReferenceSlug(enrichment))
|
||||
cache[type.getSlug()] = ReferenceDefinition(type.getReferenceSlug())
|
||||
|
||||
val options = clazz.java.enumConstants.map { it.toString() }.toSet()
|
||||
return EnumDefinition(enum = options)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package io.bkbn.kompendium.json.schema.handler
|
||||
|
||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||
import io.bkbn.kompendium.enrichment.MapEnrichment
|
||||
import io.bkbn.kompendium.json.schema.SchemaConfigurator
|
||||
import io.bkbn.kompendium.json.schema.SchemaGenerator
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
@ -20,20 +20,24 @@ object MapHandler {
|
||||
type: KType,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
enrichment: TypeEnrichment<*>? = null
|
||||
enrichment: MapEnrichment<*>? = null
|
||||
): JsonSchema {
|
||||
require(enrichment is MapEnrichment<*> || enrichment == null) {
|
||||
"Enrichment for map must be either null or a MapEnrichment"
|
||||
}
|
||||
require(type.arguments.first().type?.classifier as KClass<*> == String::class) {
|
||||
"JSON requires that map keys MUST be Strings. You provided ${type.arguments.first().type}"
|
||||
}
|
||||
val valueType = type.arguments[1].type ?: error("this indicates a bug in Kompendium, please open a GitHub issue")
|
||||
val valueSchema = SchemaGenerator.fromTypeToSchema(valueType, cache, schemaConfigurator, enrichment).let {
|
||||
if (it is TypeDefinition && it.type == "object") {
|
||||
cache[valueType.getSlug(enrichment)] = it
|
||||
ReferenceDefinition(valueType.getReferenceSlug(enrichment))
|
||||
} else {
|
||||
it
|
||||
val valueSchema =
|
||||
SchemaGenerator.fromTypeToSchema(valueType, cache, schemaConfigurator, enrichment?.valueEnrichment).let {
|
||||
if (it is TypeDefinition && it.type == "object") {
|
||||
cache[valueType.getSlug(enrichment)] = it
|
||||
ReferenceDefinition(valueType.getReferenceSlug(enrichment))
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
val definition = MapDefinition(valueSchema)
|
||||
return when (type.isMarkedNullable) {
|
||||
true -> OneOfDefinition(NullableDefinition(), definition)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package io.bkbn.kompendium.json.schema.handler
|
||||
|
||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||
import io.bkbn.kompendium.enrichment.ObjectEnrichment
|
||||
import io.bkbn.kompendium.json.schema.SchemaConfigurator
|
||||
import io.bkbn.kompendium.json.schema.SchemaGenerator
|
||||
import io.bkbn.kompendium.json.schema.definition.AnyOfDefinition
|
||||
@ -20,14 +20,14 @@ object SealedObjectHandler {
|
||||
clazz: KClass<*>,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
enrichment: TypeEnrichment<*>? = null,
|
||||
enrichment: ObjectEnrichment<*>? = null,
|
||||
): JsonSchema {
|
||||
val subclasses = clazz.sealedSubclasses
|
||||
.map { it.createType(type.arguments) }
|
||||
.map { t ->
|
||||
SchemaGenerator.fromTypeToSchema(t, cache, schemaConfigurator, enrichment)
|
||||
.let {
|
||||
schemaConfigurator.sealedTypeEnrichment(t, it)
|
||||
schemaConfigurator.sealedObjectEnrichment(t, it)
|
||||
}.let { js ->
|
||||
if (js is TypeDefinition && js.type == "object") {
|
||||
val slug = t.getSlug(enrichment)
|
||||
|
@ -1,19 +1,17 @@
|
||||
package io.bkbn.kompendium.json.schema.handler
|
||||
|
||||
import io.bkbn.kompendium.enrichment.PropertyEnrichment
|
||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||
import io.bkbn.kompendium.enrichment.Enrichment
|
||||
import io.bkbn.kompendium.enrichment.ObjectEnrichment
|
||||
import io.bkbn.kompendium.json.schema.SchemaConfigurator
|
||||
import io.bkbn.kompendium.json.schema.SchemaGenerator
|
||||
import io.bkbn.kompendium.json.schema.definition.AnyOfDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.ArrayDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.EnumDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.definition.MapDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.NullableDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.OneOfDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.json.schema.exception.UnknownSchemaException
|
||||
import io.bkbn.kompendium.json.schema.handler.EnrichmentHandler.applyToSchema
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getReferenceSlug
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getSlug
|
||||
import kotlin.reflect.KClass
|
||||
@ -32,28 +30,31 @@ object SimpleObjectHandler {
|
||||
clazz: KClass<*>,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
enrichment: TypeEnrichment<*>?,
|
||||
enrichment: ObjectEnrichment<*>?,
|
||||
): JsonSchema {
|
||||
cache[type.getSlug(enrichment)] = ReferenceDefinition(type.getReferenceSlug(enrichment))
|
||||
require(enrichment is ObjectEnrichment<*> || enrichment == null) {
|
||||
"Enrichment for object must either be of type ObjectEnrichment or null"
|
||||
}
|
||||
|
||||
val slug = type.getSlug(enrichment)
|
||||
val referenceSlug = type.getReferenceSlug(enrichment)
|
||||
cache[slug] = ReferenceDefinition(referenceSlug)
|
||||
|
||||
val typeMap = clazz.typeParameters.zip(type.arguments).toMap()
|
||||
val props = schemaConfigurator.serializableMemberProperties(clazz)
|
||||
.filterNot { it.javaField == null }
|
||||
.associate { prop ->
|
||||
val propTypeEnrichment = when (val pe = enrichment?.getEnrichmentForProperty(prop)) {
|
||||
is PropertyEnrichment -> pe
|
||||
else -> null
|
||||
}
|
||||
val propEnrichment = enrichment?.propertyEnrichment?.get(prop)
|
||||
|
||||
val schema = when (prop.needsToInjectGenerics(typeMap)) {
|
||||
true -> handleNestedGenerics(typeMap, prop, cache, schemaConfigurator, propTypeEnrichment)
|
||||
true -> handleNestedGenerics(typeMap, prop, cache, schemaConfigurator, propEnrichment)
|
||||
false -> when (typeMap.containsKey(prop.returnType.classifier)) {
|
||||
true -> handleGenericProperty(prop, typeMap, cache, schemaConfigurator, propTypeEnrichment)
|
||||
false -> handleProperty(prop, cache, schemaConfigurator, propTypeEnrichment?.typeEnrichment)
|
||||
true -> handleGenericProperty(prop, typeMap, cache, schemaConfigurator, propEnrichment)
|
||||
false -> handleProperty(prop, cache, schemaConfigurator, propEnrichment)
|
||||
}
|
||||
}
|
||||
|
||||
val enrichedSchema = propTypeEnrichment?.applyToSchema(schema) ?: schema
|
||||
val enrichedSchema = propEnrichment?.applyToSchema(schema) ?: schema
|
||||
|
||||
val nullCheckSchema = when (prop.returnType.isMarkedNullable && !enrichedSchema.isNullable()) {
|
||||
true -> OneOfDefinition(NullableDefinition(), enrichedSchema)
|
||||
@ -84,11 +85,14 @@ object SimpleObjectHandler {
|
||||
.map { schemaConfigurator.serializableName(it) }
|
||||
.toSet()
|
||||
|
||||
return TypeDefinition(
|
||||
val definition = TypeDefinition(
|
||||
type = "object",
|
||||
properties = props,
|
||||
required = required
|
||||
)
|
||||
|
||||
cache[slug] = definition
|
||||
return definition
|
||||
}
|
||||
|
||||
private fun KProperty<*>.needsToInjectGenerics(
|
||||
@ -103,7 +107,7 @@ object SimpleObjectHandler {
|
||||
prop: KProperty<*>,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
propEnrichment: PropertyEnrichment?
|
||||
propEnrichment: Enrichment?
|
||||
): JsonSchema {
|
||||
val propClass = prop.returnType.classifier as KClass<*>
|
||||
val types = prop.returnType.arguments.map {
|
||||
@ -111,7 +115,7 @@ object SimpleObjectHandler {
|
||||
typeMap.filterKeys { k -> k.name == typeSymbol }.values.first()
|
||||
}
|
||||
val constructedType = propClass.createType(types)
|
||||
return SchemaGenerator.fromTypeToSchema(constructedType, cache, schemaConfigurator, propEnrichment?.typeEnrichment)
|
||||
return SchemaGenerator.fromTypeToSchema(constructedType, cache, schemaConfigurator, propEnrichment)
|
||||
.let {
|
||||
if (it.isOrContainsObjectOrEnumDef()) {
|
||||
cache[constructedType.getSlug(propEnrichment)] = it
|
||||
@ -127,14 +131,14 @@ object SimpleObjectHandler {
|
||||
typeMap: Map<KTypeParameter, KTypeProjection>,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
propEnrichment: PropertyEnrichment?
|
||||
propEnrichment: Enrichment?
|
||||
): JsonSchema {
|
||||
val type = typeMap[prop.returnType.classifier]?.type
|
||||
?: error("This indicates a bug in Kompendium, please open a GitHub issue")
|
||||
return SchemaGenerator.fromTypeToSchema(type, cache, schemaConfigurator, propEnrichment?.typeEnrichment).let {
|
||||
return SchemaGenerator.fromTypeToSchema(type, cache, schemaConfigurator, propEnrichment).let {
|
||||
if (it.isOrContainsObjectOrEnumDef()) {
|
||||
cache[type.getSlug(propEnrichment?.typeEnrichment)] = it
|
||||
ReferenceDefinition(type.getReferenceSlug(propEnrichment?.typeEnrichment))
|
||||
cache[type.getSlug(propEnrichment)] = it
|
||||
ReferenceDefinition(type.getReferenceSlug(propEnrichment))
|
||||
} else {
|
||||
it
|
||||
}
|
||||
@ -145,7 +149,7 @@ object SimpleObjectHandler {
|
||||
prop: KProperty<*>,
|
||||
cache: MutableMap<String, JsonSchema>,
|
||||
schemaConfigurator: SchemaConfigurator,
|
||||
propEnrichment: TypeEnrichment<*>?
|
||||
propEnrichment: Enrichment?
|
||||
): JsonSchema =
|
||||
SchemaGenerator.fromTypeToSchema(prop.returnType, cache, schemaConfigurator, propEnrichment).let {
|
||||
if (it.isOrContainsObjectOrEnumDef()) {
|
||||
@ -165,37 +169,4 @@ object SimpleObjectHandler {
|
||||
}
|
||||
|
||||
private fun JsonSchema.isNullable(): Boolean = this is OneOfDefinition && this.oneOf.any { it is NullableDefinition }
|
||||
|
||||
private fun PropertyEnrichment.applyToSchema(schema: JsonSchema): JsonSchema = when (schema) {
|
||||
is AnyOfDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||
is ArrayDefinition -> schema.copy(
|
||||
deprecated = deprecated,
|
||||
description = description,
|
||||
minItems = minItems,
|
||||
maxItems = maxItems,
|
||||
uniqueItems = uniqueItems,
|
||||
)
|
||||
|
||||
is EnumDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||
is MapDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||
is NullableDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||
is OneOfDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||
is ReferenceDefinition -> schema.copy(deprecated = deprecated, description = description)
|
||||
is TypeDefinition -> schema.copy(
|
||||
deprecated = deprecated,
|
||||
description = description,
|
||||
multipleOf = multipleOf,
|
||||
maximum = maximum,
|
||||
exclusiveMaximum = exclusiveMaximum,
|
||||
minimum = minimum,
|
||||
exclusiveMinimum = exclusiveMinimum,
|
||||
maxLength = maxLength,
|
||||
minLength = minLength,
|
||||
pattern = pattern,
|
||||
contentEncoding = contentEncoding,
|
||||
contentMediaType = contentMediaType,
|
||||
maxProperties = maxProperties,
|
||||
minProperties = minProperties,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
package io.bkbn.kompendium.json.schema.util
|
||||
|
||||
import io.bkbn.kompendium.enrichment.Enrichment
|
||||
import io.bkbn.kompendium.enrichment.PropertyEnrichment
|
||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
|
||||
@ -11,9 +9,8 @@ object Helpers {
|
||||
const val COMPONENT_SLUG = "#/components/schemas"
|
||||
|
||||
fun KType.getSlug(enrichment: Enrichment? = null) = when (enrichment) {
|
||||
is TypeEnrichment<*> -> getEnrichedSlug(enrichment)
|
||||
is PropertyEnrichment -> error("Slugs should not be generated for field enrichments")
|
||||
else -> getSimpleSlug()
|
||||
null -> getSimpleSlug()
|
||||
else -> getEnrichedSlug(enrichment)
|
||||
}
|
||||
|
||||
fun KType.getSimpleSlug(): String = when {
|
||||
@ -21,12 +18,11 @@ object Helpers {
|
||||
else -> (classifier as KClass<*>).kompendiumSlug() ?: error("Could not determine simple name for $this")
|
||||
}
|
||||
|
||||
private fun KType.getEnrichedSlug(enrichment: TypeEnrichment<*>) = getSimpleSlug() + "-${enrichment.id}"
|
||||
private fun KType.getEnrichedSlug(enrichment: Enrichment) = getSimpleSlug() + "-${enrichment.id}"
|
||||
|
||||
fun KType.getReferenceSlug(enrichment: Enrichment? = null): String = when (enrichment) {
|
||||
is TypeEnrichment<*> -> getSimpleReferenceSlug() + "-${enrichment.id}"
|
||||
is PropertyEnrichment -> error("Reference slugs should never be generated for field enrichments")
|
||||
else -> getSimpleReferenceSlug()
|
||||
null -> getSimpleReferenceSlug()
|
||||
else -> getSimpleReferenceSlug() + "-${enrichment.id}"
|
||||
}
|
||||
|
||||
private fun KType.getSimpleReferenceSlug() = when {
|
||||
|
@ -13,7 +13,10 @@ import io.bkbn.kompendium.core.fixtures.TransientObject
|
||||
import io.bkbn.kompendium.core.fixtures.UnbackedObject
|
||||
import io.bkbn.kompendium.core.fixtures.GenericObject
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.getFileSnapshot
|
||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||
import io.bkbn.kompendium.enrichment.CollectionEnrichment
|
||||
import io.bkbn.kompendium.enrichment.NumberEnrichment
|
||||
import io.bkbn.kompendium.enrichment.ObjectEnrichment
|
||||
import io.bkbn.kompendium.enrichment.StringEnrichment
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
@ -114,12 +117,16 @@ class SchemaGeneratorTest : DescribeSpec({
|
||||
it("Can attach an enrichment to a simple type") {
|
||||
jsonSchemaTest<TestSimpleRequest>(
|
||||
snapshotName = "T0022__enriched_simple_object.json",
|
||||
enrichment = TypeEnrichment("simple") {
|
||||
enrichment = ObjectEnrichment("simple") {
|
||||
TestSimpleRequest::a {
|
||||
description = "This is a simple description"
|
||||
StringEnrichment("blah") {
|
||||
description = "This is a simple description"
|
||||
}
|
||||
}
|
||||
TestSimpleRequest::b {
|
||||
deprecated = true
|
||||
NumberEnrichment("bla") {
|
||||
deprecated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -127,12 +134,16 @@ class SchemaGeneratorTest : DescribeSpec({
|
||||
it("Can properly assign a reference to a nested enrichment") {
|
||||
jsonSchemaTest<ComplexRequest>(
|
||||
snapshotName = "T0023__enriched_nested_reference.json",
|
||||
enrichment = TypeEnrichment("example") {
|
||||
enrichment = ObjectEnrichment("example") {
|
||||
ComplexRequest::tables {
|
||||
description = "Collection of important items"
|
||||
typeEnrichment = TypeEnrichment("table") {
|
||||
NestedComplexItem::name {
|
||||
description = "The name of the table"
|
||||
CollectionEnrichment<List<NestedComplexItem>>("tables") {
|
||||
description = "Collection of important items"
|
||||
itemEnrichment = ObjectEnrichment("table") {
|
||||
NestedComplexItem::name {
|
||||
StringEnrichment("name") {
|
||||
description = "The name of the table"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -142,15 +153,19 @@ class SchemaGeneratorTest : DescribeSpec({
|
||||
it("Can properly assign a reference to a generic object") {
|
||||
jsonSchemaTest<GenericObject<TestSimpleRequest>>(
|
||||
snapshotName = "T0025__enrichment_generic_object.json",
|
||||
enrichment = TypeEnrichment("generic") {
|
||||
enrichment = ObjectEnrichment("generic") {
|
||||
GenericObject<TestSimpleRequest>::data {
|
||||
description = "This is a generic param"
|
||||
typeEnrichment = TypeEnrichment("simple") {
|
||||
ObjectEnrichment<TestSimpleRequest>("blob") {
|
||||
description = "This is a generic object"
|
||||
TestSimpleRequest::a {
|
||||
description = "This is a simple description"
|
||||
StringEnrichment("blah") {
|
||||
description = "This is a simple description"
|
||||
}
|
||||
}
|
||||
TestSimpleRequest::b {
|
||||
deprecated = true
|
||||
NumberEnrichment("bla") {
|
||||
deprecated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,7 +183,7 @@ class SchemaGeneratorTest : DescribeSpec({
|
||||
|
||||
private fun JsonSchema.serialize() = json.encodeToString(JsonSchema.serializer(), this)
|
||||
|
||||
private inline fun <reified T> jsonSchemaTest(snapshotName: String, enrichment: TypeEnrichment<*>? = null) {
|
||||
private inline fun <reified T> jsonSchemaTest(snapshotName: String, enrichment: ObjectEnrichment<*>? = null) {
|
||||
// act
|
||||
val schema = SchemaGenerator.fromTypeToSchema(
|
||||
type = typeOf<T>(),
|
||||
|
@ -9,7 +9,7 @@
|
||||
},
|
||||
"tables": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NestedComplexItem-table"
|
||||
"$ref": "#/components/schemas/NestedComplexItem-tables"
|
||||
},
|
||||
"description": "Collection of important items",
|
||||
"type": "array"
|
||||
|
@ -2,8 +2,8 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"description": "This is a generic param",
|
||||
"$ref": "#/components/schemas/TestSimpleRequest-simple"
|
||||
"$ref": "#/components/schemas/TestSimpleRequest-blob",
|
||||
"description": "This is a generic object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
Reference in New Issue
Block a user