From 3408bf15772d740d34d8912bccec0ed36d2781e3 Mon Sep 17 00:00:00 2001 From: Thorsten Schlathoelter Date: Tue, 11 Jun 2024 07:21:00 +0200 Subject: [PATCH] fix(#1175): OpenAPI connector enhancements for simulator random message generation --- connectors/citrus-openapi/pom.xml | 6 ++ .../OpenApiClientResponseActionBuilder.java | 1 + .../openapi/model/OasModelHelper.java | 17 ++-- .../openapi/model/v2/Oas20ModelHelper.java | 18 ++-- .../openapi/model/v3/Oas30ModelHelper.java | 83 +++++++++---------- 5 files changed, 65 insertions(+), 60 deletions(-) diff --git a/connectors/citrus-openapi/pom.xml b/connectors/citrus-openapi/pom.xml index da92c9c032..fdeb577917 100644 --- a/connectors/citrus-openapi/pom.xml +++ b/connectors/citrus-openapi/pom.xml @@ -76,6 +76,12 @@ ${project.version} test + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.17.0 + compile + diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientResponseActionBuilder.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientResponseActionBuilder.java index 29b4d7e036..bfe7045f9d 100644 --- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientResponseActionBuilder.java +++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientResponseActionBuilder.java @@ -97,6 +97,7 @@ public static void fillMessageFromResponse(OpenApiSpecification openApiSpecifica } } ); + } private static void fillRequiredHeaders( diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/OasModelHelper.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/OasModelHelper.java index f8d1e703cd..ab77225e68 100644 --- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/OasModelHelper.java +++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/OasModelHelper.java @@ -58,7 +58,7 @@ private OasModelHelper() { * @param schema to check * @return true if given schema is an object. */ - public static boolean isObjectType(OasSchema schema) { + public static boolean isObjectType(@Nullable OasSchema schema) { return schema != null && "object".equals(schema.type); } @@ -67,8 +67,8 @@ public static boolean isObjectType(OasSchema schema) { * @param schema to check * @return true if given schema is an array. */ - public static boolean isArrayType(OasSchema schema) { - return "array".equals(schema.type); + public static boolean isArrayType(@Nullable OasSchema schema) { + return schema != null && "array".equals(schema.type); } /** @@ -76,14 +76,14 @@ public static boolean isArrayType(OasSchema schema) { * @param schema to check * @return true if given schema is an object array. */ - public static boolean isObjectArrayType(OasSchema schema) { - if (schema == null || !"array".equals(schema.type)) { + public static boolean isObjectArrayType(@Nullable OasSchema schema) { + if (schema == null || !"array".equals(schema.type)) { return false; } Object items = schema.items; - if (items instanceof OasSchema oasSchema) { + if (items instanceof OasSchema oasSchema) { return isObjectType(oasSchema); - } else if (items instanceof List list) { + } else if (items instanceof List list) { return list.stream().allMatch(item -> item instanceof OasSchema oasSchema && isObjectType(oasSchema)); } @@ -95,7 +95,7 @@ public static boolean isObjectArrayType(OasSchema schema) { * @param schema to check * @return true if given schema has a reference. */ - public static boolean isReferenceType(OasSchema schema) { + public static boolean isReferenceType(@Nullable OasSchema schema) { return schema != null && schema.$ref != null; } @@ -194,7 +194,6 @@ public static String getReferenceName(String reference) { public static Optional getSchema(OasResponse response) { return delegate(response, Oas20ModelHelper::getSchema, Oas30ModelHelper::getSchema); } - public static Optional getParameterSchema(OasParameter parameter) { return delegate(parameter, Oas20ModelHelper::getParameterSchema, Oas30ModelHelper::getParameterSchema); } diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/v2/Oas20ModelHelper.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/v2/Oas20ModelHelper.java index d3b83c4000..f4480dffff 100644 --- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/v2/Oas20ModelHelper.java +++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/v2/Oas20ModelHelper.java @@ -56,12 +56,12 @@ public static List getSchemes(Oas20Document openApiDoc) { public static String getBasePath(Oas20Document openApiDoc) { return Optional.ofNullable(openApiDoc.basePath) - .map(basePath -> basePath.startsWith("/") ? basePath : "/" + basePath).orElse("/"); + .map(basePath -> basePath.startsWith("/") ? basePath : "/" + basePath).orElse("/"); } public static Map getSchemaDefinitions(Oas20Document openApiDoc) { if (openApiDoc == null - || openApiDoc.definitions == null) { + || openApiDoc.definitions == null) { return Collections.emptyMap(); } @@ -72,7 +72,7 @@ public static Optional getSchema(Oas20Response response) { return Optional.ofNullable(response.schema); } - public static Optional getRequestBodySchema(Oas20Document openApiDoc, Oas20Operation operation) { + public static Optional getRequestBodySchema(@Nullable Oas20Document ignoredOpenApiDoc, Oas20Operation operation) { if (operation.parameters == null) { return Optional.empty(); } @@ -80,8 +80,8 @@ public static Optional getRequestBodySchema(Oas20Document openApiDoc, final List operationParameters = operation.parameters; Optional body = operationParameters.stream() - .filter(p -> "body".equals(p.in) && p.schema != null) - .findFirst(); + .filter(p -> "body".equals(p.in) && p.schema != null) + .findFirst(); return body.map(oasParameter -> (OasSchema) oasParameter.schema); } @@ -94,7 +94,7 @@ public static Optional getRequestContentType(Oas20Operation operation) { return Optional.empty(); } - public static Collection getResponseTypes(Oas20Operation operation, @Nullable Oas20Response response) { + public static Collection getResponseTypes(Oas20Operation operation,@Nullable Oas20Response ignoredResponse) { if (operation == null) { return Collections.emptyList(); } @@ -105,11 +105,11 @@ public static Collection getResponseTypes(Oas20Operation operation, @Nul * Returns the response content for random response generation. Note that this implementation currently only returns {@link MediaType#APPLICATION_JSON_VALUE}, * if this type exists. Otherwise, it will return an empty Optional. The reason for this is, that we cannot safely guess the type other than for JSON. * - * @param openApiDoc + * @param ignoredOpenApiDoc required to implement quasi interface but ignored in this implementation. * @param operation * @return */ - public static Optional getResponseContentTypeForRandomGeneration(@Nullable Oas20Document openApiDoc, Oas20Operation operation) { + public static Optional getResponseContentTypeForRandomGeneration(@Nullable Oas20Document ignoredOpenApiDoc, Oas20Operation operation) { if (operation.produces != null) { for (String mediaType : operation.produces) { if (MediaType.APPLICATION_JSON_VALUE.equals(mediaType)) { @@ -154,7 +154,7 @@ public static Map getHeaders(Oas20Response response) { } return response.headers.getHeaders().stream() - .collect(Collectors.toMap(OasHeader::getName, Oas20ModelHelper::getHeaderSchema)); + .collect(Collectors.toMap(OasHeader::getName, Oas20ModelHelper::getHeaderSchema)); } /** diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/v3/Oas30ModelHelper.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/v3/Oas30ModelHelper.java index 0cdf5c9227..3b2c1995bc 100644 --- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/v3/Oas30ModelHelper.java +++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/v3/Oas30ModelHelper.java @@ -26,7 +26,6 @@ import io.apicurio.datamodels.openapi.v3.models.Oas30Parameter; import io.apicurio.datamodels.openapi.v3.models.Oas30RequestBody; import io.apicurio.datamodels.openapi.v3.models.Oas30Response; -import jakarta.annotation.Nullable; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; @@ -79,17 +78,17 @@ public static List getSchemes(Oas30Document openApiDoc) { } return openApiDoc.servers.stream() - .map(Oas30ModelHelper::resolveUrl) - .map(serverUrl -> { - try { - return new URL(serverUrl).getProtocol(); - } catch (MalformedURLException e) { - LOG.warn(String.format(NO_URL_ERROR_MESSAGE, serverUrl)); - return null; - } - }) - .filter(Objects::nonNull) - .toList(); + .map(Oas30ModelHelper::resolveUrl) + .map(serverUrl -> { + try { + return new URL(serverUrl).getProtocol(); + } catch (MalformedURLException e) { + LOG.warn(String.format(NO_URL_ERROR_MESSAGE, serverUrl)); + return null; + } + }) + .filter(Objects::nonNull) + .toList(); } public static String getBasePath(Oas30Document openApiDoc) { @@ -120,8 +119,8 @@ public static Map getSchemaDefinitions(Oas30Document openApiD } return openApiDoc.components.schemas.entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getKey, Entry::getValue)); + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, Entry::getValue)); } public static Optional getSchema(Oas30Response response) { @@ -131,11 +130,11 @@ public static Optional getSchema(Oas30Response response) { } return content.entrySet() - .stream() - .filter(entry -> !isFormDataMediaType(entry.getKey())) - .filter(entry -> entry.getValue().schema != null) - .map(entry -> (OasSchema) entry.getValue().schema) - .findFirst(); + .stream() + .filter(entry -> !isFormDataMediaType(entry.getKey())) + .filter(entry -> entry.getValue().schema != null) + .map(entry -> (OasSchema) entry.getValue().schema) + .findFirst(); } public static Optional getRequestBodySchema(Oas30Document openApiDoc, Oas30Operation operation) { @@ -146,8 +145,8 @@ public static Optional getRequestBodySchema(Oas30Document openApiDoc, Oas30RequestBody bodyToUse = operation.requestBody; if (openApiDoc.components != null - && openApiDoc.components.requestBodies != null - && bodyToUse.$ref != null) { + && openApiDoc.components.requestBodies != null + && bodyToUse.$ref != null) { bodyToUse = openApiDoc.components.requestBodies.get(OasModelHelper.getReferenceName(bodyToUse.$ref)); } @@ -156,12 +155,12 @@ public static Optional getRequestBodySchema(Oas30Document openApiDoc, } return bodyToUse.content.entrySet() - .stream() - .filter(entry -> !isFormDataMediaType(entry.getKey())) - .filter(entry -> entry.getValue().schema != null) - .findFirst() - .map(Map.Entry::getValue) - .map(oas30MediaType -> oas30MediaType.schema); + .stream() + .filter(entry -> !isFormDataMediaType(entry.getKey())) + .filter(entry -> entry.getValue().schema != null) + .findFirst() + .map(Map.Entry::getValue) + .map(oas30MediaType -> oas30MediaType.schema); } public static Optional getRequestContentType(Oas30Operation operation) { @@ -170,13 +169,13 @@ public static Optional getRequestContentType(Oas30Operation operation) { } return operation.requestBody.content.entrySet() - .stream() - .filter(entry -> entry.getValue().schema != null) - .map(Map.Entry::getKey) - .findFirst(); + .stream() + .filter(entry -> entry.getValue().schema != null) + .map(Map.Entry::getKey) + .findFirst(); } - public static Collection getResponseTypes(@Nullable Oas30Operation operation, Oas30Response response) { + public static Collection getResponseTypes(Oas30Operation operation, Oas30Response response) { if (operation == null) { return Collections.emptySet(); } @@ -195,12 +194,12 @@ public static Optional getResponseContentTypeForRandomGeneration(Oas30Do Optional responseForRandomGeneration = getResponseForRandomGeneration( openApiDoc, operation); return responseForRandomGeneration.map( - Oas30Response.class::cast).flatMap(res -> res.content.entrySet() - .stream() + Oas30Response.class::cast).flatMap(res -> res.content.entrySet() + .stream() .filter(entry -> MediaType.APPLICATION_JSON_VALUE.equals(entry.getKey())) - .filter(entry -> entry.getValue().schema != null) - .map(Map.Entry::getKey) - .findFirst()); + .filter(entry -> entry.getValue().schema != null) + .map(Map.Entry::getKey) + .findFirst()); } public static Optional getResponseForRandomGeneration(Oas30Document openApiDoc, Oas30Operation operation) { @@ -247,9 +246,9 @@ public static Map getRequiredHeaders(Oas30Response response) } return response.headers.entrySet() - .stream() - .filter(entry -> Boolean.TRUE.equals(entry.getValue().required)) - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().schema)); + .stream() + .filter(entry -> Boolean.TRUE.equals(entry.getValue().required)) + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().schema)); } public static Map getHeaders(Oas30Response response) { @@ -258,8 +257,8 @@ public static Map getHeaders(Oas30Response response) { } return response.headers.entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().schema)); + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().schema)); } private static boolean isFormDataMediaType(String type) {