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..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 @@ -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; @@ -51,6 +52,7 @@ import org.kie.kogito.serverless.workflow.parser.ServerlessWorkflowParser; 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; @@ -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 = metadata.get("errorMessage"); + if (errorMessage != null && !errorMessage.isBlank()) { + NodeFactory errorMessageNode = + factory.actionNode(parserContext.newId()).action(new ErrorExpressionActionSupplier(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/ErrorExpressionActionSupplier.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/ErrorExpressionActionSupplier.java new file mode 100644 index 00000000000..4331ea320db --- /dev/null +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/suppliers/ErrorExpressionActionSupplier.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.ErrorExpressionAction; + +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.ObjectCreationExpr; + +public class ErrorExpressionActionSupplier extends ErrorExpressionAction implements ExpressionSupplier { + + private ObjectCreationExpr expression; + + public ErrorExpressionActionSupplier(String lang, String expr, String inputVar) { + super(lang, expr, inputVar); + this.expression = ExpressionUtils.getObjectCreationExpr(ErrorExpressionAction.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/ErrorExpressionAction.java b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/ErrorExpressionAction.java new file mode 100644 index 00000000000..a4b1f1da11d --- /dev/null +++ b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/actions/ErrorExpressionAction.java @@ -0,0 +1,50 @@ +/* + * 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 com.fasterxml.jackson.databind.JsonNode; + +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()) { + 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()); + } + } + + private void setError(KogitoProcessContext context, String error) { + ((ProcessInstance) context.getProcessInstance()).setErrorState((NodeInstance) context.getNodeInstance(), new IllegalArgumentException(error)); + } +} 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); + } }