Repository: cjbooms/fabrikt
Pull request #26 (2020-12-14 13:28:02)

Issue #24 Add the ability to generate the quarkus reflection-config.jon file
Issue #24 Add the ability to generate the quarkus reflection-config.jon file


Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 43d608c..dc70b85 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -21,6 +21,9 @@ jobs:
     steps:
     # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
     - uses: actions/checkout@v2
+    - uses: actions/setup-java@v1
+      with:
+        java-version: '11'
 
     # Runs a single command using the runners shell
     - name: Build Project
diff --git a/README.md b/README.md
index 8ded8bc..d0183d6 100644
--- a/README.md
+++ b/README.md
@@ -35,16 +35,18 @@ This section documents the available CLI parameters for controlling what gets ge
  | * `--base-package`     | The base package which all code will be generated under.
  |   `--http-client-opts` | Select the options for the http client code that you want to be generated.
  |                        | CHOOSE ANY OF:
- |                        |   `RESILIENCE4J` - Generates a fault tolerance service for the client using the following library "io.github.resilience4j:resilience4j-all:1.2.0"
+ |                        |   `RESILIENCE4J` - Generates a fault tolerance service for the client using the following library "io.github.resilience4j:resilience4j-all:+"
  |   `--http-model-opts`  | Select the options for the http models that you want to be generated.
  |                        | CHOOSE ANY OF:
  |                        |   `JAVA_SERIALIZATION` - This option adds Java Serializable interface to the generated models
+ |                        |   `QUARKUS_REFLECTION` - This option adds @RegisterForReflection to the generated models. Requires dependency "'io.quarkus:quarkus-core:+"
  |   `--output-directory` | Allows the generation dir to be overridden. Defaults to current dir
  |   `--targets`          | Targets are the parts of the application that you want to be generated.
  |                        | CHOOSE ANY OF:
  |                        |   `HTTP_MODELS` - Jackson annotated data classes to represent the schema objects defined in the input.
  |                        |   `CONTROLLERS` - Spring annotated HTTP controllers for each of the endpoints defined in the input.
  |                        |   `CLIENT` - Simple http rest client.
+ |                        |   `QUARKUS_REFLECTION_CONFIG` - This options generates the reflection-config.json file for quarkus integration projects
 
 ## Examples
 
@@ -78,7 +80,7 @@ tasks {
         )
     }
     withType<KotlinCompile> {
-        kotlinOptions.jvmTarget = "1.8"
+        kotlinOptions.jvmTarget = "11"
         dependsOn(generateCode)
     }
 }
diff --git a/build.gradle.kts b/build.gradle.kts
index d48adeb..72ef19f 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -100,7 +100,7 @@ tasks {
 
     withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
         kotlinOptions {
-            jvmTarget = "1.8"
+            jvmTarget = "11"
         }
         dependsOn(formatKotlin)
     }
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ba94df8..12d38de 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenOptions.kt b/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenOptions.kt
index d338159..dc927c8 100644
--- a/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenOptions.kt
+++ b/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenOptions.kt
@@ -19,6 +19,12 @@ enum class CodeGenerationType(val description: String, val requires: Set<Interna
             InternalCodeGenGenType.MODELS,
             InternalCodeGenGenType.CLIENT
         )
+    ),
+    QUARKUS_REFLECTION_CONFIG(
+        "This options generates the reflection-config.json file for quarkus integration projects",
+        setOf(
+            InternalCodeGenGenType.QUARKUS_REFLECTION_RESOURCE
+        )
     );
 
     override fun toString() = "`${super.toString()}` - $description"
@@ -29,17 +35,19 @@ enum class InternalCodeGenGenType {
     MODELS,
     CONTROLLERS,
     SERVICE_INTERFACES,
-    CLIENT
+    CLIENT,
+    QUARKUS_REFLECTION_RESOURCE
 }
 
 enum class ClientCodeGenOptionType(private val description: String) {
-    RESILIENCE4J("Generates a fault tolerance service for the client using the following library \"io.github.resilience4j:resilience4j-all:1.2.0\"");
+    RESILIENCE4J("Generates a fault tolerance service for the client using the following library \"io.github.resilience4j:resilience4j-all:+\"");
 
     override fun toString() = "`${super.toString()}` - $description"
 }
 
 enum class ModelCodeGenOptionType(val description: String) {
-    JAVA_SERIALIZATION("This option adds Java Serializable interface to the generated models");
+    JAVA_SERIALIZATION("This option adds Java Serializable interface to the generated models"),
+    QUARKUS_REFLECTION("This option adds @RegisterForReflection to the generated models. Requires dependency \"'io.quarkus:quarkus-core:+\"");
 
     override fun toString() = "`${super.toString()}` - $description"
 }
