You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/11/11 10:05:23 UTC

[camel] branch camel-3.18.x updated: [CAMEL-18711] OpenAPI Schema references not generating correctly (#8708)

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch camel-3.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-3.18.x by this push:
     new d69cf258747 [CAMEL-18711] OpenAPI Schema references not generating correctly (#8708)
d69cf258747 is described below

commit d69cf25874791de36eeafb0cfdaf2fec5520c8fc
Author: iliya-gr <il...@gmail.com>
AuthorDate: Fri Nov 11 12:58:44 2022 +0300

    [CAMEL-18711] OpenAPI Schema references not generating correctly (#8708)
    
    Add custom ModelResolver to generate x-className extension
    Use x-className extension to make correct type reference
---
 .../apache/camel/openapi/RestModelConverters.java  |  75 ++++++---
 .../apache/camel/openapi/RestOpenApiReader.java    |  32 +++-
 .../org/apache/camel/openapi/ComplexTypesTest.java |  59 ++++++++
 .../model/CustomDataWithSchemaAnnotation.java      |  29 ++++
 ...mpleComplexRequestTypeWithSchemaAnnotation.java |  74 +++++++++
 ...pleComplexResponseTypeWithSchemaAnnotation.java |  62 ++++++++
 ...ForComplexTypesRequestWithSchemaAnnotation.json | 158 +++++++++++++++++++
 ...orComplexTypesResponseWithSchemaAnnotation.json | 101 +++++++++++++
 ...ForComplexTypesRequestWithSchemaAnnotation.json | 168 +++++++++++++++++++++
 ...orComplexTypesResponseWithSchemaAnnotation.json | 111 ++++++++++++++
 10 files changed, 845 insertions(+), 24 deletions(-)

diff --git a/components/camel-openapi-java/src/main/java/org/apache/camel/openapi/RestModelConverters.java b/components/camel-openapi-java/src/main/java/org/apache/camel/openapi/RestModelConverters.java
index b19183e9182..51113ba5cff 100644
--- a/components/camel-openapi-java/src/main/java/org/apache/camel/openapi/RestModelConverters.java
+++ b/components/camel-openapi-java/src/main/java/org/apache/camel/openapi/RestModelConverters.java
@@ -18,9 +18,12 @@ package org.apache.camel.openapi;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
+import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import io.apicurio.datamodels.core.models.Extension;
 import io.apicurio.datamodels.openapi.models.OasDocument;
@@ -33,6 +36,9 @@ import io.apicurio.datamodels.openapi.v3.models.Oas30Document;
 import io.apicurio.datamodels.openapi.v3.models.Oas30Schema.Oas30AnyOfSchema;
 import io.apicurio.datamodels.openapi.v3.models.Oas30Schema.Oas30OneOfSchema;
 import io.apicurio.datamodels.openapi.v3.models.Oas30SchemaDefinition;
+import io.swagger.v3.core.converter.AnnotatedType;
+import io.swagger.v3.core.converter.ModelConverter;
+import io.swagger.v3.core.converter.ModelConverterContext;
 import io.swagger.v3.core.converter.ModelConverters;
 import io.swagger.v3.core.jackson.ModelResolver;
 import io.swagger.v3.oas.models.media.ArraySchema;
@@ -50,11 +56,18 @@ import org.slf4j.LoggerFactory;
 public class RestModelConverters {
 
     private static final Logger LOG = LoggerFactory.getLogger(RestModelConverters.class);
-    private static final ModelConverters MODEL_CONVERTERS;
+    private static final ModelConverters MODEL30_CONVERTERS;
 
     static {
-        MODEL_CONVERTERS = ModelConverters.getInstance();
-        MODEL_CONVERTERS.addConverter(new FqnModelResolver());
+        MODEL30_CONVERTERS = ModelConverters.getInstance();
+        MODEL30_CONVERTERS.addConverter(new ClassNameExtensionModelResolver(new FqnModelResolver()));
+    }
+
+    private static final ModelConverters MODEL20_CONVERTERS;
+
+    static {
+        MODEL20_CONVERTERS = ModelConverters.getInstance();
+        MODEL20_CONVERTERS.addConverter(new ClassNameExtensionModelResolver());
     }
 
     public List<? extends OasSchema> readClass(OasDocument oasDocument, Class<?> clazz) {
@@ -80,13 +93,11 @@ public class RestModelConverters {
             oasDocument.components = oasDocument.createComponents();
         }
 
-        Map<String, Schema> swaggerModel = MODEL_CONVERTERS.readAll(clazz);
+        Map<String, Schema> swaggerModel = MODEL30_CONVERTERS.readAll(clazz);
         swaggerModel.forEach((key, schema) -> {
             Oas30SchemaDefinition model = oasDocument.components.createSchemaDefinition(key);
             oasDocument.components.addSchemaDefinition(key, model);
             processSchema(model, schema);
-
-            addClassNameExtension(model, key);
         });
 
         return oasDocument.components.getSchemaDefinitions();
@@ -102,13 +113,11 @@ public class RestModelConverters {
             oasDocument.definitions = oasDocument.createDefinitions();
         }
 
-        Map<String, Schema> swaggerModel = ModelConverters.getInstance().readAll(clazz);
+        Map<String, Schema> swaggerModel = MODEL20_CONVERTERS.readAll(clazz);
         swaggerModel.forEach((key, schema) -> {
             Oas20SchemaDefinition model = oasDocument.definitions.createSchemaDefinition(key);
             oasDocument.definitions.addDefinition(key, model);
             processSchema(model, schema);
-
-            addClassNameExtension(model, key);
         });
 
         return oasDocument.definitions.getDefinitions();
@@ -251,20 +260,12 @@ public class RestModelConverters {
                 Extension extension = model.createExtension();
                 extension.name = (String) key;
                 extension.value = value;
+
+                model.addExtension((String) key, extension);
             });
         }
     }
 
-    private void addClassNameExtension(OasSchema schema, String name) {
-        Extension extension = schema.createExtension();
-        extension.name = "x-className";
-        Map<String, String> value = new HashMap<>();
-        value.put("type", "string");
-        value.put("format", name);
-        extension.value = value;
-        schema.addExtension("x-className", extension);
-    }
-
     private static class FqnModelResolver extends ModelResolver {
         public FqnModelResolver() {
             this(new ObjectMapper());
@@ -276,4 +277,40 @@ public class RestModelConverters {
         }
     }
 
+    private static class ClassNameExtensionModelResolver extends ModelResolver {
+        private final ModelResolver delegate;
+
+        public ClassNameExtensionModelResolver() {
+            this(new ModelResolver(new ObjectMapper()));
+        }
+
+        public ClassNameExtensionModelResolver(ModelResolver delegate) {
+            super(delegate.objectMapper());
+            this.delegate = delegate;
+        }
+
+        @Override
+        public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context, Iterator<ModelConverter> next) {
+            Schema result = delegate.resolve(annotatedType, context, next);
+
+            if (result != null && Objects.equals("object", result.getType())) {
+                JavaType type;
+                if (annotatedType.getType() instanceof JavaType) {
+                    type = (JavaType) annotatedType.getType();
+                } else {
+                    type = _mapper.constructType(annotatedType.getType());
+                }
+
+                if (!type.isContainerType()) {
+                    Map<String, String> value = new HashMap<>();
+                    value.put("type", "string");
+                    value.put("format", type.getRawClass().getName());
+
+                    result.addExtension("x-className", value);
+                }
+            }
+            return result;
+        }
+    }
+
 }
diff --git a/components/camel-openapi-java/src/main/java/org/apache/camel/openapi/RestOpenApiReader.java b/components/camel-openapi-java/src/main/java/org/apache/camel/openapi/RestOpenApiReader.java
index 2716f6806a1..eca73f2d131 100644
--- a/components/camel-openapi-java/src/main/java/org/apache/camel/openapi/RestOpenApiReader.java
+++ b/components/camel-openapi-java/src/main/java/org/apache/camel/openapi/RestOpenApiReader.java
@@ -707,7 +707,7 @@ public class RestOpenApiReader {
                             bp.schema = arrayModel;
 
                         } else {
-                            String ref = modelTypeAsRef(type);
+                            String ref = modelTypeAsRef(type, openApi);
                             if (ref != null) {
                                 Oas30Schema refModel = (Oas30Schema) bp.createSchema();
                                 refModel.$ref = OAS30_SCHEMA_DEFINITION_PREFIX + ref;
@@ -930,7 +930,7 @@ public class RestOpenApiReader {
                             arrayModel = modelTypeAsProperty(type, openApi, arrayModel);
                             bp.schema = arrayModel;
                         } else {
-                            String ref = modelTypeAsRef(type);
+                            String ref = modelTypeAsRef(type, openApi);
                             if (ref != null) {
                                 Oas20Schema refModel = (Oas20Schema) bp.createSchema();
                                 refModel.$ref = OAS20_SCHEMA_DEFINITION_PREFIX + ref;
@@ -1357,7 +1357,7 @@ public class RestOpenApiReader {
         response.headers.addHeader(name, ip);
     }
 
-    private String modelTypeAsRef(String typeName) {
+    private String modelTypeAsRef(String typeName, OasDocument openApi) {
         boolean array = typeName.endsWith("[]");
         if (array) {
             typeName = typeName.substring(0, typeName.length() - 2);
@@ -1367,7 +1367,29 @@ public class RestOpenApiReader {
             return null;
         }
 
-        return typeName;
+        if (openApi instanceof Oas20Document) {
+            if (((Oas20Document) openApi).definitions != null) {
+                for (Oas20SchemaDefinition model : ((Oas20Document) openApi).definitions.getDefinitions()) {
+                    @SuppressWarnings("rawtypes")
+                    Map modelType = (Map) model.getExtension("x-className").value;
+                    if (modelType != null && typeName.equals(modelType.get("format"))) {
+                        return model.getName();
+                    }
+                }
+            }
+        } else if (openApi instanceof Oas30Document) {
+            if (((Oas30Document) openApi).components != null
+                    && ((Oas30Document) openApi).components.schemas != null) {
+                for (Oas30SchemaDefinition model : ((Oas30Document) openApi).components.schemas.values()) {
+                    @SuppressWarnings("rawtypes")
+                    Map modelType = (Map) model.getExtension("x-className").value;
+                    if (modelType != null && typeName.equals(modelType.get("format"))) {
+                        return model.getName();
+                    }
+                }
+            }
+        }
+        return null;
     }
 
     private OasSchema modelTypeAsProperty(String typeName, OasDocument openApi, OasSchema prop) {
@@ -1376,7 +1398,7 @@ public class RestOpenApiReader {
             typeName = typeName.substring(0, typeName.length() - 2);
         }
 
-        String ref = modelTypeAsRef(typeName);
+        String ref = modelTypeAsRef(typeName, openApi);
 
         if (ref != null) {
             if (openApi instanceof Oas20Document) {
diff --git a/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/ComplexTypesTest.java b/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/ComplexTypesTest.java
index 777feccfa57..92c29887ab0 100644
--- a/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/ComplexTypesTest.java
+++ b/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/ComplexTypesTest.java
@@ -34,7 +34,9 @@ import org.apache.camel.impl.engine.DefaultClassResolver;
 import org.apache.camel.model.rest.RestBindingMode;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.openapi.model.SampleComplexRequestType;
+import org.apache.camel.openapi.model.SampleComplexRequestTypeWithSchemaAnnotation;
 import org.apache.camel.openapi.model.SampleComplexResponseType;
+import org.apache.camel.openapi.model.SampleComplexResponseTypeWithSchemaAnnotation;
 import org.apache.camel.test.junit5.CamelTestSupport;
 import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
@@ -93,6 +95,39 @@ public class ComplexTypesTest extends CamelTestSupport {
                         .routeId("complex response type")
                         .log("/complex invoked")
                         .setBody(constant(new SampleComplexResponseType()));
+
+                rest().post("/complexRequestWithSchemaAnnotation")
+                        .description("Demo complex request type")
+                        .type(SampleComplexRequestTypeWithSchemaAnnotation.class)
+                        .consumes("application/json")
+                        .produces("text/plain")
+                        .bindingMode(RestBindingMode.json)
+                        .responseMessage()
+                        .code(200)
+                        .message("Receives a complex object as parameter")
+                        .endResponseMessage()
+                        .outType(SampleComplexResponseTypeWithSchemaAnnotation.InnerClass.class)
+                        .to("direct:requestWithSchemaAnnotation");
+                from("direct:requestWithSchemaAnnotation")
+                        .routeId("complex request type with schema annotation")
+                        .log("/complex request invoked");
+
+                rest().get("/complexResponseWithSchemaAnnotation")
+                        .description("Demo complex response type")
+                        .type(SampleComplexRequestType.InnerClass.class)
+                        .consumes("application/json")
+                        .outType(SampleComplexResponseTypeWithSchemaAnnotation.class)
+                        .produces("application/json")
+                        .bindingMode(RestBindingMode.json)
+                        .responseMessage()
+                        .code(200)
+                        .message("Returns a complex object")
+                        .endResponseMessage()
+                        .to("direct:responseWithSchemaAnnotation");
+                from("direct:responseWithSchemaAnnotation")
+                        .routeId("complex response type with schema annotation")
+                        .log("/complex invoked")
+                        .setBody(constant(new SampleComplexResponseTypeWithSchemaAnnotation()));
             }
         };
     }
@@ -117,6 +152,30 @@ public class ComplexTypesTest extends CamelTestSupport {
         checkSchemaGeneration("/complexResponse", "2.0", "V2SchemaForComplexTypesResponse.json");
     }
 
+    @Test
+    public void testV3SchemaForComplexTypesWithSchemaAnnotationRequest() throws Exception {
+        checkSchemaGeneration("/complexRequestWithSchemaAnnotation", "3.0",
+                "V3SchemaForComplexTypesRequestWithSchemaAnnotation.json");
+    }
+
+    @Test
+    public void testV2SchemaForComplexTypesWithSchemaAnnotationRequest() throws Exception {
+        checkSchemaGeneration("/complexRequestWithSchemaAnnotation", "2.0",
+                "V2SchemaForComplexTypesRequestWithSchemaAnnotation.json");
+    }
+
+    @Test
+    public void testV3SchemaForComplexTypesWithSchemaAnnotationResponse() throws Exception {
+        checkSchemaGeneration("/complexResponseWithSchemaAnnotation", "3.0",
+                "V3SchemaForComplexTypesResponseWithSchemaAnnotation.json");
+    }
+
+    @Test
+    public void testV2SchemaForComplexTypesWithSchemaAnnotationResponse() throws Exception {
+        checkSchemaGeneration("/complexResponseWithSchemaAnnotation", "2.0",
+                "V2SchemaForComplexTypesResponseWithSchemaAnnotation.json");
+    }
+
     private void checkSchemaGeneration(String uri, String apiVersion, String schemaResource) throws Exception {
         BeanConfig config = getBeanConfig(apiVersion);
 
diff --git a/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/model/CustomDataWithSchemaAnnotation.java b/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/model/CustomDataWithSchemaAnnotation.java
new file mode 100644
index 00000000000..4515cac7321
--- /dev/null
+++ b/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/model/CustomDataWithSchemaAnnotation.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.openapi.model;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "customData")
+class CustomDataWithSchemaAnnotation implements GenericData {
+
+    private String customDataField;
+
+    public String getCustomDataField() {
+        return customDataField;
+    }
+}
diff --git a/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/model/SampleComplexRequestTypeWithSchemaAnnotation.java b/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/model/SampleComplexRequestTypeWithSchemaAnnotation.java
new file mode 100644
index 00000000000..c22d391b714
--- /dev/null
+++ b/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/model/SampleComplexRequestTypeWithSchemaAnnotation.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.openapi.model;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "sampleRequestWithSchema")
+public class SampleComplexRequestTypeWithSchemaAnnotation extends GenericComplexRequestType<CustomDataWithSchemaAnnotation> {
+    @JsonProperty(required = true)
+    private String requestField1;
+    private String requestField2;
+    private List<String> listOfStrings;
+    private String[] arrayOfString;
+    private Map<String, String> mapOfStrings;
+    private TimeUnit timeUnit;
+    private InnerClass innerClass;
+
+    public String getRequestField1() {
+        return requestField1;
+    }
+
+    public String getRequestField2() {
+        return requestField2;
+    }
+
+    public List<String> getListOfStrings() {
+        return listOfStrings;
+    }
+
+    public String[] getArrayOfString() {
+        return arrayOfString;
+    }
+
+    @JsonProperty(required = true)
+    public Map<String, String> getMapOfStrings() {
+        return mapOfStrings;
+    }
+
+    public TimeUnit getTimeUnit() {
+        return timeUnit;
+    }
+
+    public InnerClass getInnerClass() {
+        return innerClass;
+    }
+
+    @Schema(name = "requestInner")
+    public static class InnerClass {
+        private long longField;
+
+        public long getLongField() {
+            return longField;
+        }
+    }
+}
diff --git a/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/model/SampleComplexResponseTypeWithSchemaAnnotation.java b/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/model/SampleComplexResponseTypeWithSchemaAnnotation.java
new file mode 100644
index 00000000000..3d612df279a
--- /dev/null
+++ b/components/camel-openapi-java/src/test/java/org/apache/camel/openapi/model/SampleComplexResponseTypeWithSchemaAnnotation.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.openapi.model;
+
+import java.time.Month;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "sampleResponseWithSchema")
+public class SampleComplexResponseTypeWithSchemaAnnotation {
+    @JsonProperty(required = true)
+    private String responseField1 = "Response Field 1";
+    private String responseField2 = "Response Field 2";
+    private String[] arrayOfStrings;
+    private Month month;
+    private InnerClass innerClass;
+
+    public String getResponseField1() {
+        return responseField1;
+    }
+
+    public String getResponseField2() {
+        return responseField2;
+    }
+
+    @JsonProperty(required = true)
+    public String[] getArrayOfStrings() {
+        return arrayOfStrings;
+    }
+
+    public Month getMonth() {
+        return month;
+    }
+
+    public InnerClass getInnerClass() {
+        return innerClass;
+    }
+
+    @Schema(name = "responseInner")
+    public static class InnerClass {
+        double doubleField;
+
+        public double getDoubleField() {
+            return doubleField;
+        }
+    }
+}
diff --git a/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V2SchemaForComplexTypesRequestWithSchemaAnnotation.json b/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V2SchemaForComplexTypesRequestWithSchemaAnnotation.json
new file mode 100644
index 00000000000..dd744b9aa90
--- /dev/null
+++ b/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V2SchemaForComplexTypesRequestWithSchemaAnnotation.json
@@ -0,0 +1,158 @@
+{
+  "swagger" : "2.x",
+  "host" : "localhost:8080",
+  "basePath" : "/api",
+  "schemes" : [ "http" ],
+  "paths" : {
+    "/complexRequestWithSchemaAnnotation" : {
+      "post" : {
+        "consumes" : [ "application/json" ],
+        "produces" : [ "text/plain" ],
+        "parameters" : [ {
+          "name" : "body",
+          "schema" : {
+            "$ref" : "#/definitions/sampleRequestWithSchema"
+          },
+          "in" : "body",
+          "required" : true
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Receives a complex object as parameter",
+            "schema" : {
+              "$ref" : "#/definitions/responseInner"
+            }
+          }
+        },
+        "operationId" : "verb",
+        "summary" : "Demo complex request type",
+        "x-camelContextId" : "camel"
+      }
+    }
+  },
+  "definitions" : {
+    "customData" : {
+      "type" : "object",
+      "properties" : {
+        "customDataField" : {
+          "type" : "string"
+        }
+      },
+      "x-className" : {
+        "format" : "org.apache.camel.openapi.model.CustomDataWithSchemaAnnotation",
+        "type" : "string"
+      }
+    },
+    "requestInner" : {
+      "type" : "object",
+      "properties" : {
+        "longField" : {
+          "format" : "int64",
+          "type" : "integer"
+        }
+      },
+      "x-className" : {
+        "format" : "org.apache.camel.openapi.model.SampleComplexRequestTypeWithSchemaAnnotation$InnerClass",
+        "type" : "string"
+      }
+    },
+    "sampleRequestWithSchema" : {
+      "required" : [ "mapOfStrings", "requestField1" ],
+      "type" : "object",
+      "properties" : {
+        "data" : {
+          "$ref" : "#/definitions/customData"
+        },
+        "listOfData" : {
+          "type" : "array",
+          "items" : {
+            "$ref" : "#/definitions/customData"
+          }
+        },
+        "listOfListOfData" : {
+          "type" : "array",
+          "items" : {
+            "type" : "array",
+            "items" : {
+              "$ref" : "#/definitions/customData"
+            }
+          }
+        },
+        "mapOfData" : {
+          "type" : "object",
+          "additionalProperties" : {
+            "$ref" : "#/definitions/customData"
+          }
+        },
+        "mapOfMapOfData" : {
+          "type" : "object",
+          "additionalProperties" : {
+            "type" : "object",
+            "additionalProperties" : {
+              "$ref" : "#/definitions/customData"
+            }
+          }
+        },
+        "requestField1" : {
+          "type" : "string"
+        },
+        "requestField2" : {
+          "type" : "string"
+        },
+        "listOfStrings" : {
+          "type" : "array",
+          "items" : {
+            "type" : "string"
+          }
+        },
+        "arrayOfString" : {
+          "type" : "array",
+          "items" : {
+            "type" : "string"
+          }
+        },
+        "mapOfStrings" : {
+          "type" : "object",
+          "additionalProperties" : {
+            "type" : "string"
+          }
+        },
+        "timeUnit" : {
+          "enum" : [ "NANOSECONDS", "MICROSECONDS", "MILLISECONDS", "SECONDS", "MINUTES", "HOURS", "DAYS" ],
+          "type" : "string"
+        },
+        "innerClass" : {
+          "$ref" : "#/definitions/requestInner"
+        }
+      },
+      "x-className" : {
+        "format" : "org.apache.camel.openapi.model.SampleComplexRequestTypeWithSchemaAnnotation",
+        "type" : "string"
+      }
+    },
+    "responseInner" : {
+      "type" : "object",
+      "properties" : {
+        "doubleField" : {
+          "format" : "double",
+          "type" : "number"
+        }
+      },
+      "x-className" : {
+        "format" : "org.apache.camel.openapi.model.SampleComplexResponseTypeWithSchemaAnnotation$InnerClass",
+        "type" : "string"
+      }
+    }
+  },
+  "securityDefinitions" : {
+    "global" : {
+      "flow" : "accessCode",
+      "authorizationUrl" : "https://AUTHORIZATION_URL",
+      "tokenUrl" : "https://TOKEN_URL",
+      "scopes" : {
+        "groups" : "Required scopes for Camel REST APIs"
+      },
+      "type" : "oauth2"
+    }
+  }
+}
diff --git a/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V2SchemaForComplexTypesResponseWithSchemaAnnotation.json b/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V2SchemaForComplexTypesResponseWithSchemaAnnotation.json
new file mode 100644
index 00000000000..4823e969630
--- /dev/null
+++ b/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V2SchemaForComplexTypesResponseWithSchemaAnnotation.json
@@ -0,0 +1,101 @@
+{
+  "swagger" : "2.x",
+  "host" : "localhost:8080",
+  "basePath" : "/api",
+  "schemes" : [ "http" ],
+  "paths" : {
+    "/complexResponseWithSchemaAnnotation" : {
+      "get" : {
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "body",
+          "schema" : {
+            "$ref" : "#/definitions/SampleComplexRequestType_InnerClass"
+          },
+          "in" : "body",
+          "required" : true
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Returns a complex object",
+            "schema" : {
+              "$ref" : "#/definitions/sampleResponseWithSchema"
+            }
+          }
+        },
+        "operationId" : "verb",
+        "summary" : "Demo complex response type",
+        "x-camelContextId" : "camel"
+      }
+    }
+  },
+  "definitions" : {
+    "SampleComplexRequestType_InnerClass" : {
+      "type" : "object",
+      "properties" : {
+        "longField" : {
+          "format" : "int64",
+          "type" : "integer"
+        }
+      },
+      "x-className" : {
+        "format" : "org.apache.camel.openapi.model.SampleComplexRequestType$InnerClass",
+        "type" : "string"
+      }
+    },
+    "responseInner" : {
+      "type" : "object",
+      "properties" : {
+        "doubleField" : {
+          "format" : "double",
+          "type" : "number"
+        }
+      },
+      "x-className" : {
+        "format" : "org.apache.camel.openapi.model.SampleComplexResponseTypeWithSchemaAnnotation$InnerClass",
+        "type" : "string"
+      }
+    },
+    "sampleResponseWithSchema" : {
+      "required" : [ "arrayOfStrings", "responseField1" ],
+      "type" : "object",
+      "properties" : {
+        "responseField1" : {
+          "type" : "string"
+        },
+        "responseField2" : {
+          "type" : "string"
+        },
+        "arrayOfStrings" : {
+          "type" : "array",
+          "items" : {
+            "type" : "string"
+          }
+        },
+        "month" : {
+          "enum" : [ "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER" ],
+          "type" : "string"
+        },
+        "innerClass" : {
+          "$ref" : "#/definitions/responseInner"
+        }
+      },
+      "x-className" : {
+        "format" : "org.apache.camel.openapi.model.SampleComplexResponseTypeWithSchemaAnnotation",
+        "type" : "string"
+      }
+    }
+  },
+  "securityDefinitions" : {
+    "global" : {
+      "flow" : "accessCode",
+      "authorizationUrl" : "https://AUTHORIZATION_URL",
+      "tokenUrl" : "https://TOKEN_URL",
+      "scopes" : {
+        "groups" : "Required scopes for Camel REST APIs"
+      },
+      "type" : "oauth2"
+    }
+  }
+}
diff --git a/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V3SchemaForComplexTypesRequestWithSchemaAnnotation.json b/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V3SchemaForComplexTypesRequestWithSchemaAnnotation.json
new file mode 100644
index 00000000000..6b6cc5301a4
--- /dev/null
+++ b/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V3SchemaForComplexTypesRequestWithSchemaAnnotation.json
@@ -0,0 +1,168 @@
+{
+  "openapi" : "3.x",
+  "servers" : [ {
+    "url" : "http://localhost:8080/api"
+  } ],
+  "paths" : {
+    "/complexRequestWithSchemaAnnotation" : {
+      "post" : {
+        "requestBody" : {
+          "description" : "",
+          "content" : {
+            "application/json" : {
+              "schema" : {
+                "$ref" : "#/components/schemas/sampleRequestWithSchema"
+              }
+            }
+          },
+          "required" : true
+        },
+        "responses" : {
+          "200" : {
+            "content" : {
+              "text/plain" : {
+                "schema" : {
+                  "$ref" : "#/components/schemas/responseInner"
+                }
+              }
+            },
+            "description" : "Receives a complex object as parameter"
+          }
+        },
+        "operationId" : "verb",
+        "summary" : "Demo complex request type",
+        "x-camelContextId" : "camel"
+      }
+    }
+  },
+  "components" : {
+    "schemas" : {
+      "customData" : {
+        "type" : "object",
+        "properties" : {
+          "customDataField" : {
+            "type" : "string"
+          }
+        },
+        "x-className" : {
+          "format" : "org.apache.camel.openapi.model.CustomDataWithSchemaAnnotation",
+          "type" : "string"
+        }
+      },
+      "requestInner" : {
+        "type" : "object",
+        "properties" : {
+          "longField" : {
+            "format" : "int64",
+            "type" : "integer"
+          }
+        },
+        "x-className" : {
+          "format" : "org.apache.camel.openapi.model.SampleComplexRequestTypeWithSchemaAnnotation$InnerClass",
+          "type" : "string"
+        }
+      },
+      "sampleRequestWithSchema" : {
+        "required" : [ "mapOfStrings", "requestField1" ],
+        "type" : "object",
+        "properties" : {
+          "data" : {
+            "$ref" : "#/components/schemas/customData"
+          },
+          "listOfData" : {
+            "type" : "array",
+            "items" : {
+              "$ref" : "#/components/schemas/customData"
+            }
+          },
+          "listOfListOfData" : {
+            "type" : "array",
+            "items" : {
+              "type" : "array",
+              "items" : {
+                "$ref" : "#/components/schemas/customData"
+              }
+            }
+          },
+          "mapOfData" : {
+            "type" : "object",
+            "additionalProperties" : {
+              "$ref" : "#/components/schemas/customData"
+            }
+          },
+          "mapOfMapOfData" : {
+            "type" : "object",
+            "additionalProperties" : {
+              "type" : "object",
+              "additionalProperties" : {
+                "$ref" : "#/components/schemas/customData"
+              }
+            }
+          },
+          "requestField1" : {
+            "type" : "string"
+          },
+          "requestField2" : {
+            "type" : "string"
+          },
+          "listOfStrings" : {
+            "type" : "array",
+            "items" : {
+              "type" : "string"
+            }
+          },
+          "arrayOfString" : {
+            "type" : "array",
+            "items" : {
+              "type" : "string"
+            }
+          },
+          "mapOfStrings" : {
+            "type" : "object",
+            "additionalProperties" : {
+              "type" : "string"
+            }
+          },
+          "timeUnit" : {
+            "enum" : [ "NANOSECONDS", "MICROSECONDS", "MILLISECONDS", "SECONDS", "MINUTES", "HOURS", "DAYS" ],
+            "type" : "string"
+          },
+          "innerClass" : {
+            "$ref" : "#/components/schemas/requestInner"
+          }
+        },
+        "x-className" : {
+          "format" : "org.apache.camel.openapi.model.SampleComplexRequestTypeWithSchemaAnnotation",
+          "type" : "string"
+        }
+      },
+      "responseInner" : {
+        "type" : "object",
+        "properties" : {
+          "doubleField" : {
+            "format" : "double",
+            "type" : "number"
+          }
+        },
+        "x-className" : {
+          "format" : "org.apache.camel.openapi.model.SampleComplexResponseTypeWithSchemaAnnotation$InnerClass",
+          "type" : "string"
+        }
+      }
+    },
+    "securitySchemes" : {
+      "global" : {
+        "flows" : {
+          "authorizationCode" : {
+            "authorizationUrl" : "https://AUTHORIZATION_URL",
+            "tokenUrl" : "https://TOKEN_URL",
+            "scopes" : {
+              "groups" : "Required scopes for Camel REST APIs"
+            }
+          }
+        },
+        "type" : "oauth2"
+      }
+    }
+  }
+}
diff --git a/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V3SchemaForComplexTypesResponseWithSchemaAnnotation.json b/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V3SchemaForComplexTypesResponseWithSchemaAnnotation.json
new file mode 100644
index 00000000000..bb5191c16d7
--- /dev/null
+++ b/components/camel-openapi-java/src/test/resources/org/apache/camel/openapi/V3SchemaForComplexTypesResponseWithSchemaAnnotation.json
@@ -0,0 +1,111 @@
+{
+  "openapi" : "3.x",
+  "servers" : [ {
+    "url" : "http://localhost:8080/api"
+  } ],
+  "paths" : {
+    "/complexResponseWithSchemaAnnotation" : {
+      "get" : {
+        "requestBody" : {
+          "description" : "",
+          "content" : {
+            "application/json" : {
+              "schema" : {
+                "$ref" : "#/components/schemas/SampleComplexRequestType_InnerClass"
+              }
+            }
+          },
+          "required" : true
+        },
+        "responses" : {
+          "200" : {
+            "content" : {
+              "application/json" : {
+                "schema" : {
+                  "$ref" : "#/components/schemas/sampleResponseWithSchema"
+                }
+              }
+            },
+            "description" : "Returns a complex object"
+          }
+        },
+        "operationId" : "verb",
+        "summary" : "Demo complex response type",
+        "x-camelContextId" : "camel"
+      }
+    }
+  },
+  "components" : {
+    "schemas" : {
+      "SampleComplexRequestType_InnerClass" : {
+        "type" : "object",
+        "properties" : {
+          "longField" : {
+            "format" : "int64",
+            "type" : "integer"
+          }
+        },
+        "x-className" : {
+          "format" : "org.apache.camel.openapi.model.SampleComplexRequestType$InnerClass",
+          "type" : "string"
+        }
+      },
+      "responseInner" : {
+        "type" : "object",
+        "properties" : {
+          "doubleField" : {
+            "format" : "double",
+            "type" : "number"
+          }
+        },
+        "x-className" : {
+          "format" : "org.apache.camel.openapi.model.SampleComplexResponseTypeWithSchemaAnnotation$InnerClass",
+          "type" : "string"
+        }
+      },
+      "sampleResponseWithSchema" : {
+        "required" : [ "arrayOfStrings", "responseField1" ],
+        "type" : "object",
+        "properties" : {
+          "responseField1" : {
+            "type" : "string"
+          },
+          "responseField2" : {
+            "type" : "string"
+          },
+          "arrayOfStrings" : {
+            "type" : "array",
+            "items" : {
+              "type" : "string"
+            }
+          },
+          "month" : {
+            "enum" : [ "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER" ],
+            "type" : "string"
+          },
+          "innerClass" : {
+            "$ref" : "#/components/schemas/responseInner"
+          }
+        },
+        "x-className" : {
+          "format" : "org.apache.camel.openapi.model.SampleComplexResponseTypeWithSchemaAnnotation",
+          "type" : "string"
+        }
+      }
+    },
+    "securitySchemes" : {
+      "global" : {
+        "flows" : {
+          "authorizationCode" : {
+            "authorizationUrl" : "https://AUTHORIZATION_URL",
+            "tokenUrl" : "https://TOKEN_URL",
+            "scopes" : {
+              "groups" : "Required scopes for Camel REST APIs"
+            }
+          }
+        },
+        "type" : "oauth2"
+      }
+    }
+  }
+}