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

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


The following commit(s) were added to refs/heads/main by this push:
     new f992f68ae56 support OpenAPI 3.1 in response body type resolution 
(#16829)
f992f68ae56 is described below

commit f992f68ae56d16fabb5451a578c0e1647f4aabfd
Author: Christian Pieczewski <[email protected]>
AuthorDate: Thu Jan 16 09:08:03 2025 +0100

    support OpenAPI 3.1 in response body type resolution (#16829)
    
    This commit introduces support for resolving response body types from
    OpenAPI 3.1 specifications in addition to the existing support for
    OpenAPI 3.0.
---
 .../camel/component/rest/openapi/OpenApiUtils.java | 101 +++++++++++++--------
 .../component/rest/openapi/OpenApiUtilsTest.java   |  59 ++++++++++++
 2 files changed, 121 insertions(+), 39 deletions(-)

diff --git 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/OpenApiUtils.java
 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/OpenApiUtils.java
index 099050aef0c..ff7bd82268d 100644
--- 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/OpenApiUtils.java
+++ 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/OpenApiUtils.java
@@ -26,6 +26,7 @@ import java.util.stream.Collectors;
 
 import io.swagger.v3.oas.models.Components;
 import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.SpecVersion;
 import io.swagger.v3.oas.models.media.ArraySchema;
 import io.swagger.v3.oas.models.media.Content;
 import io.swagger.v3.oas.models.media.MediaType;
@@ -59,6 +60,8 @@ public class OpenApiUtils {
             "date", LocalDate.class,
             "date-time", LocalDateTime.class);
 
+    private static final Pattern CLASS_NAME_PATTERN = 
Pattern.compile(".*\\/(.*)");
+
     private final AtomicBoolean packageScanInit = new AtomicBoolean();
     private final Set<Class<?>> scannedClasses = new HashSet<>();
     private CamelContext camelContext;
@@ -72,10 +75,7 @@ public class OpenApiUtils {
     }
 
     public boolean isRequiredBody(Operation operation) {
-        if (operation.getRequestBody() != null) {
-            return Boolean.TRUE == operation.getRequestBody().getRequired();
-        }
-        return false;
+        return operation.getRequestBody() != null && Boolean.TRUE == 
operation.getRequestBody().getRequired();
     }
 
     public String getConsumes(Operation operation) {
@@ -170,54 +170,77 @@ public class OpenApiUtils {
         Schema<?> schema = mediaType.getValue().getSchema();
 
         if (mediaTypeName.contains("xml") && schema.getXml() != null) {
-            String ref = schema.getXml().getName();
-            // must refer to a class name, so upper case
-            ref = Character.toUpperCase(ref.charAt(0)) + ref.substring(1);
-            // find class via simple name
-            for (Class<?> clazz : scannedClasses) {
-                if (clazz.getSimpleName().equals(ref)) {
-                    return clazz;
-                }
-            }
+            return loadBindingClassForXml(schema);
         } else if (mediaTypeName.contains("json")) {
-            if (schema instanceof ArraySchema) {
-                schema = schema.getItems();
-            }
-            String schemaName = findSchemaName(schema);
-            if (schemaName == null) {
-                Class<?> primitiveType = resolveType(schema);
-                if (primitiveType != null) {
-                    return primitiveType;
-                }
-            }
-            String schemaTitle = schema.getTitle();
-            Pattern classNamePattern = Pattern.compile(".*\\/(.*)");
-            schemaName = Optional.ofNullable(schemaName)
-                    
.orElse(Optional.ofNullable(schema.get$ref()).orElse(schema.getType()));
-            Matcher classNameMatcher = classNamePattern.matcher(schemaName);
-            String classToFind = classNameMatcher.find()
-                    ? classNameMatcher.group(1)
-                    : schemaName;
-
-            return scannedClasses.stream()
-                    .filter(aClass -> 
aClass.getSimpleName().equals(classToFind) || 
aClass.getSimpleName().equals(schemaTitle)) //use either the name or title of 
schema to find the class
-                    .findFirst()
-                    .orElse(null);
+            return loadBindingClassForJson(schema);
         }
 
         // class not found
         return null;
     }
 
+    private Class<?> loadBindingClassForXml(Schema<?> schema) {
+        String ref = schema.getXml().getName();
+        // must refer to a class name, so upper case
+        ref = Character.toUpperCase(ref.charAt(0)) + ref.substring(1);
+        // find class via simple name
+        for (Class<?> clazz : scannedClasses) {
+            if (clazz.getSimpleName().equals(ref)) {
+                return clazz;
+            }
+        }
+        return null;
+    }
+
+    private Class<?> loadBindingClassForJson(Schema<?> schema) {
+        if (isArrayType(schema)) {
+            schema = schema.getItems();
+        }
+        String schemaName = findSchemaName(schema);
+        if (schemaName == null) {
+            Class<?> primitiveType = resolvePrimitiveType(schema);
+            if (primitiveType != null) {
+                return primitiveType;
+            }
+        }
+
+        schemaName = Optional.ofNullable(schemaName)
+                
.orElse(Optional.ofNullable(schema.get$ref()).orElse(getSchemaType(schema)));
+        Matcher classNameMatcher = CLASS_NAME_PATTERN.matcher(schemaName);
+        String classToFind = classNameMatcher.find()
+                ? classNameMatcher.group(1)
+                : schemaName;
+
+        String schemaTitle = schema.getTitle();
+        return scannedClasses.stream()
+                .filter(aClass -> aClass.getSimpleName().equals(classToFind) 
|| aClass.getSimpleName().equals(schemaTitle)) //use either the name or title 
of schema to find the class
+                .findFirst()
+                .orElse(null);
+    }
+
+    public boolean isArrayType(Schema<?> schema) {
+        if (schema.getSpecVersion() == SpecVersion.V30) {
+            return schema instanceof ArraySchema;
+        }
+        return 
"array".equals(schema.getTypes().stream().findFirst().orElse(null));
+    }
+
+    private String getSchemaType(Schema<?> schema) {
+        if (schema.getSpecVersion() == SpecVersion.V30) {
+            return schema.getType();
+        }
+        return schema.getTypes() == null ? null : 
schema.getTypes().stream().findFirst().orElse(null);
+    }
+
     private String resolveClassName(Schema<?> schema, Class<?> clazz) {
-        if (schema instanceof ArraySchema) {
+        if (isArrayType(schema)) {
             return clazz.getName().concat("[]");
         }
         return clazz.getName();
     }
 
-    private Class<?> resolveType(Schema<?> schema) {
-        String type = schema.getType();
+    private Class<?> resolvePrimitiveType(Schema<?> schema) {
+        String type = getSchemaType(schema);
         if (type == null) {
             return null;
         }
diff --git 
a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/OpenApiUtilsTest.java
 
b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/OpenApiUtilsTest.java
index c5a44a31150..b7d5770e31a 100644
--- 
a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/OpenApiUtilsTest.java
+++ 
b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/OpenApiUtilsTest.java
@@ -19,6 +19,7 @@ package org.apache.camel.component.rest.openapi;
 import java.util.Map;
 
 import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.OpenAPI;
 import io.swagger.v3.oas.models.Operation;
 import io.swagger.v3.oas.models.media.Content;
 import io.swagger.v3.oas.models.media.IntegerSchema;
@@ -29,6 +30,7 @@ import io.swagger.v3.oas.models.media.StringSchema;
 import io.swagger.v3.oas.models.parameters.RequestBody;
 import io.swagger.v3.oas.models.responses.ApiResponse;
 import io.swagger.v3.oas.models.responses.ApiResponses;
+import io.swagger.v3.parser.OpenAPIV3Parser;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.junit.jupiter.api.Test;
 
@@ -36,6 +38,43 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class OpenApiUtilsTest {
+
+    private static final String TAG_OPENAPI_YAML = """
+            ---
+            openapi: <openapi_version>
+            info:
+                title: Tag API
+                version: v1
+            paths:
+              /tag:
+                get:
+                  summary: Get a tag
+                  operationId: getTag
+                  responses:
+                    '200':
+                      description: Successful operation
+                      content:
+                        application/json:
+                          schema:
+                            type: array
+                            items:
+                              $ref: '#/components/schemas/TagResponseDto'
+            components:
+              schemas:
+                TagResponseDto:
+                  type: object
+                  properties:
+                    id:
+                      type: integer
+                      description: The ID of the tag
+                    name:
+                      type: string
+                      description: The name of the tag
+                  required:
+                    - id
+                    - name
+                    """;
+
     @Test
     public void shouldReturnAllProduces() {
         Operation operation = new Operation();
@@ -126,6 +165,26 @@ public class OpenApiUtilsTest {
         assertEquals(TagResponseDto.class.getName(), 
utils.manageResponseBody(operation));
     }
 
+    @Test
+    public void shouldManageResponseFromOpenApi31Parser() throws Exception {
+        String bindingPackagePath = OpenApiUtils.class.getPackage().getName();
+        OpenAPIV3Parser parser = new OpenAPIV3Parser();
+        OpenAPI openApi = 
parser.readContents(TAG_OPENAPI_YAML.replace("<openapi_version>", 
"3.1.0")).getOpenAPI();
+        Operation operation = openApi.getPaths().get("/tag").getGet();
+        OpenApiUtils utils = new OpenApiUtils(new DefaultCamelContext(), 
bindingPackagePath, openApi.getComponents());
+        assertEquals(TagResponseDto.class.getName() + "[]", 
utils.manageResponseBody(operation));
+    }
+
+    @Test
+    public void shouldManageRequestFromOpenApi30Parser() throws Exception {
+        String bindingPackagePath = OpenApiUtils.class.getPackage().getName();
+        OpenAPIV3Parser parser = new OpenAPIV3Parser();
+        OpenAPI openApi = 
parser.readContents(TAG_OPENAPI_YAML.replace("<openapi_version>", 
"3.0.0")).getOpenAPI();
+        Operation operation = openApi.getPaths().get("/tag").getGet();
+        OpenApiUtils utils = new OpenApiUtils(new DefaultCamelContext(), 
bindingPackagePath, openApi.getComponents());
+        assertEquals(TagResponseDto.class.getName() + "[]", 
utils.manageResponseBody(operation));
+    }
+
     private ApiResponse createResponse(String... contentTypes) {
         ApiResponse response = new ApiResponse();
 

Reply via email to