diff --git a/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenerator.kt b/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenerator.kt
index 51a6366..5472b94 100644
--- a/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenerator.kt
+++ b/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenerator.kt
@@ -3,16 +3,20 @@ package com.cjbooms.fabrikt.cli
 import com.cjbooms.fabrikt.cli.CodeGenerationType.CLIENT
 import com.cjbooms.fabrikt.cli.CodeGenerationType.CONTROLLERS
 import com.cjbooms.fabrikt.cli.CodeGenerationType.HTTP_MODELS
+import com.cjbooms.fabrikt.cli.CodeGenerationType.QUARKUS_REFLECTION_CONFIG
 import com.cjbooms.fabrikt.configurations.Packages
 import com.cjbooms.fabrikt.generators.client.OkHttpClientGenerator
 import com.cjbooms.fabrikt.generators.controller.SpringControllerGenerator
 import com.cjbooms.fabrikt.generators.model.JacksonModelGenerator
+import com.cjbooms.fabrikt.generators.model.QuarkusReflectionModelGenerator
 import com.cjbooms.fabrikt.generators.service.SpringServiceInterfaceGenerator
 import com.cjbooms.fabrikt.model.Clients
 import com.cjbooms.fabrikt.model.Controllers
 import com.cjbooms.fabrikt.model.GeneratedFile
 import com.cjbooms.fabrikt.model.KotlinSourceSet
 import com.cjbooms.fabrikt.model.Models
+import com.cjbooms.fabrikt.model.ResourceFile
+import com.cjbooms.fabrikt.model.ResourceSourceSet
 import com.cjbooms.fabrikt.model.Services
 import com.cjbooms.fabrikt.model.SourceApi
 import com.squareup.kotlinpoet.FileSpec
@@ -32,6 +36,7 @@ class CodeGenerator(
             CLIENT -> generateClient()
             CONTROLLERS -> generateControllers()
             HTTP_MODELS -> generateModels()
+            QUARKUS_REFLECTION_CONFIG -> generateQuarkusReflectionResource()
         }
 
     private fun generateModels(): Collection<GeneratedFile> = sourceSet(models().files)
@@ -47,11 +52,18 @@ class CodeGenerator(
         return sourceSet(client().files).plus(lib).plus(sourceSet(models().files))
     }
 
+    private fun generateQuarkusReflectionResource(): Collection<GeneratedFile> = resourceSet(resources(models()))
+
     private fun sourceSet(fileSpec: Collection<FileSpec>) = setOf(KotlinSourceSet(fileSpec))
 
+    private fun resourceSet(resFiles: Collection<ResourceFile>) = setOf(ResourceSourceSet(resFiles))
+
     private fun models(): Models =
         JacksonModelGenerator(packages, sourceApi, modelOptions).generate()
 
+    private fun resources(models: Models): List<ResourceFile> =
+        listOfNotNull(QuarkusReflectionModelGenerator(models, modelOptions).generate())
+
     private fun services(): Services =
         SpringServiceInterfaceGenerator(packages, sourceApi, models().models).generate()
 
diff --git a/src/main/kotlin/com/cjbooms/fabrikt/generators/model/JacksonModelGenerator.kt b/src/main/kotlin/com/cjbooms/fabrikt/generators/model/JacksonModelGenerator.kt
index 4b5e890..4710471 100644
--- a/src/main/kotlin/com/cjbooms/fabrikt/generators/model/JacksonModelGenerator.kt
+++ b/src/main/kotlin/com/cjbooms/fabrikt/generators/model/JacksonModelGenerator.kt
@@ -3,6 +3,7 @@ package com.cjbooms.fabrikt.generators.model
 import com.cjbooms.fabrikt.cli.ModelCodeGenOptionType
 import com.cjbooms.fabrikt.configurations.Packages
 import com.cjbooms.fabrikt.generators.ClassType
+import com.cjbooms.fabrikt.generators.GeneratorUtils.toClassName
 import com.cjbooms.fabrikt.generators.PropertyUtils.addToClass
 import com.cjbooms.fabrikt.generators.TypeFactory.createList
 import com.cjbooms.fabrikt.generators.TypeFactory.createMapOfMapsStringToStringAny
@@ -30,6 +31,7 @@ import com.cjbooms.fabrikt.util.KaizenParserExtensions.safeName
 import com.cjbooms.fabrikt.util.KaizenParserExtensions.toMapValueClassName
 import com.cjbooms.fabrikt.util.NormalisedString.toModelClassName
 import com.reprezen.kaizen.oasparser.model3.Discriminator
