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();