Repository: tipsy/javalin
Pull request #889 (2020-02-29 18:38:16)

[openapi] Add beans in query params to components
#884

tipsy: Thanks @28Smiles !

Patch:
diff --git a/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedContent.kt b/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedContent.kt
index d485ba3b..eeac36dd 100644
--- a/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedContent.kt
+++ b/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedContent.kt
@@ -83,7 +83,7 @@ class DocumentedContent @JvmOverloads constructor(
     )
 
     fun isNonRefType(): Boolean = if (schema == null) {
-        fromType in nonRefTypes
+        fromType.isNonRefType()
     } else {
         true
     }
@@ -109,6 +109,8 @@ fun Class<*>.guessContentType(): String =
             else -> "application/json"
         }
 
+fun Class<*>.isNonRefType(): Boolean = this in nonRefTypes
+
 /**
  * A set of types that should be inlined, instead of creating a reference, in the OpenApi documentation
  */
diff --git a/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedParameter.kt b/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedParameter.kt
index c3212f5b..028a54a7 100644
--- a/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedParameter.kt
+++ b/src/main/java/io/javalin/plugin/openapi/dsl/DocumentedParameter.kt
@@ -1,6 +1,9 @@
 package io.javalin.plugin.openapi.dsl
 
 import io.javalin.plugin.openapi.external.findSchema
+import io.javalin.plugin.openapi.external.mediaTypeRef
+import io.javalin.plugin.openapi.external.schema
+import io.swagger.v3.oas.models.Components
 import io.swagger.v3.oas.models.media.ArraySchema
 import io.swagger.v3.oas.models.parameters.Parameter
 
@@ -13,11 +16,25 @@ class DocumentedParameter(
 fun Parameter.applyDocumentedParameter(documentedParameter: DocumentedParameter) {
     `in` = documentedParameter.`in`
     name = documentedParameter.name
-    schema = findSchema(documentedParameter.type)?.main
+    schema = if (documentedParameter.type.isNonRefType()) {
+        findSchema(documentedParameter.type)?.main
+    } else {
+        mediaTypeRef(documentedParameter.type).schema
+    }
 }
 
 fun Parameter.applyRepeatableDocumentedParameter(documentedParameter: DocumentedParameter) {
     `in` = documentedParameter.`in`
     name = documentedParameter.name
-    schema = findSchema(documentedParameter.type)?.main?.let { ArraySchema().items(it) }
+    schema = if (documentedParameter.type.isNonRefType()) {
+        findSchema(documentedParameter.type)?.main?.let { ArraySchema().items(it) }
+    } else {
+        ArraySchema().items(mediaTypeRef(documentedParameter.type).schema)
+    }
+}
+
+fun Components.applyDocumentedParameter(documentedParameter: DocumentedParameter) {
+    if (!documentedParameter.type.isNonRefType()) {
+        this.schema(documentedParameter.type)
+    }
 }
diff --git a/src/main/java/io/javalin/plugin/openapi/dsl/OpenApiDocumentation.kt b/src/main/java/io/javalin/plugin/openapi/dsl/OpenApiDocumentation.kt
index 381a29fb..d52ba489 100644
--- a/src/main/java/io/javalin/plugin/openapi/dsl/OpenApiDocumentation.kt
+++ b/src/main/java/io/javalin/plugin/openapi/dsl/OpenApiDocumentation.kt
@@ -89,6 +89,7 @@ class OpenApiDocumentation {
     @JvmOverloads
     fun param(documentedParameter: DocumentedParameter, isRepeatable: Boolean = false, openApiUpdater: OpenApiUpdater<Parameter>? = null) = apply {
         val parameterUpdaterList = parameterUpdaterListMapping.getOrSetDefault(documentedParameter.name, mutableListOf())
+        componentsUpdaterList.add { it.applyDocumentedParameter(documentedParameter) }
         parameterUpdaterList.add {
             if (isRepeatable)
                 it.applyRepeatableDocumentedParameter(documentedParameter)


Test patch:
diff --git a/src/test/java/io/javalin/openapi/TestOpenApiAnnotations.kt b/src/test/java/io/javalin/openapi/TestOpenApiAnnotations.kt
index 45b4a977..a411a586 100644
--- a/src/test/java/io/javalin/openapi/TestOpenApiAnnotations.kt
+++ b/src/test/java/io/javalin/openapi/TestOpenApiAnnotations.kt
@@ -274,6 +274,19 @@ fun getBodyOneOfHandler(ctx: Context) {
 fun getResponseOneOfHandler(ctx: Context) {
 }
 
+@OpenApi(
+        queryParams = [
+            OpenApiParam("user", type = User::class)
+        ],
+        responses = [
+            OpenApiResponse("200", content = [
+                OpenApiContent(from = User::class)
+            ])
+        ]
+)
+fun getQueryParamBeanHandler(ctx: Context) {
+}
+
 @OpenApi(
         queryParams = [
             OpenApiParam("id", Long::class, isRepeatable = true)
@@ -355,6 +368,13 @@ class TestOpenApiAnnotations {
         }.assertEqualTo(simpleExample)
     }
 
+    @Test
+    fun `createOpenApiSchema with query param bean`() {
+        val actual = extractSchemaForTest {
+            it.get("/test", ::getQueryParamBeanHandler)
+        }.assertEqualTo(queryBeanExample)
+    }
+
     @Test
     fun `createOpenApiSchema with repeatable query param`() {
         extractSchemaForTest {
diff --git a/src/test/java/io/javalin/openapi/json.kt b/src/test/java/io/javalin/openapi/json.kt
index 867bfa94..2c3aaad7 100644
--- a/src/test/java/io/javalin/openapi/json.kt
+++ b/src/test/java/io/javalin/openapi/json.kt
@@ -83,6 +83,73 @@ val complexExampleUsersGetResponsesJson = """
 }
 """.formatJson()
 
+@Language("JSON")
+val queryBeanExample = """
+    {
+      "openapi" : "3.0.1",
+      "info" : {
+        "title" : "Example",
+        "version" : "1.0.0"
+      },
+      "paths" : {
+        "/test" : {
+          "get" : {
+            "summary" : "Get test",
+            "operationId" : "getTest",
+            "parameters" : [ {
+              "name" : "user",
+              "in" : "query",
+              "schema" : {
+                "$ref" : "#/components/schemas/User"
+              }
+            } ],
+            "responses" : {
+              "200" : {
+                "description" : "OK",
+                "content" : {
+                  "application/json" : {
+                    "schema" : {
+                      "$ref" : "#/components/schemas/User"
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      },
+      "components" : {
+        "schemas" : {
+          "Address" : {
+            "required" : [ "number", "street" ],
+            "type" : "object",
+            "properties" : {
+              "street" : {
+                "type" : "string"
+              },
+              "number" : {
+                "type" : "integer",
+                "format" : "int32"
+              }
+            }
+          },
+          "User" : {
+            "required" : [ "name" ],
+            "type" : "object",
+            "properties" : {
+              "name" : {
+                "type" : "string"
+              },
+              "address" : {
+                "$ref" : "#/components/schemas/Address"
+              }
+            }
+          }
+        }
+      }
+    }
+""".formatJson()
+
 @Language("JSON")
 val simpleExample = """
   {