+import com.squareup.kotlinpoet.AnnotationSpec
 import com.squareup.kotlinpoet.ClassName
 import com.squareup.kotlinpoet.CodeBlock
 import com.squareup.kotlinpoet.FunSpec
@@ -187,6 +189,7 @@ class JacksonModelGenerator(
                     .addParameter("value", String::class)
                     .build()
             )
+            .addQuarkusReflectionAnnotation()
         enum.entries.forEach {
             classBuilder.addEnumConstant(
                 it.toUpperCase(),
@@ -222,7 +225,8 @@ class JacksonModelGenerator(
                     )
                 )
                 .addModifiers(KModifier.DATA)
-                .addSerializableInterface(),
+                .addSerializableInterface()
+                .addQuarkusReflectionAnnotation(),
             ClassType.VANILLA_MODEL
         )
 
@@ -249,7 +253,7 @@ class JacksonModelGenerator(
                 )
             }
             .toMap()
-        classBuilder.addAnnotation(polymorphicSubTypes(subTypes))
+        classBuilder.addAnnotation(polymorphicSubTypes(subTypes)).addQuarkusReflectionAnnotation()
 
         return properties.addToClass(classBuilder,
             ClassType.SUPER_MODEL
@@ -271,6 +275,7 @@ class JacksonModelGenerator(
                 )
                 .addModifiers(KModifier.DATA)
                 .addSerializableInterface()
+                .addQuarkusReflectionAnnotation()
                 .superclass(
                     toModelType(
                         packages.base,
@@ -301,14 +306,15 @@ class JacksonModelGenerator(
         return classBuilder.primaryConstructor(constructorBuilder.build()).build()
     }
 
-    private fun TypeSpec.Builder.addSerializableInterface(): TypeSpec.Builder =
-        ifJavaSerializableIsEnabled(this) {
-            it.addSuperinterface(Serializable::class)
-        }
-
-    private fun ifJavaSerializableIsEnabled(builder: TypeSpec.Builder, block: (TypeSpec.Builder) -> TypeSpec.Builder): TypeSpec.Builder =
+    private fun TypeSpec.Builder.addSerializableInterface(): TypeSpec.Builder {
         if (options.any { it == ModelCodeGenOptionType.JAVA_SERIALIZATION })
-            block(builder)
-        else
-            builder
+            this.addSuperinterface(Serializable::class)
+        return this
+    }
+
+    private fun TypeSpec.Builder.addQuarkusReflectionAnnotation(): TypeSpec.Builder {
+        if (options.any { it == ModelCodeGenOptionType.QUARKUS_REFLECTION })
+            this.addAnnotation(AnnotationSpec.builder("RegisterForReflection".toClassName("io.quarkus.runtime.annotations")).build())
+        return this
+    }
 }
diff --git a/src/main/kotlin/com/cjbooms/fabrikt/generators/model/QuarkusReflectionModelGenerator.kt b/src/main/kotlin/com/cjbooms/fabrikt/generators/model/QuarkusReflectionModelGenerator.kt
new file mode 100644
index 0000000..017cf39
--- /dev/null
+++ b/src/main/kotlin/com/cjbooms/fabrikt/generators/model/QuarkusReflectionModelGenerator.kt
@@ -0,0 +1,28 @@
+package com.cjbooms.fabrikt.generators.model
+
+import com.cjbooms.fabrikt.cli.ModelCodeGenOptionType
+import com.cjbooms.fabrikt.model.Models
+import com.cjbooms.fabrikt.model.QuarkusReflectionModel
+import com.cjbooms.fabrikt.model.ResourceFile
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.SerializationFeature
+import com.fasterxml.jackson.module.kotlin.registerKotlinModule
+
+class QuarkusReflectionModelGenerator(
+    private val models: Models,
+    private val options: Set<ModelCodeGenOptionType> = emptySet()
+) {
+    companion object {
+        const val RESOURCE_FILE_NAME = "reflection-config.json"
+    }
+    private val objectMapper: ObjectMapper = ObjectMapper().registerKotlinModule().enable(SerializationFeature.INDENT_OUTPUT)
+
+    fun generate(): ResourceFile? {
+        return if (options.any { it == ModelCodeGenOptionType.QUARKUS_REFLECTION }) {
+            val reflectionConfigs = models.models.map {
+                QuarkusReflectionModel(it.className.canonicalName)
+            }
+            ResourceFile(objectMapper.writeValueAsString(reflectionConfigs).byteInputStream(), RESOURCE_FILE_NAME)
+        } else null
+    }
+}
diff --git a/src/main/kotlin/com/cjbooms/fabrikt/model/Destinations.kt b/src/main/kotlin/com/cjbooms/fabrikt/model/Destinations.kt
index 8a264c3..9d96928 100644
--- a/src/main/kotlin/com/cjbooms/fabrikt/model/Destinations.kt
+++ b/src/main/kotlin/com/cjbooms/fabrikt/model/Destinations.kt
@@ -7,8 +7,8 @@ object Destinations {
     val PATH_ROOT: Path = Paths.get("")
 
     const val MAIN_KT_SOURCE = "src/main/kotlin/"
+    const val MAIN_RESOURCES = "src/main/resources"
     val MAIN_KT_SRC: Path = PATH_ROOT.resolve(MAIN_KT_SOURCE)
-    private const val MAIN_RESOURCES_STRING = "src/main/resources"
 
     fun servicesPackage(basePackage: String): String = "$basePackage.service"
     fun modelsPackage(basePackage: String): String = "$basePackage.models"
diff --git a/src/main/kotlin/com/cjbooms/fabrikt/model/GeneratedFiles.kt b/src/main/kotlin/com/cjbooms/fabrikt/model/GeneratedFiles.kt
index 45d7c84..21c524e 100644
--- a/src/main/kotlin/com/cjbooms/fabrikt/model/GeneratedFiles.kt
+++ b/src/main/kotlin/com/cjbooms/fabrikt/model/GeneratedFiles.kt
@@ -1,8 +1,11 @@
 package com.cjbooms.fabrikt.model
 
 import com.cjbooms.fabrikt.model.Destinations.MAIN_KT_SOURCE
+import com.cjbooms.fabrikt.model.Destinations.MAIN_RESOURCES
+import com.cjbooms.fabrikt.util.FileUtils.writeFileTo
 import com.squareup.kotlinpoet.FileSpec
 import java.io.File
+import java.io.InputStream
 import java.nio.file.Path
 
 sealed class GeneratedFile {
@@ -16,6 +19,22 @@ data class SimpleFile(val path: Path, val content: String) : GeneratedFile() {
     }
 }
 
+data class ResourceFile(
+    val inputStream: InputStream,
+    val fileName: String
+) : GeneratedFile() {
+    override fun writeFileTo(outputDir: File) = inputStream.writeFileTo(Path.of(outputDir.absolutePath, fileName))
+}
+
 data class KotlinSourceSet(val files: Collection<FileSpec>, val srcDir: String = MAIN_KT_SOURCE) : GeneratedFile() {
     override fun writeFileTo(outputDir: File) = files.distinct().forEach { it.writeTo(outputDir.resolve(srcDir)) }
 }
+
+data class ResourceSourceSet(val files: Collection<ResourceFile>, val srcDir: String = MAIN_RESOURCES) : GeneratedFile() {
+    override fun writeFileTo(outputDir: File) {
+        with(outputDir.resolve(srcDir)) {
+            mkdirs()
+            files.forEach { it.writeFileTo(this) }
+        }
+    }
+}
diff --git a/src/main/kotlin/com/cjbooms/fabrikt/model/QuarkusReflectionModel.kt b/src/main/kotlin/com/cjbooms/fabrikt/model/QuarkusReflectionModel.kt
new file mode 100644
index 0000000..1dffd39
--- /dev/null
+++ b/src/main/kotlin/com/cjbooms/fabrikt/model/QuarkusReflectionModel.kt
@@ -0,0 +1,11 @@
+package com.cjbooms.fabrikt.model
+
+data class QuarkusReflectionModel(
+    val name: String,
+    val allDeclaredConstructors: Boolean = true,
+    val allPublicConstructors: Boolean = true,
+    val allDeclaredMethods: Boolean = true,
+    val allPublicMethods: Boolean = true,
+    val allDeclaredFields: Boolean = true,
+    val allPublicFields: Boolean = true
+)
diff --git a/src/main/kotlin/com/cjbooms/fabrikt/util/FileUtils.kt b/src/main/kotlin/com/cjbooms/fabrikt/util/FileUtils.kt
new file mode 100644
index 0000000..8fbfd60
--- /dev/null
+++ b/src/main/kotlin/com/cjbooms/fabrikt/util/FileUtils.kt
@@ -0,0 +1,11 @@
+package com.cjbooms.fabrikt.util
+
+import java.io.InputStream
+import java.nio.file.Path
+
+object FileUtils {
+
+    fun InputStream.writeFileTo(path: Path) {
+        path.toFile().outputStream().use { this.copyTo(it) }
+    }
+}


Test patch:
diff --git a/src/test/kotlin/com/cjbooms/fabrikt/generators/ModelGeneratorTest.kt b/src/test/kotlin/com/cjbooms/fabrikt/generators/ModelGeneratorTest.kt
index 3ddd49d..5162747 100644
--- a/src/test/kotlin/com/cjbooms/fabrikt/generators/ModelGeneratorTest.kt
+++ b/src/test/kotlin/com/cjbooms/fabrikt/generators/ModelGeneratorTest.kt
@@ -96,6 +96,19 @@ class ModelGeneratorTest {
         assertThat(models).isEqualTo(expectedModels)
     }
 
+    @Test
+    fun `quarkus reflection models are generated from a full API definition when the quarkus-reflection option is set`() {
+        val basePackage = "examples.quarkusReflectionModels"
+        val spec = javaClass.getResource("/examples/quarkusReflectionModels/api.yaml").readText()
+        val expectedModels = javaClass.getResource("/examples/quarkusReflectionModels/models/Models.kt").readText()
+
+        val models = JacksonModelGenerator(Packages(basePackage), SourceApi(spec), setOf(ModelCodeGenOptionType.QUARKUS_REFLECTION))
+                .generate()
+                .toSingleFile()
+
+        assertThat(models).isEqualTo(expectedModels)
+    }
+
     private fun Models.toSingleFile(): String {
         val destPackage = if (models.isNotEmpty()) models.first().destinationPackage else ""
         val singleFileBuilder = FileSpec.builder(destPackage, "dummyFilename")
diff --git a/src/test/kotlin/com/cjbooms/fabrikt/generators/ResourceGeneratorTest.kt b/src/test/kotlin/com/cjbooms/fabrikt/generators/ResourceGeneratorTest.kt
new file mode 100644
index 0000000..9dd3493
--- /dev/null
+++ b/src/test/kotlin/com/cjbooms/fabrikt/generators/ResourceGeneratorTest.kt
@@ -0,0 +1,41 @@
+package com.cjbooms.fabrikt.generators
+
+import com.cjbooms.fabrikt.cli.ModelCodeGenOptionType
+import com.cjbooms.fabrikt.configurations.Packages
+import com.cjbooms.fabrikt.generators.model.JacksonModelGenerator
+import com.cjbooms.fabrikt.generators.model.QuarkusReflectionModelGenerator
+import com.cjbooms.fabrikt.model.ResourceFile
+import com.cjbooms.fabrikt.model.SourceApi
+import java.nio.file.Paths
+import java.util.stream.Stream
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.TestInstance
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.MethodSource
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+class ResourceGeneratorTest {
+
+    private fun testCases(): Stream<String> = Stream.of(
+        "githubApi"
+    )
+
+    @ParameterizedTest
+    @MethodSource("testCases")
+    fun `correct quarkus reflection resource is generated for different OpenApi Specifications`(testCaseName: String) {
+        print("Testcase: $testCaseName")
+        val basePackage = "examples.$testCaseName"
+        val apiLocation = javaClass.getResource("/examples/$testCaseName/api.yaml")
+        val sourceApi = SourceApi(apiLocation.readText(), baseDir = Paths.get(apiLocation.toURI()))
+        val expectedResource = javaClass.getResource("/examples/$testCaseName/resources/reflection-config.json").readText()
+        val options = setOf(ModelCodeGenOptionType.QUARKUS_REFLECTION)
+
+        val models = JacksonModelGenerator(Packages(basePackage), sourceApi, options).generate()
+
+        val resources = QuarkusReflectionModelGenerator(models, options).generate()?.toSingleFile()
+
+        assertThat(resources).isEqualTo(expectedResource)
+    }
+
+    private fun ResourceFile.toSingleFile(): String = String(inputStream.readAllBytes())
+}
diff --git a/src/test/resources/examples/githubApi/resources/reflection-config.json b/src/test/resources/examples/githubApi/resources/reflection-config.json
new file mode 100644
index 0000000..06bb896
--- /dev/null
+++ b/src/test/resources/examples/githubApi/resources/reflection-config.json
@@ -0,0 +1,137 @@
+[ {
+  "name" : "examples.githubApi.models.BulkEntityDetails",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.EntityDetails",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.EventResults",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.Event",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.ContributorQueryResult",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.OrganisationQueryResult",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.RepositoryQueryResult",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.PullRequestQueryResult",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.Contributor",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.Status",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.Organisation",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.Repository",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.Visibility",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.PullRequest",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.Author",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.Webhook",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+}, {
+  "name" : "examples.githubApi.models.StatusQueryParam",
+  "allDeclaredConstructors" : true,
+  "allPublicConstructors" : true,
+  "allDeclaredMethods" : true,
+  "allPublicMethods" : true,
+  "allDeclaredFields" : true,
+  "allPublicFields" : true
+} ]
\ No newline at end of file
diff --git a/src/test/resources/examples/quarkusReflectionModels/api.yaml b/src/test/resources/examples/quarkusReflectionModels/api.yaml
new file mode 100644
index 0000000..2fa8b07
--- /dev/null
+++ b/src/test/resources/examples/quarkusReflectionModels/api.yaml
@@ -0,0 +1,287 @@
+openapi: "3.0.0"
+info:
+  title: "API Example"
+  version: "1.0"
+paths:
+  /example-path-1:
+    get:
+      summary: "GET example path 1"
+      parameters:
+        - $ref: "#/components/parameters/QueryParam1"
+        - $ref: "#/components/parameters/QueryParam2"
+      responses:
+        200:
+          description: "successful operation"
+          headers:
+            Cache-Control:
+              $ref: "#/components/headers/CacheControl"
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/QueryResult"
+
+    post:
+      summary: "POST example path 1"
+      parameters:
+        - $ref: "#/components/parameters/QueryParam1"
+      requestBody:
+        $ref: "#/components/requestBodies/PostBody"
+      responses:
+        201:
+          headers:
+            Location:
+              $ref: "#/components/headers/Location"
+          description: "Successful operation"
+
+  /example-path-2/{path_param}:
+    get:
+      summary: "GET example path 2"
+      parameters:
+        - $ref: "#/components/parameters/PathParam"
+        - $ref: "#/components/parameters/QueryParam2"
+        - $ref: "#/components/parameters/IfNoneMatch"
+      responses:
+        200:
+          description: "successful operation"
+          headers:
+            Cache-Control:
+              $ref: "#/components/headers/CacheControl"
+          content:
+            application/json:
+              schema:
+                discriminator:
+                  propertyName: "model_type"
+                oneOf:
+                  - $ref: "#/components/schemas/FirstModel"
+                  - $ref: "#/components/schemas/SecondModel"
+                  - $ref: "#/components/schemas/ThirdModel"
+        304:
+          description: "Returned in conjunction with use of the If-None-Match header.\
+            \ Indicates that the resource as described by the passed etag value has\
+            \ not changed"
+    head:
+      summary: "HEAD example path 2"
+      parameters:
+        - $ref: "#/components/parameters/PathParam"
+        - $ref: "#/components/parameters/QueryParam3"
+        - $ref: "#/components/parameters/IfNoneMatch"
+      responses:
+        200:
+          description: "Record Exists"
+        304:
+          description: "Returned in conjunction with use of the If-None-Match header.\
+            \ Indicates that the resource as described by the passed etag value has\
+            \ not changed"
+
+    put:
+      summary: "PUT example path 2"
+      parameters:
+        - $ref: "#/components/parameters/PathParam"
+        - $ref: "#/components/parameters/IfMatch"
+      requestBody:
+        $ref: "#/components/requestBodies/PutBody"
+      responses:
+        204:
+          description: "Operation successful"
+
+  /example-path-3/{path_param}/subresource:
+    put:
+      summary: "PUT example path 3"
+      parameters:
+        - $ref: "#/components/parameters/QueryParam1"
+        - $ref: "#/components/parameters/PathParam"
+        - $ref: "#/components/parameters/IfMatch"
+      requestBody:
+        $ref: "#/components/requestBodies/PutBody"
+      responses:
+        204:
+          description: "Operation successful"
+
+components:
+  parameters:
+    PathParam:
+      name: "path_param"
+      description: "The resource id"
+      in: "path"
+      required: true
+      schema:
+        type: "string"
+
+    IfMatch:
+      name: "If-Match"
+      description: "The RFC7232 If-Match header field"
+      in: "header"
+      schema:
+        type: "string"
+      required: true
+
+    IfNoneMatch:
+      name: "If-None-Match"
+      description: "The RFC7232 If-None-Match header field"
+      in: "header"
+      schema:
+        type: "string"
+      required: false
+
+    QueryParam1:
+      name: "query_param1"
+      in: "query"
+      schema:
+        type: "array"
+        items:
+          type: "string"
+      required: false
+      allowEmptyValue: false
+
+    QueryParam2:
+      name: "query_param2"
+      in: "query"
+      schema:
+        type: integer
+      required: false
+
+    QueryParam3:
+      name: "query_param3"
+      in: "query"
+      schema:
+        type: boolean
+      required: false
+
+  headers:
+    Location:
+      description: "The Location header indicates the URL of a newly created resource"
+      schema:
+        type: "string"
+
+    CacheControl:
+      description: "The RFC7234 Cache Control header"
+      schema:
+        type: "string"
+      example: "must-revalidate, max-age=5"
+
+  requestBodies:
+    PostBody:
+      required: true
+      content:
+        application/json:
+          schema:
+            discriminator:
+              propertyName: "model_type"
+            oneOf:
+              - $ref: "#/components/schemas/FirstModel"
+              - $ref: "#/components/schemas/SecondModel"
+              - $ref: "#/components/schemas/ThirdModel"
+    PutBody:
+      required: true
+      content:
+        application/json:
+          schema:
+            $ref: "#/components/schemas/FirstModel"
+
+
+  schemas:
+    QueryResult:
+      type: "object"
+      required:
+        - "items"
+      properties:
+        items:
+          type: "array"
+          minItems: 0
+          items:
+            oneOf:
+              - $ref: "#/components/schemas/FirstModel"
+              - $ref: "#/components/schemas/SecondModel"
+              - $ref: "#/components/schemas/ThirdModel"
+    Content:
+      type: "object"
+      required:
+        - "id"
+        - "attr_1"
+        - "attr_2"
+        - "attr_3"
+        - "etag"
+        - "model_type"
+      properties:
+        id:
+          description: "The unique resource id"
+          type: "string"
+          readOnly: true
+        first_attr:
+          description: "The attribute 1"
+          type: "string"
+          format: "date-time"
+          example: "2016-01-27T10:52:46.406Z"
+          readOnly: true
+        second_attr:
+          description: "The attribute 2"
+          type: "string"
+          readOnly: true
+        third_attr:
+          type: "string"
+          x-extensible-enum:
+            - "enum_type_1"
+            - "enum_type_2"
+          description: "Enum types for attribute 3"
+          example: "enum_type_2"
+        etag:
+          type: "string"
+          description: "Etag value to be used in conjunction with If-Match headers for optimistic locking purposes"
+          readOnly: true
+        model_type:
+          type: "string"
+          description: "The model discrimination type"
+          x-extensible-enum:
+            - "first_model"
+            - "second_model"
+            - "third_model"
+          example: "third_model"
+      discriminator:
+        propertyName: "model_type"
+        mapping:
+          first_model: "#/components/schemas/FirstModel"
+          second_model: "#/components/schemas/SecondModel"
+          third_model: "#/components/schemas/ThirdModel"
+
+    FirstModel:
+      allOf:
+        - $ref: "#/components/schemas/Content"
+        - type: "object"
+          properties:
+            extra_first_attr:
+              description: "The attribute 1 for model 1"
+              type: array
+              items:
+                type: "string"
+                minItems: 1
+                maxItems: 10
+              readOnly: true
+    SecondModel:
+      allOf:
+        - $ref: "#/components/schemas/Content"
+        - type: "object"
+          properties:
+            extra_first_attr:
+              description: "The attribute 1 for model 2"
+              type: "string"
+              readOnly: true
+            extra_second_attr:
+              description: "The attribute 2 for model 2"
+              type: boolean
+              readOnly: true
+
+    ThirdModel:
+      allOf:
+        - $ref: "#/components/schemas/Content"
+        - type: "object"
+          properties:
+            extra_first_attr:
+              description: "The attribute 1 for model 3"
+              type: "string"
+              format: "date-time"
+              example: "2016-01-27T10:52:46.406Z"
+              readOnly: true
+            extra_second_attr:
+              description: "The attribute 2 for model 3"
+              type: integer
+              readOnly: true
diff --git a/src/test/resources/examples/quarkusReflectionModels/models/Models.kt b/src/test/resources/examples/quarkusReflectionModels/models/Models.kt
new file mode 100644
index 0000000..1a08bff
--- /dev/null
+++ b/src/test/resources/examples/quarkusReflectionModels/models/Models.kt
@@ -0,0 +1,161 @@
+package examples.quarkusReflectionModels.models
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.fasterxml.jackson.annotation.JsonSubTypes
+import com.fasterxml.jackson.annotation.JsonTypeInfo
+import com.fasterxml.jackson.annotation.JsonValue
+import io.quarkus.runtime.annotations.RegisterForReflection
+import java.time.OffsetDateTime
+import javax.validation.Valid
+import javax.validation.constraints.NotNull
+import javax.validation.constraints.Size
+import kotlin.Boolean
+import kotlin.Int
+import kotlin.String
+import kotlin.collections.List
+
+@RegisterForReflection
+data class QueryResult(
+    @param:JsonProperty("items")
+    @get:JsonProperty("items")
+    @get:NotNull
+    @get:Size(min = 0)
+    @get:Valid
+    val items: List<Content>
+)
+
+@JsonTypeInfo(
+    use = JsonTypeInfo.Id.NAME,
+    include = JsonTypeInfo.As.EXISTING_PROPERTY,
+    property = "model_type",
+    visible = true
+)
+@JsonSubTypes(
+    JsonSubTypes.Type(
+        value = FirstModel::class,
+        name =
+            "first_model"
+    ),
+    JsonSubTypes.Type(
+        value = SecondModel::class,
+        name =
+            "second_model"
+    ),
+    JsonSubTypes.Type(value = ThirdModel::class, name = "third_model")
+)
+@RegisterForReflection
+sealed class Content(
+    open val id: String? = null,
+    open val firstAttr: OffsetDateTime? = null,
+    open val secondAttr: String? = null,
+    open val thirdAttr: ThirdAttr? = null,
+    open val etag: String? = null
+) {
+    abstract val modelType: ModelType
+}
+
+@RegisterForReflection
+enum class ThirdAttr(
+    @JsonValue
+    val value: String
+) {
+    ENUM_TYPE_1("enum_type_1"),
+
+    ENUM_TYPE_2("enum_type_2");
+}
+
+@RegisterForReflection
+enum class ModelType(
+    @JsonValue
+    val value: String
+) {
+    FIRST_MODEL("first_model"),
+
+    SECOND_MODEL("second_model"),
+
+    THIRD_MODEL("third_model");
+}
+
+@RegisterForReflection
+data class FirstModel(
+    @param:JsonProperty("id")
+    @get:JsonProperty("id")
+    override val id: String? = null,
+    @param:JsonProperty("first_attr")
+    @get:JsonProperty("first_attr")
+    override val firstAttr: OffsetDateTime? = null,
+    @param:JsonProperty("second_attr")
+    @get:JsonProperty("second_attr")
+    override val secondAttr: String? = null,
+    @param:JsonProperty("third_attr")
+    @get:JsonProperty("third_attr")
+    override val thirdAttr: ThirdAttr? = null,
+    @param:JsonProperty("etag")
+    @get:JsonProperty("etag")
+    override val etag: String? = null,
+    @param:JsonProperty("extra_first_attr")
+    @get:JsonProperty("extra_first_attr")
+    val extraFirstAttr: List<String>? = null
+) : Content(id, firstAttr, secondAttr, thirdAttr, etag) {
+    @get:JsonProperty("model_type")
+    @get:NotNull
+    override val modelType: ModelType = ModelType.FIRST_MODEL
+}
+
+@RegisterForReflection
+data class SecondModel(
+    @param:JsonProperty("id")
+    @get:JsonProperty("id")
+    override val id: String? = null,
+    @param:JsonProperty("first_attr")
+    @get:JsonProperty("first_attr")
+    override val firstAttr: OffsetDateTime? = null,
+    @param:JsonProperty("second_attr")
+    @get:JsonProperty("second_attr")
+    override val secondAttr: String? = null,
+    @param:JsonProperty("third_attr")
+    @get:JsonProperty("third_attr")
+    override val thirdAttr: ThirdAttr? = null,
+    @param:JsonProperty("etag")
+    @get:JsonProperty("etag")
+    override val etag: String? = null,
+    @param:JsonProperty("extra_first_attr")
+    @get:JsonProperty("extra_first_attr")
+    val extraFirstAttr: String? = null,
+    @param:JsonProperty("extra_second_attr")
+    @get:JsonProperty("extra_second_attr")
+    val extraSecondAttr: Boolean? = null
+) : Content(id, firstAttr, secondAttr, thirdAttr, etag) {
+    @get:JsonProperty("model_type")
+    @get:NotNull
+    override val modelType: ModelType = ModelType.SECOND_MODEL
+}
+
+@RegisterForReflection
+data class ThirdModel(
+    @param:JsonProperty("id")
+    @get:JsonProperty("id")
+    override val id: String? = null,
+    @param:JsonProperty("first_attr")
+    @get:JsonProperty("first_attr")
+    override val firstAttr: OffsetDateTime? = null,
+    @param:JsonProperty("second_attr")
+    @get:JsonProperty("second_attr")
+    override val secondAttr: String? = null,
+    @param:JsonProperty("third_attr")
+    @get:JsonProperty("third_attr")
+    override val thirdAttr: ThirdAttr? = null,
+    @param:JsonProperty("etag")
+    @get:JsonProperty("etag")
+    override val etag: String? = null,
+    @param:JsonProperty("extra_first_attr")
+    @get:JsonProperty("extra_first_attr")
+    val extraFirstAttr: OffsetDateTime? = null,
+    @param:JsonProperty("extra_second_attr")
+    @get:JsonProperty("extra_second_attr")
+    val extraSecondAttr: Int? = null
+) : Content(id, firstAttr, secondAttr, thirdAttr, etag) {
+    @get:JsonProperty("model_type")
+    @get:NotNull
+    override val modelType: ModelType = ModelType.THIRD_MODEL
+}