From f485b62adee984b1ad4b4cd2d66a65bdbea53916 Mon Sep 17 00:00:00 2001 From: Francisco Javier Tirado Sarti <65240126+fjtirado@users.noreply.github.com> Date: Fri, 6 Oct 2023 12:23:35 +0200 Subject: [PATCH] [KOGITO-9861] Fixing compilation issues with nested classes and arrays (#3242) * [KOGITO-9861] Fixing compilation issues with nested and arrays * [KOGITO-9861] Adding IT test --- .../workflow/WorkflowWorkItemHandler.java | 6 +- ...lassAnnotatedWorkflowHandlerGenerator.java | 24 +++++-- .../WorkflowOpenApiHandlerGenerator.java | 2 +- .../src/main/resources/application.properties | 3 +- .../src/main/resources/openapiarray.sw.json | 27 ++++++++ .../src/main/resources/specs/array.yaml | 25 ++++++++ .../quarkus/workflows/OpenAPIArrayFlowIT.java | 57 +++++++++++++++++ .../workflows/OpenAPIArrayMockService.java | 62 +++++++++++++++++++ 8 files changed, 198 insertions(+), 8 deletions(-) create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/openapiarray.sw.json create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/array.yaml create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayFlowIT.java create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayMockService.java diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/WorkflowWorkItemHandler.java b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/WorkflowWorkItemHandler.java index 9d87627bddb..557cb6c86dd 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/WorkflowWorkItemHandler.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/WorkflowWorkItemHandler.java @@ -26,7 +26,6 @@ import org.kie.kogito.internal.process.runtime.KogitoWorkItemHandler; import org.kie.kogito.internal.process.runtime.KogitoWorkItemManager; import org.kie.kogito.jackson.utils.JsonObjectUtils; -import org.kie.kogito.jackson.utils.ObjectMapperFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,10 +47,13 @@ public void executeWorkItem(KogitoWorkItem workItem, KogitoWorkItemManager manag protected static V buildBody(Map params, Class clazz) { for (Object obj : params.values()) { if (obj != null && clazz.isAssignableFrom(obj.getClass())) { + logger.trace("Invoking workitemhandler with value {}", obj); return clazz.cast(obj); } } - return ObjectMapperFactory.get().convertValue(params, clazz); + V value = JsonObjectUtils.convertValue(params, clazz); + logger.trace("Invoking workitemhandler with value {}", value); + return value; } @Override diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/ClassAnnotatedWorkflowHandlerGenerator.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/ClassAnnotatedWorkflowHandlerGenerator.java index 32fce36910e..95d117f8859 100644 --- a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/ClassAnnotatedWorkflowHandlerGenerator.java +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/ClassAnnotatedWorkflowHandlerGenerator.java @@ -30,6 +30,7 @@ import org.kie.kogito.codegen.api.context.KogitoBuildContext; import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.type.ClassOrInterfaceType; import static com.github.javaparser.StaticJavaParser.parseClassOrInterfaceType; import static com.github.javaparser.StaticJavaParser.parseType; @@ -46,16 +47,31 @@ public Collection generateHandlerClasses(KogitoBui protected abstract Stream generateHandler(KogitoBuildContext context, AnnotationInstance a); protected final com.github.javaparser.ast.type.Type fromClass(Type param) { + return fromClass(param, true); + } + + protected final com.github.javaparser.ast.type.Type fromClass(Type param, boolean includeGeneric) { switch (param.kind()) { case CLASS: - return parseClassOrInterfaceType(param.asClassType().name().toString()); + return parseClassOrInterfaceType(fromDotName(param.asClassType().name())); case PRIMITIVE: - return parseType(param.asPrimitiveType().name().toString()); + return parseType(fromDotName(param.asPrimitiveType().name())); case PARAMETERIZED_TYPE: - return parseClassOrInterfaceType(param.asParameterizedType().name().toString()) - .setTypeArguments(NodeList.nodeList(param.asParameterizedType().arguments().stream().map(this::fromClass).collect(Collectors.toList()))); + ClassOrInterfaceType result = parseClassOrInterfaceType(fromDotName(param.asParameterizedType().name())); + if (includeGeneric) { + result.setTypeArguments(NodeList.nodeList(param.asParameterizedType().arguments().stream().map(this::fromClass).collect(Collectors.toList()))); + } + return result; default: throw new UnsupportedOperationException("Kind " + param.kind() + " is not supported"); } } + + private String fromDotName(DotName dotName) { + String result = dotName.toString(); + if (dotName.isInner()) { + result = result.replace('$', '.'); + } + return result; + } } diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/openapi/WorkflowOpenApiHandlerGenerator.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/openapi/WorkflowOpenApiHandlerGenerator.java index 36c97d8d732..f502b34a834 100644 --- a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/openapi/WorkflowOpenApiHandlerGenerator.java +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/openapi/WorkflowOpenApiHandlerGenerator.java @@ -109,7 +109,7 @@ private WorkflowHandlerGeneratedFile generateHandler(KogitoBuildContext context, if (annotation != null) { methodCallExpr.addArgument(new CastExpr(fromClass(param), new MethodCallExpr(parameters, "remove").addArgument(new StringLiteralExpr(annotation.value().asString())))); } else { - methodCallExpr.addArgument(new MethodCallExpr("buildBody").addArgument(parameters).addArgument(new ClassExpr(fromClass(param)))); + methodCallExpr.addArgument(new MethodCallExpr("buildBody").addArgument(parameters).addArgument(new ClassExpr(fromClass(param, false)))); } } clazz.addMethod("getRestClass", Keyword.PROTECTED).setType(parseClassOrInterfaceType(Class.class.getCanonicalName()).setTypeArguments(classNameType)) diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/application.properties b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/application.properties index 464e0b8e6ea..238674ad345 100644 --- a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/application.properties +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/application.properties @@ -9,7 +9,7 @@ quarkus.flyway.clean-at-start=true quarkus.http.test-port=0 quarkus.log.level=INFO -#quarkus.log.category."org.kie.kogito".level=DEBUG +#quarkus.log.category."org.kie.kogito.serverless.workflow".level=DEBUG # To include the greethidden workflow kogito.codegen.ignoreHiddenFiles=false @@ -20,6 +20,7 @@ quarkus.kubernetes-client.devservices.enabled=false # OpenApi client properties, see OperationsMockService, which is mocking these two services quarkus.rest-client.multiplication.url=${multiplication-service-mock.url} quarkus.rest-client.subtraction.url=${subtraction-service-mock.url} +quarkus.rest-client.array_yaml.url=${array-service-mock.url} # OpenApi client properties to access the general purpose external-service, which is mocked by the ExternalServiceMock quarkus.rest-client.external_service_yaml.url=${external-service-mock.url} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/openapiarray.sw.json b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/openapiarray.sw.json new file mode 100644 index 00000000000..32645efec4f --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/openapiarray.sw.json @@ -0,0 +1,27 @@ +{ + "id": "openapiarray", + "name": "Open API Array Test", + "version": "v1.0", + "start": "DoIt", + "functions": [ + { + "name": "testArray", + "operation": "specs/array.yaml#testArray" + } + ], + "states": [ + { + "name": "DoIt", + "type": "operation", + "actions": [ + { + "name": "testArray", + "functionRef": { + "refName": "testArray", + "arguments": ".inputArray" + } + }], + "end": true + } + ] +} \ No newline at end of file diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/array.yaml b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/array.yaml new file mode 100644 index 00000000000..14fe0ad7746 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/array.yaml @@ -0,0 +1,25 @@ +--- +openapi: 3.0.3 +info: + title: Generated API + version: "1.0" +paths: + /testArray: + post: + operationId: testArray + requestBody: + content: + application/json: + schema: + type: array + items: + type: number + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + type: number diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayFlowIT.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayFlowIT.java new file mode 100644 index 00000000000..c4923c191bb --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayFlowIT.java @@ -0,0 +1,57 @@ +/* + * 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.kie.kogito.quarkus.workflows; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; + +@QuarkusTestResource(OpenAPIArrayMockService.class) +@QuarkusIntegrationTest +class OpenAPIArrayFlowIT { + + @BeforeAll + static void init() { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + @Test + void testArray() { + given() + .contentType(ContentType.JSON) + .when() + .body(Collections.singletonMap("inputArray", Arrays.asList(1, 2, 3, 4))) + .post("/openapiarray") + .then() + .statusCode(201) + .body("id", notNullValue()) + .body("workflowdata.response", is(Arrays.asList(1, 2, 3, 4))); + } +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayMockService.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayMockService.java new file mode 100644 index 00000000000..c67a911d8e3 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayMockService.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.kie.kogito.quarkus.workflows; + +import java.util.Map; +import java.util.function.UnaryOperator; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.MappingBuilder; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +public class OpenAPIArrayMockService implements QuarkusTestResourceLifecycleManager { + + private static WireMockServer arrayService; + + @Override + public Map start() { + arrayService = startServer("[1,2,3,4]", p -> p); + return Map.of("array-service-mock.url", arrayService.baseUrl()); + } + + @Override + public void stop() { + if (arrayService != null) { + arrayService.stop(); + } + } + + private static WireMockServer startServer(final String response, UnaryOperator function) { + final WireMockServer server = new WireMockServer(options().dynamicPort()); + server.start(); + server.stubFor(function.apply(post(urlEqualTo("/testArray")).withRequestBody(equalToJson("[1,2,3,4]"))) + .withPort(server.port()) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody(response))); + return server; + } +}