Skip to content

Commit

Permalink
Support OpenApi3 Any type (#109)
Browse files Browse the repository at this point in the history
* Add broken test and fix formatting

* Add support for any type
  • Loading branch information
cjbooms authored Mar 3, 2022
1 parent dabaa46 commit 5b5e7e3
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import com.cjbooms.fabrikt.util.KaizenParserExtensions.isInlinedTypedAdditionalP
import com.cjbooms.fabrikt.util.KaizenParserExtensions.isOneOfPolymorphicTypes
import com.cjbooms.fabrikt.util.KaizenParserExtensions.isPolymorphicSubType
import com.cjbooms.fabrikt.util.KaizenParserExtensions.isPolymorphicSuperType
import com.cjbooms.fabrikt.util.KaizenParserExtensions.isReferenceObjectDefinition
import com.cjbooms.fabrikt.util.KaizenParserExtensions.isSimpleType
import com.cjbooms.fabrikt.util.KaizenParserExtensions.mappingKeys
import com.cjbooms.fabrikt.util.KaizenParserExtensions.safeName
Expand Down Expand Up @@ -241,7 +240,7 @@ class JacksonModelGenerator(
try {
externalApiSchemas.getOrPut(URL(docUrl)) { mutableSetOf() }.add(schema.safeName())
} catch (ex: MalformedURLException) {
// skip
// skip
}
}
if (depth < 10) schema.captureMissingExternalSchemas(apiDocUrl, depth + 1)
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/com/cjbooms/fabrikt/model/KotlinTypeInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ sealed class KotlinTypeInfo(val modelKClass: KClass<*>, val generatedModelClassN
object Uuid : KotlinTypeInfo(UUID::class)
object Boolean : KotlinTypeInfo(kotlin.Boolean::class)
object UntypedObject : KotlinTypeInfo(Any::class)
object AnyType : KotlinTypeInfo(Any::class)
data class Object(val simpleClassName: String) : KotlinTypeInfo(GeneratedType::class, simpleClassName)
data class Array(val parameterizedType: KotlinTypeInfo) : KotlinTypeInfo(List::class)
data class Map(val parameterizedType: KotlinTypeInfo) : KotlinTypeInfo(Map::class)
Expand Down Expand Up @@ -81,6 +82,7 @@ sealed class KotlinTypeInfo(val modelKClass: KClass<*>, val generatedModelClassN
MapTypeAdditionalProperties(
from(schema.additionalPropertiesSchema, "", enclosingName)
)
OasType.Any -> AnyType
}
}
}
21 changes: 16 additions & 5 deletions src/main/kotlin/com/cjbooms/fabrikt/model/OasType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ sealed class OasType(
val format: String? = null,
val specialization: Specialization = Specialization.NONE
) {
object Any : OasType(null)
object Boolean : OasType("boolean")
object Date : OasType("string", "date")
object DateTime : OasType("string", "date-time")
Expand All @@ -34,10 +35,18 @@ sealed class OasType(
object Enum : OasType("string", specialization = Specialization.ENUM)
object Uuid : OasType("string", specialization = Specialization.UUID)
object Map : OasType("object", specialization = Specialization.MAP)
object UnknownAdditionalProperties : OasType("object", specialization = Specialization.UNKNOWN_ADDITIONAL_PROPERTIES)
object UntypedObjectAdditionalProperties : OasType("object", specialization = Specialization.UNTYPED_OBJECT_ADDITIONAL_PROPERTIES)
object TypedObjectAdditionalProperties : OasType("object", specialization = Specialization.TYPED_OBJECT_ADDITIONAL_PROPERTIES)
object TypedMapAdditionalProperties : OasType("object", specialization = Specialization.TYPED_MAP_ADDITIONAL_PROPERTIES)
object UnknownAdditionalProperties :
OasType("object", specialization = Specialization.UNKNOWN_ADDITIONAL_PROPERTIES)

object UntypedObjectAdditionalProperties :
OasType("object", specialization = Specialization.UNTYPED_OBJECT_ADDITIONAL_PROPERTIES)

object TypedObjectAdditionalProperties :
OasType("object", specialization = Specialization.TYPED_OBJECT_ADDITIONAL_PROPERTIES)

object TypedMapAdditionalProperties :
OasType("object", specialization = Specialization.TYPED_MAP_ADDITIONAL_PROPERTIES)

companion object {
fun Schema.toOasType(oasKey: String): OasType =
values(OasType::class)
Expand All @@ -47,7 +56,9 @@ sealed class OasType(
.let { candidates ->
if (candidates.size > 1) candidates.find { it.format == format }
else candidates.firstOrNull()
} ?: throw IllegalStateException("Unknown OAS type: ${safeType()} and format: $format")
} ?: throw IllegalStateException(
"Unknown OAS type: ${safeType()} and format: $format and specialization: ${getSpecialization(oasKey)}"
)

private fun values(clazz: KClass<OasType>) =
clazz.nestedClasses.filter { it.isFinal && it.isSubclassOf(clazz) }
Expand Down
23 changes: 11 additions & 12 deletions src/main/kotlin/com/cjbooms/fabrikt/util/KaizenParserExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import com.cjbooms.fabrikt.cli.ModelCodeGenOptionType
import com.cjbooms.fabrikt.generators.MutableSettings
import com.cjbooms.fabrikt.model.OasType
import com.cjbooms.fabrikt.model.PropertyInfo
import com.cjbooms.fabrikt.util.KaizenParserExtensions.getSchemaNameInParent
import com.cjbooms.fabrikt.util.KaizenParserExtensions.mappingKeys
import com.cjbooms.fabrikt.util.NormalisedString.toMapValueClassName
import com.cjbooms.fabrikt.util.NormalisedString.toModelClassName
import com.reprezen.jsonoverlay.Overlay
Expand Down Expand Up @@ -52,9 +50,6 @@ object KaizenParserExtensions {
fun Schema.isInlinedArrayDefinition() =
isArrayType() && !isSchemaLess() && this.itemsSchema.isInlinedObjectDefinition()

fun Schema.isReferenceObjectDefinition() =
isObjectType() && !isSchemaLess() && !Overlay.of(this).pathFromRoot.contains("properties")

fun Schema.toModelClassName(enclosingClassName: String = "") = enclosingClassName + safeName().toModelClassName()

fun Schema.toMapValueClassName() = safeName().toMapValueClassName()
Expand All @@ -77,16 +72,17 @@ object KaizenParserExtensions {
fun Schema.getEnumValues(): List<String> = when {
this.hasEnums() -> this.enums.map { it.toString() }.filterNot { it.isBlank() }
!MutableSettings.modelOptions().contains(ModelCodeGenOptionType.X_EXTENSIBLE_ENUMS) -> emptyList()
else -> extensions[EXTENSIBLE_ENUM_KEY]?.let { it as List<String?> }?.filterNotNull()?.filterNot { it.isBlank() } ?: emptyList()
else -> extensions[EXTENSIBLE_ENUM_KEY]?.let { it as List<String?> }?.filterNotNull()
?.filterNot { it.isBlank() } ?: emptyList()
}

fun Schema.hasAdditionalProperties(): Boolean = isObjectType() && Overlay.of(additionalPropertiesSchema).isPresent

fun Schema.isUnknownAdditionalProperties(oasKey: String) = type == null &&
getSchemaNameInParent() ?: oasKey == "additionalProperties" && properties?.isEmpty() == true
(getSchemaNameInParent() ?: oasKey) == "additionalProperties" && properties?.isEmpty() == true

fun Schema.isUntypedAdditionalProperties(oasKey: String) = type == OasType.Object.type &&
getSchemaNameInParent() ?: oasKey == "additionalProperties" && properties?.isEmpty() == true
(getSchemaNameInParent() ?: oasKey) == "additionalProperties" && properties?.isEmpty() == true

fun Schema.isTypedAdditionalProperties(oasKey: String) = type == OasType.Object.type &&
(getSchemaNameInParent() == "additionalProperties" || oasKey == "additionalProperties") && properties?.isEmpty() != true
Expand All @@ -95,8 +91,8 @@ object KaizenParserExtensions {
(oasKey == "additionalProperties") && properties?.isEmpty() == true &&
hasAdditionalProperties()

fun Schema.isComplexTypedAdditionalProperties(oasKey: String) =
getSchemaNameInParent() ?: oasKey == "additionalProperties" && properties?.isEmpty() != true && !isSimpleType()
fun Schema.isComplexTypedAdditionalProperties(oasKey: String) = (getSchemaNameInParent() ?: oasKey) ==
"additionalProperties" && properties?.isEmpty() != true && !isSimpleType()

fun Schema.isSimpleType(): Boolean =
(simpleTypes.contains(type) && !isEnumDefinition()) || isInlineableMapDefinition()
Expand All @@ -105,7 +101,8 @@ object KaizenParserExtensions {

private fun Schema.isArrayType() = OasType.Array.type == type

fun Schema.isNotDefined() = type == null && !(hasAllOfSchemas() || hasOneOfSchemas() || hasAnyOfSchemas())
fun Schema.isNotDefined() = !Overlay.of(this).isPresent &&
type == null && !(hasAllOfSchemas() || hasOneOfSchemas() || hasAnyOfSchemas())

private fun Schema.getSchemaNameInParent(): String? = Overlay.of(this).pathInParent

Expand Down Expand Up @@ -175,7 +172,9 @@ object KaizenParserExtensions {
allOfSchemas?.firstOrNull { it.type != null } != null -> allOfSchemas.first { it.type != null }.type
oneOfSchemas?.firstOrNull { it.type != null } != null -> oneOfSchemas.first { it.type != null }.type
anyOfSchemas?.firstOrNull { it.type != null } != null -> anyOfSchemas.first { it.type != null }.type
else -> "object"
isOneOfPolymorphicTypes() -> "object"
isUnknownAdditionalProperties("") -> "object"
else -> null
}

fun Schema.isOneOfPolymorphicTypes() =
Expand Down
7 changes: 6 additions & 1 deletion src/test/resources/examples/anyOfOneOfAllOf/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,9 @@ components:
second_property:
type: string
third_property:
type: string
type: string
forth_property:
$ref: '#/components/schemas/AnyValue'

AnyValue:
description: Tests support for https://swagger.io/docs/specification/data-models/data-types/#any
6 changes: 5 additions & 1 deletion src/test/resources/examples/anyOfOneOfAllOf/models/Models.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package examples.anyOfOneOfAllOf.models

import com.fasterxml.jackson.annotation.JsonProperty
import java.math.BigDecimal
import kotlin.Any
import kotlin.String

data class ComplexParent(
Expand Down Expand Up @@ -82,5 +83,8 @@ data class SecondOneB(
val secondProperty: String? = null,
@param:JsonProperty("third_property")
@get:JsonProperty("third_property")
val thirdProperty: String? = null
val thirdProperty: String? = null,
@param:JsonProperty("forth_property")
@get:JsonProperty("forth_property")
val forthProperty: Any? = null
)

0 comments on commit 5b5e7e3

Please sign in to comment.