From e5bfa814dc4af4dd18c41887845ff8203a79b4c8 Mon Sep 17 00:00:00 2001 From: Francisco Javier Tirado Sarti Date: Tue, 30 Apr 2024 14:39:42 +0200 Subject: [PATCH 1/3] [Fix #3475] Parsing the errorMessage metadata keys in end nodes --- .../parser/handlers/StateHandler.java | 15 +++++- .../AbortExpressionActionSupplier.java | 43 +++++++++++++++++ .../actions/AbortExpressionAction.java | 48 +++++++++++++++++++ .../actions/AbortExpressionException.java | 25 ++++++++++ 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/AbortExpressionActionSupplier.java create mode 100644 kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionAction.java create mode 100644 kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionException.java diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/StateHandler.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/StateHandler.java index 599ea4a3dd2..fc6741e0974 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/StateHandler.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/StateHandler.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.List; import java.util.ListIterator; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; @@ -49,6 +50,7 @@ import org.kie.kogito.serverless.workflow.SWFConstants; import org.kie.kogito.serverless.workflow.parser.ParserContext; import org.kie.kogito.serverless.workflow.parser.ServerlessWorkflowParser; +import org.kie.kogito.serverless.workflow.suppliers.AbortExpressionActionSupplier; import org.kie.kogito.serverless.workflow.suppliers.CollectorActionSupplier; import org.kie.kogito.serverless.workflow.suppliers.CompensationActionSupplier; import org.kie.kogito.serverless.workflow.suppliers.ExpressionActionSupplier; @@ -517,15 +519,26 @@ protected final void createTimerNode(RuleFlowNodeContainerFactory factory, WorkflowElementIdentifier id = parserContext.newId(); EndNodeFactory nodeFactory = factory.endNode(id); NodeFactory startNode = nodeFactory; + List produceEvents = end.getProduceEvents(); if (produceEvents != null && !produceEvents.isEmpty()) { startNode = handleProduceEvents(factory, nodeFactory, produceEvents); } + + Map metadata = state.getMetadata(); + if (metadata != null) { + String errorMessage = state.getMetadata().get("errorMessage"); + if (errorMessage != null && !errorMessage.isBlank()) { + NodeFactory errorMessageNode = + factory.actionNode(parserContext.newId()).action(new AbortExpressionActionSupplier(workflow.getExpressionLang(), errorMessage, SWFConstants.DEFAULT_WORKFLOW_VAR)); + connect(errorMessageNode, startNode); + startNode = errorMessageNode; + } + } nodeFactory.terminate(end.isTerminate()); return startNode; } - @SuppressWarnings("squid:S1452") private NodeFactory compensationEvent(RuleFlowNodeContainerFactory factory, NodeFactory sourceFactory) { WorkflowElementIdentifier eventId = parserContext.newId(); NodeFactory compensationNode = diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/AbortExpressionActionSupplier.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/AbortExpressionActionSupplier.java new file mode 100644 index 00000000000..d4d4327aa1a --- /dev/null +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/AbortExpressionActionSupplier.java @@ -0,0 +1,43 @@ +/* + * 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.serverless.workflow.suppliers; + +import org.jbpm.compiler.canonical.ExpressionSupplier; +import org.jbpm.compiler.canonical.ProcessMetaData; +import org.jbpm.compiler.canonical.descriptors.ExpressionUtils; +import org.kie.kogito.internal.process.runtime.KogitoNode; +import org.kie.kogito.serverless.workflow.actions.AbortExpressionAction; + +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.ObjectCreationExpr; + +public class AbortExpressionActionSupplier extends AbortExpressionAction implements ExpressionSupplier { + + private ObjectCreationExpr expression; + + public AbortExpressionActionSupplier(String lang, String expr, String inputVar) { + super(lang, expr, inputVar); + this.expression = ExpressionUtils.getObjectCreationExpr(AbortExpressionAction.class, lang, expr, inputVar); + } + + @Override + public Expression get(KogitoNode node, ProcessMetaData metadata) { + return expression; + } +} diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionAction.java b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionAction.java new file mode 100644 index 00000000000..e316aab33c4 --- /dev/null +++ b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionAction.java @@ -0,0 +1,48 @@ +/* + * 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.serverless.workflow.actions; + +import org.jbpm.process.instance.ProcessInstance; +import org.jbpm.workflow.instance.NodeInstance; +import org.kie.kogito.internal.process.runtime.KogitoProcessContext; +import org.kie.kogito.internal.process.runtime.KogitoProcessInstance; + +public class AbortExpressionAction extends BaseExpressionAction { + + public AbortExpressionAction(String lang, String expr, String inputVar) { + super(lang, expr, inputVar); + } + + public void execute(KogitoProcessContext context) throws Exception { + if (expr.isValid()) { + String error = evaluate(context, String.class); + if (error != null) { + setError(context, error); + } + } else { + setError(context, expr.toString()); + } + } + + private void setError(KogitoProcessContext context, String error) { + ProcessInstance pi = (ProcessInstance) context.getProcessInstance(); + pi.setErrorState((NodeInstance) context.getNodeInstance(), new AbortExpressionException(error)); + pi.setState(KogitoProcessInstance.STATE_ABORTED); + } +} diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionException.java b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionException.java new file mode 100644 index 00000000000..c1520e792fd --- /dev/null +++ b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionException.java @@ -0,0 +1,25 @@ +/* + * 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.serverless.workflow.actions; + +public class AbortExpressionException extends RuntimeException { + public AbortExpressionException(String message) { + super(message); + } +} From 741372ec883c390569f78236e760b3b928acf0e1 Mon Sep 17 00:00:00 2001 From: Francisco Javier Tirado Sarti Date: Tue, 30 Apr 2024 19:30:44 +0200 Subject: [PATCH 2/3] [Fix #3475] Do not abort process instance --- .../parser/handlers/StateHandler.java | 6 ++--- ...ava => ErrorExpressionActionSupplier.java} | 8 +++--- .../actions/AbortExpressionException.java | 25 ------------------- ...Action.java => ErrorExpressionAction.java} | 20 ++++++++------- 4 files changed, 18 insertions(+), 41 deletions(-) rename kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/{AbortExpressionActionSupplier.java => ErrorExpressionActionSupplier.java} (86%) delete mode 100644 kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionException.java rename kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/{AbortExpressionAction.java => ErrorExpressionAction.java} (69%) diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/StateHandler.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/StateHandler.java index fc6741e0974..3cec858b915 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/StateHandler.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/StateHandler.java @@ -50,9 +50,9 @@ import org.kie.kogito.serverless.workflow.SWFConstants; import org.kie.kogito.serverless.workflow.parser.ParserContext; import org.kie.kogito.serverless.workflow.parser.ServerlessWorkflowParser; -import org.kie.kogito.serverless.workflow.suppliers.AbortExpressionActionSupplier; import org.kie.kogito.serverless.workflow.suppliers.CollectorActionSupplier; import org.kie.kogito.serverless.workflow.suppliers.CompensationActionSupplier; +import org.kie.kogito.serverless.workflow.suppliers.ErrorExpressionActionSupplier; import org.kie.kogito.serverless.workflow.suppliers.ExpressionActionSupplier; import org.kie.kogito.serverless.workflow.suppliers.MergeActionSupplier; import org.kie.kogito.serverless.workflow.suppliers.ProduceEventActionSupplier; @@ -527,10 +527,10 @@ protected final void createTimerNode(RuleFlowNodeContainerFactory factory, Map metadata = state.getMetadata(); if (metadata != null) { - String errorMessage = state.getMetadata().get("errorMessage"); + String errorMessage = metadata.get("errorMessage"); if (errorMessage != null && !errorMessage.isBlank()) { NodeFactory errorMessageNode = - factory.actionNode(parserContext.newId()).action(new AbortExpressionActionSupplier(workflow.getExpressionLang(), errorMessage, SWFConstants.DEFAULT_WORKFLOW_VAR)); + factory.actionNode(parserContext.newId()).action(new ErrorExpressionActionSupplier(workflow.getExpressionLang(), errorMessage, SWFConstants.DEFAULT_WORKFLOW_VAR)); connect(errorMessageNode, startNode); startNode = errorMessageNode; } diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/AbortExpressionActionSupplier.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/ErrorExpressionActionSupplier.java similarity index 86% rename from kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/AbortExpressionActionSupplier.java rename to kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/ErrorExpressionActionSupplier.java index d4d4327aa1a..4331ea320db 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/AbortExpressionActionSupplier.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/ErrorExpressionActionSupplier.java @@ -22,18 +22,18 @@ import org.jbpm.compiler.canonical.ProcessMetaData; import org.jbpm.compiler.canonical.descriptors.ExpressionUtils; import org.kie.kogito.internal.process.runtime.KogitoNode; -import org.kie.kogito.serverless.workflow.actions.AbortExpressionAction; +import org.kie.kogito.serverless.workflow.actions.ErrorExpressionAction; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.ObjectCreationExpr; -public class AbortExpressionActionSupplier extends AbortExpressionAction implements ExpressionSupplier { +public class ErrorExpressionActionSupplier extends ErrorExpressionAction implements ExpressionSupplier { private ObjectCreationExpr expression; - public AbortExpressionActionSupplier(String lang, String expr, String inputVar) { + public ErrorExpressionActionSupplier(String lang, String expr, String inputVar) { super(lang, expr, inputVar); - this.expression = ExpressionUtils.getObjectCreationExpr(AbortExpressionAction.class, lang, expr, inputVar); + this.expression = ExpressionUtils.getObjectCreationExpr(ErrorExpressionAction.class, lang, expr, inputVar); } @Override diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionException.java b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionException.java deleted file mode 100644 index c1520e792fd..00000000000 --- a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionException.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.serverless.workflow.actions; - -public class AbortExpressionException extends RuntimeException { - public AbortExpressionException(String message) { - super(message); - } -} diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionAction.java b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/ErrorExpressionAction.java similarity index 69% rename from kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionAction.java rename to kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/ErrorExpressionAction.java index e316aab33c4..a4b1f1da11d 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/AbortExpressionAction.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/ErrorExpressionAction.java @@ -21,19 +21,23 @@ import org.jbpm.process.instance.ProcessInstance; import org.jbpm.workflow.instance.NodeInstance; import org.kie.kogito.internal.process.runtime.KogitoProcessContext; -import org.kie.kogito.internal.process.runtime.KogitoProcessInstance; -public class AbortExpressionAction extends BaseExpressionAction { +import com.fasterxml.jackson.databind.JsonNode; - public AbortExpressionAction(String lang, String expr, String inputVar) { +public class ErrorExpressionAction extends BaseExpressionAction { + + public ErrorExpressionAction(String lang, String expr, String inputVar) { super(lang, expr, inputVar); } public void execute(KogitoProcessContext context) throws Exception { if (expr.isValid()) { - String error = evaluate(context, String.class); - if (error != null) { - setError(context, error); + JsonNode error = evaluate(context, JsonNode.class); + if (!error.isNull() && error.isTextual()) { + String errorStr = error.asText(); + if (!errorStr.isBlank()) { + setError(context, errorStr); + } } } else { setError(context, expr.toString()); @@ -41,8 +45,6 @@ public void execute(KogitoProcessContext context) throws Exception { } private void setError(KogitoProcessContext context, String error) { - ProcessInstance pi = (ProcessInstance) context.getProcessInstance(); - pi.setErrorState((NodeInstance) context.getNodeInstance(), new AbortExpressionException(error)); - pi.setState(KogitoProcessInstance.STATE_ABORTED); + ((ProcessInstance) context.getProcessInstance()).setErrorState((NodeInstance) context.getNodeInstance(), new IllegalArgumentException(error)); } } From 7a156070605209612309bf2550d437b34892799e Mon Sep 17 00:00:00 2001 From: Francisco Javier Tirado Sarti Date: Fri, 3 May 2024 11:22:12 +0200 Subject: [PATCH 3/3] [Fix #3475] Adding IT test --- .../main/resources/errorWithMetadata.sw.json | 18 ++++++++++++++++ .../kogito/quarkus/workflows/ErrorRestIT.java | 21 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorWithMetadata.sw.json diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorWithMetadata.sw.json b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorWithMetadata.sw.json new file mode 100644 index 00000000000..7a7a9551e9a --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorWithMetadata.sw.json @@ -0,0 +1,18 @@ +{ + "id": "errorWithMetadata", + "version": "1.0", + "name": "Workflow Error example with metadata", + "description": "An example of how to abort a workflow with error given a condition", + "start": "checkEven", + "states": [ + { + "name": "checkEven", + "type": "operation", + "actions": [], + "end" : true, + "metadata": { + "errorMessage": "if .number % 2 != 0 then \"Is Odd number!!!!\" else null end" + } + } + ] +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ErrorRestIT.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ErrorRestIT.java index 07e2ad9a2cd..84afb411927 100644 --- a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ErrorRestIT.java +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ErrorRestIT.java @@ -75,4 +75,25 @@ private void innerErrorRest(String workflowId) { .body("workflowdata.numberType", is("even")) .body("workflowdata.perfect", is(false)); } + + @Test + public void testErrorWithMetadata() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"number\" : 12342}") + .when() + .post("/errorWithMetadata") + .then() + .statusCode(201); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"number\" : 12341}") + .when() + .post("/errorWithMetadata") + .then() + .statusCode(400); + } }