From 874f02f5a5fd8c34ca71a0e88d7f34dae550fee4 Mon Sep 17 00:00:00 2001 From: Tomasz Michalak Date: Mon, 4 Nov 2019 15:21:10 +0100 Subject: [PATCH 1/5] Adding composite node identifier, renamed Action node to Single Node --- api/README.md | 14 -- .../task/ConfigurationTaskProvider.java | 6 +- .../task/ConfigurationTaskProviderTest.java | 28 ++-- handler/engine/README.md | 64 ++++---- .../io/knotx/fragments/engine/TaskEngine.java | 7 +- .../fragments/engine/graph/CompositeNode.java | 14 +- .../io/knotx/fragments/engine/graph/Node.java | 2 +- .../fragments/engine/graph/NodeType.java | 21 +++ .../{ActionNode.java => SingleNode.java} | 10 +- .../FragmentsEngineConcurrencyTest.java | 4 +- .../engine/FragmentsEngineOrderTest.java | 4 +- .../engine/TaskEngineCompositeNodeTest.java | 139 +++++++++++------- .../engine/TaskEngineScenariosTest.java | 94 ++++++------ .../engine/TaskEngineSingleOperationTest.java | 36 +++-- 14 files changed, 241 insertions(+), 202 deletions(-) delete mode 100644 api/README.md create mode 100644 handler/engine/src/main/java/io/knotx/fragments/engine/graph/NodeType.java rename handler/engine/src/main/java/io/knotx/fragments/engine/graph/{ActionNode.java => SingleNode.java} (88%) diff --git a/api/README.md b/api/README.md deleted file mode 100644 index 6edd7863..00000000 --- a/api/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Knot.x Fragment API -This repository contains API model for the Fragments. -A **Fragment** is a piece of any kind of document that can be processed independently (e.g. -an HTML snippet that represents the shopping cart or a JSON containing person's bookshelf). - -Originally it may came from breaking a bigger document into smaller pieces. -Fragment is not associated in any way with any particular format, so examples of the fragment may be: -- piece of HTML markup, -- piece of PDF file, -- piece of Office document (e.g. Word paragraph or Excel row), -- some (binary) part of an image. - -Fragment is a Data Object and you can read more about it composition in the -[Fragment Data Object docs](https://github.com/Knotx/knotx-fragments/blob/master/api/docs/asciidoc/dataobjects.adoc). diff --git a/handler/core/src/main/java/io/knotx/fragments/task/ConfigurationTaskProvider.java b/handler/core/src/main/java/io/knotx/fragments/task/ConfigurationTaskProvider.java index eb753908..3071a29e 100644 --- a/handler/core/src/main/java/io/knotx/fragments/task/ConfigurationTaskProvider.java +++ b/handler/core/src/main/java/io/knotx/fragments/task/ConfigurationTaskProvider.java @@ -22,7 +22,7 @@ import io.knotx.fragments.engine.FragmentEventContext; import io.knotx.fragments.engine.Task; -import io.knotx.fragments.engine.graph.ActionNode; +import io.knotx.fragments.engine.graph.SingleNode; import io.knotx.fragments.engine.graph.CompositeNode; import io.knotx.fragments.engine.graph.Node; import io.knotx.fragments.handler.action.ActionProvider; @@ -73,7 +73,7 @@ private Node buildActionNode(GraphNodeOptions options, Map edges) ActionNodeConfigOptions config = new ActionNodeConfigOptions(options.getNode().getConfig()); Action action = actionProvider.get(config.getAction()).orElseThrow( () -> new GraphConfigurationException("No provider for action " + config.getAction())); - return new ActionNode(config.getAction(), toRxFunction(action), edges); + return new SingleNode(config.getAction(), toRxFunction(action), edges); } private Node buildCompositeNode(GraphNodeOptions options, Map edges) { @@ -82,7 +82,7 @@ private Node buildCompositeNode(GraphNodeOptions options, Map edge List nodes = config.getSubtasks().stream() .map(this::initGraphRootNode) .collect(Collectors.toList()); - return new CompositeNode(nodes, edges.get(SUCCESS_TRANSITION), edges.get(ERROR_TRANSITION)); + return new CompositeNode("composite", nodes, edges.get(SUCCESS_TRANSITION), edges.get(ERROR_TRANSITION)); } private Function> toRxFunction( diff --git a/handler/core/src/test/java/io/knotx/fragments/task/ConfigurationTaskProviderTest.java b/handler/core/src/test/java/io/knotx/fragments/task/ConfigurationTaskProviderTest.java index f011835c..a403c3ab 100644 --- a/handler/core/src/test/java/io/knotx/fragments/task/ConfigurationTaskProviderTest.java +++ b/handler/core/src/test/java/io/knotx/fragments/task/ConfigurationTaskProviderTest.java @@ -17,7 +17,6 @@ */ package io.knotx.fragments.task; -import static io.knotx.fragments.engine.graph.CompositeNode.COMPOSITE_NODE_ID; import static io.knotx.fragments.handler.api.domain.FragmentResult.ERROR_TRANSITION; import static io.knotx.fragments.handler.api.domain.FragmentResult.SUCCESS_TRANSITION; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -30,7 +29,7 @@ import io.knotx.fragments.engine.FragmentEvent; import io.knotx.fragments.engine.FragmentEventContext; import io.knotx.fragments.engine.Task; -import io.knotx.fragments.engine.graph.ActionNode; +import io.knotx.fragments.engine.graph.SingleNode; import io.knotx.fragments.engine.graph.CompositeNode; import io.knotx.fragments.engine.graph.Node; import io.knotx.fragments.handler.action.ActionProvider; @@ -62,6 +61,7 @@ class ConfigurationTaskProviderTest { private static final Map NO_TRANSITIONS = Collections.emptyMap(); private static final String TASK_NAME = "task"; + private static final String COMPOSITE_NODE_ID = "composite"; private static final FragmentEventContext SAMPLE_FRAGMENT_EVENT = new FragmentEventContext(new FragmentEvent(new Fragment("type", new JsonObject().put(FragmentsHandlerOptions.DEFAULT_TASK_KEY, TASK_NAME), "body")), @@ -124,7 +124,7 @@ void expectSingleActionNodeGraph() { assertEquals(TASK_NAME, task.getName()); assertTrue(task.getRootNode().isPresent()); Node rootNode = task.getRootNode().get(); - assertTrue(rootNode instanceof ActionNode); + assertTrue(rootNode instanceof SingleNode); assertEquals("simpleAction", rootNode.getId()); assertFalse(rootNode.next(SUCCESS_TRANSITION).isPresent()); } @@ -148,12 +148,12 @@ void expectActionNodesGraphWithTransition() { assertTrue(task.getRootNode().isPresent()); Node rootNode = task.getRootNode().get(); - assertTrue(rootNode instanceof ActionNode); + assertTrue(rootNode instanceof SingleNode); assertEquals("actionA", rootNode.getId()); Optional customNode = rootNode.next("customTransition"); assertTrue(customNode.isPresent()); - assertTrue(customNode.get() instanceof ActionNode); - ActionNode customSingleNode = (ActionNode) customNode.get(); + assertTrue(customNode.get() instanceof SingleNode); + SingleNode customSingleNode = (SingleNode) customNode.get(); assertEquals("actionB", customSingleNode.getId()); } @@ -176,14 +176,14 @@ void expectSingleCompositeNodeGraphWithNoEdges() { assertTrue(task.getRootNode().isPresent()); Node rootNode = task.getRootNode().get(); assertTrue(rootNode instanceof CompositeNode); - assertEquals(COMPOSITE_NODE_ID, rootNode.getId()); + assertEquals("composite", rootNode.getId()); assertFalse(rootNode.next(SUCCESS_TRANSITION).isPresent()); assertFalse(rootNode.next(ERROR_TRANSITION).isPresent()); CompositeNode compositeRootNode = (CompositeNode) rootNode; assertEquals(1, compositeRootNode.getNodes().size()); Node node = compositeRootNode.getNodes().get(0); - assertTrue(node instanceof ActionNode); + assertTrue(node instanceof SingleNode); assertEquals("simpleAction", node.getId()); } @@ -209,11 +209,11 @@ void expectCompositeNodeWithSingleNodeOnSuccessGraph() { assertTrue(task.getRootNode().isPresent()); Node rootNode = task.getRootNode().get(); assertTrue(rootNode instanceof CompositeNode); - assertEquals(COMPOSITE_NODE_ID, rootNode.getId()); + assertEquals("composite", rootNode.getId()); Optional onSuccess = rootNode.next(SUCCESS_TRANSITION); assertTrue(onSuccess.isPresent()); Node onSuccessNode = onSuccess.get(); - assertTrue(onSuccessNode instanceof ActionNode); + assertTrue(onSuccessNode instanceof SingleNode); assertEquals("lastAction", onSuccessNode.getId()); } @@ -238,11 +238,11 @@ void expectCompositeNodeWithSingleNodeOnErrorGraph() { assertTrue(task.getRootNode().isPresent()); Node rootNode = task.getRootNode().get(); assertTrue(rootNode instanceof CompositeNode); - assertEquals(COMPOSITE_NODE_ID, rootNode.getId()); + assertEquals("composite", rootNode.getId()); Optional onError = rootNode.next(ERROR_TRANSITION); assertTrue(onError.isPresent()); Node onErrorNode = onError.get(); - assertTrue(onErrorNode instanceof ActionNode); + assertTrue(onErrorNode instanceof SingleNode); assertEquals("fallbackAction", onErrorNode.getId()); } @@ -291,7 +291,7 @@ void expectNestedCompositeNodesGraph() { assertTrue(task.getRootNode().isPresent()); Node rootNode = task.getRootNode().get(); assertTrue(rootNode instanceof CompositeNode); - assertEquals(COMPOSITE_NODE_ID, rootNode.getId()); + assertEquals("composite", rootNode.getId()); CompositeNode compositeRootNode = (CompositeNode) rootNode; assertEquals(1, compositeRootNode.getNodes().size()); @@ -302,7 +302,7 @@ void expectNestedCompositeNodesGraph() { assertEquals(1, compositeChildNode.getNodes().size()); Node node = compositeChildNode.getNodes().get(0); - assertTrue(node instanceof ActionNode); + assertTrue(node instanceof SingleNode); assertEquals("simpleAction", node.getId()); } diff --git a/handler/engine/README.md b/handler/engine/README.md index 96a6f833..d3f47823 100644 --- a/handler/engine/README.md +++ b/handler/engine/README.md @@ -2,48 +2,56 @@ Fragments Engine is a reactive asynchronous map-reduce implementation, enjoying the benefits of Reactive Extensions, that evaluates each Fragment independently using a `Task` definition. `Task` specifies a directed graph of Nodes, allowing to transform Fragment into the new one. -``` -F -> F', T -``` -- `F` - Fragment to transform -- `F'` - the modified Fragment -- `T` represents Transition, a text value, that defines the next Node from the graph ## How does it work Any *Fragment* can define its processing path - a **Task** (which is a **directed graph** of **Nodes**). A **Task** specifies the nodes through which Fragments will be routed by the Task Engine. Each Node may define possible *outgoing edges* - **Transitions**. -Additionally, a **Node** can do one of the following: - - define a **single** [Action](https://github.com/Knotx/knotx-fragments/tree/master/handler/api#action) - that will be applied to Fragment (called [Action Node](#action-node)), - - define **many**/**parallel** Actions that are applied to Fragment (called [Composite Node](#composite-node)) . - -### Action Node -Action that is applied on the node is a transformation function -`java.util.function.Function>` that transforms one Fragment -into another. - -Part of the `FragmentResult` is a *Transition* that defines the next *Node* in the graph that the Fragment -should visit. If there is no transition defined, default `_success` value is used. -Action Node transformation may return any *Transition*, but all the transitions but `_success` must be -configured. If there is no **path** configured for the transition, the following logic is applied: - - if the *Transition* equals `_success` (default value), graph processing finishes - - otherwise "Unsupported Transition" error occurs. +### Node +The node responsibility can be described as: +> Graph node gets a fragment, processes it and responds with Transition. So a node is the function +>`F -> (F', T)` where `F` is the Fragment, `F'` is a modified Fragment and `T` is the Transition. + +The node definition is abstract. It allows to define simple processing nodes but also more complex +structures such as a list of subgraphs. + +There are two **node** types: + - **simple nodes** that are simple operations that do some fragment modifications (called [Single Node](#single-node)), + - **parallel complex nodes** that represent a list of subgraphs (called [Composite Node](#composite-node)). + +### Single Node +The node represents a single operation that transforms one Fragment into another. + +This operation can respond with **custom** transitions. ### Composite Node -This Node may consist of other Composite Nodes or Action Nodes or a mix of both. -It enables parallel processing of independent Actions (e.g. calling two external data sources). +This node may consist of other Composite Nodes or Single Nodes or a mix of both. +It enables parallel processing of independent nodes (e.g. calling two external data sources). Composite Node may define only two transitions: - `_success` - the default one, means that operation ends without any exception - `_error` - when operation throws an exception > Important note! -> Action Nodes inside the Composite Node may only modify the Fragment's payload and should not modify the Fragment's body. -> This is because Actions are executed in parallel and the output of modifying a single Fragment's body in parallel -> may differ between different executions. +> Single Nodes inside the Composite Node may only modify the Fragment's payload and should not modify the Fragment's body. + +### Transition +A directed graph consists of nodes and edges. Edges are called transitions. Transition is a simple text. + +The pre-defined transitions are: +- `_success` - the default one, indicates that operation completes successfully (no exception) +- `_error` - means that operation has throw an exception + +There are two important rules to remember: +> If a node responds with *_success* transition, but the transition is not configured, then +>processing is finished. + +> If a node responds with *_error* transition, but the transition is not configured, then an +>exception is returned. + +> If a node responds with a not configured transition, the "Unsupported Transition" error occurs. -## Node states +#### States ![Node with exits](assets/images/graph_node.png) diff --git a/handler/engine/src/main/java/io/knotx/fragments/engine/TaskEngine.java b/handler/engine/src/main/java/io/knotx/fragments/engine/TaskEngine.java index f5936f6a..59729d50 100644 --- a/handler/engine/src/main/java/io/knotx/fragments/engine/TaskEngine.java +++ b/handler/engine/src/main/java/io/knotx/fragments/engine/TaskEngine.java @@ -15,7 +15,8 @@ */ package io.knotx.fragments.engine; -import io.knotx.fragments.engine.graph.ActionNode; +import io.knotx.fragments.engine.graph.NodeType; +import io.knotx.fragments.engine.graph.SingleNode; import io.knotx.fragments.engine.graph.CompositeNode; import io.knotx.fragments.engine.graph.Node; import io.knotx.fragments.handler.api.domain.FragmentResult; @@ -59,14 +60,14 @@ private Single processTask(TaskExecutionContext context, N } private Single getResult(TaskExecutionContext context) { - return context.getCurrentNode().isComposite() + return NodeType.COMPOSITE.equals(context.getCurrentNode().getType()) ? mapReduce(context) : execute(context); } private Single execute(TaskExecutionContext context) { return Single.just(context.getCurrentNode()) - .map(ActionNode.class::cast) + .map(SingleNode.class::cast) .observeOn(RxHelper.blockingScheduler(vertx)) .flatMap(gn -> gn.doAction(context.fragmentContextInstance())) .doOnSuccess(fr -> context.handleSuccess(fr.getTransition())) diff --git a/handler/engine/src/main/java/io/knotx/fragments/engine/graph/CompositeNode.java b/handler/engine/src/main/java/io/knotx/fragments/engine/graph/CompositeNode.java index 22235adb..27696beb 100644 --- a/handler/engine/src/main/java/io/knotx/fragments/engine/graph/CompositeNode.java +++ b/handler/engine/src/main/java/io/knotx/fragments/engine/graph/CompositeNode.java @@ -23,12 +23,13 @@ public class CompositeNode implements Node { - public static final String COMPOSITE_NODE_ID = "composite"; + private final String id; private final List nodes; private final Node onSuccess; private final Node onError; - public CompositeNode(List nodes, Node onSuccess, Node onError) { + public CompositeNode(String id, List nodes, Node onSuccess, Node onError) { + this.id = id; this.nodes = nodes; this.onSuccess = onSuccess; this.onError = onError; @@ -36,7 +37,7 @@ public CompositeNode(List nodes, Node onSuccess, Node onError) { @Override public String getId() { - return COMPOSITE_NODE_ID; + return id; } @Override @@ -51,8 +52,8 @@ public Optional next(String transition) { } @Override - public boolean isComposite() { - return true; + public NodeType getType() { + return NodeType.COMPOSITE; } public List getNodes() { @@ -62,7 +63,8 @@ public List getNodes() { @Override public String toString() { return "CompositeNode{" + - "nodes=" + nodes + + "id='" + id + '\'' + + ", nodes=" + nodes + ", onSuccess=" + onSuccess + ", onError=" + onError + '}'; diff --git a/handler/engine/src/main/java/io/knotx/fragments/engine/graph/Node.java b/handler/engine/src/main/java/io/knotx/fragments/engine/graph/Node.java index 2432c4c1..1c36e94a 100644 --- a/handler/engine/src/main/java/io/knotx/fragments/engine/graph/Node.java +++ b/handler/engine/src/main/java/io/knotx/fragments/engine/graph/Node.java @@ -23,6 +23,6 @@ public interface Node { Optional next(String transition); - boolean isComposite(); + NodeType getType(); } diff --git a/handler/engine/src/main/java/io/knotx/fragments/engine/graph/NodeType.java b/handler/engine/src/main/java/io/knotx/fragments/engine/graph/NodeType.java new file mode 100644 index 00000000..6a97673c --- /dev/null +++ b/handler/engine/src/main/java/io/knotx/fragments/engine/graph/NodeType.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 Knot.x Project + * + * Licensed 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 io.knotx.fragments.engine.graph; + +public enum NodeType { + SINGLE, + COMPOSITE +} diff --git a/handler/engine/src/main/java/io/knotx/fragments/engine/graph/ActionNode.java b/handler/engine/src/main/java/io/knotx/fragments/engine/graph/SingleNode.java similarity index 88% rename from handler/engine/src/main/java/io/knotx/fragments/engine/graph/ActionNode.java rename to handler/engine/src/main/java/io/knotx/fragments/engine/graph/SingleNode.java index fffc4632..d3f69dd0 100644 --- a/handler/engine/src/main/java/io/knotx/fragments/engine/graph/ActionNode.java +++ b/handler/engine/src/main/java/io/knotx/fragments/engine/graph/SingleNode.java @@ -22,7 +22,7 @@ import java.util.Optional; import java.util.function.Function; -public class ActionNode implements Node { +public class SingleNode implements Node { private String id; @@ -30,11 +30,11 @@ public class ActionNode implements Node { private Map transitions; - public ActionNode(String id, Function> action) { + public SingleNode(String id, Function> action) { this(id, action, null); } - public ActionNode(String id, Function> action, + public SingleNode(String id, Function> action, Map transitions) { this.id = id; this.action = action; @@ -52,8 +52,8 @@ public Optional next(String transition) { } @Override - public boolean isComposite() { - return false; + public NodeType getType() { + return NodeType.SINGLE; } public Single doAction(FragmentContext fragmentContext) { diff --git a/handler/engine/src/test/java/io/knotx/fragments/engine/FragmentsEngineConcurrencyTest.java b/handler/engine/src/test/java/io/knotx/fragments/engine/FragmentsEngineConcurrencyTest.java index dfbe37dd..fdbfd5a6 100644 --- a/handler/engine/src/test/java/io/knotx/fragments/engine/FragmentsEngineConcurrencyTest.java +++ b/handler/engine/src/test/java/io/knotx/fragments/engine/FragmentsEngineConcurrencyTest.java @@ -18,7 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import io.knotx.fragments.api.Fragment; -import io.knotx.fragments.engine.graph.ActionNode; +import io.knotx.fragments.engine.graph.SingleNode; import io.knotx.fragments.handler.api.domain.FragmentContext; import io.knotx.fragments.handler.api.domain.FragmentResult; import io.knotx.server.api.context.ClientRequest; @@ -86,7 +86,7 @@ void expectParallelEvaluationStrategy(VertxTestContext testContext, Vertx vertx) } private FragmentEventContextTaskAware initFragmentEventContextTaskAware() { - ActionNode graphNode = new ActionNode("id", BLOCKING_OPERATION, + SingleNode graphNode = new SingleNode("id", BLOCKING_OPERATION, Collections.emptyMap()); Fragment fragment = new Fragment("snippet", new JsonObject(), "some body"); diff --git a/handler/engine/src/test/java/io/knotx/fragments/engine/FragmentsEngineOrderTest.java b/handler/engine/src/test/java/io/knotx/fragments/engine/FragmentsEngineOrderTest.java index 0b0c0939..a4a52b7b 100644 --- a/handler/engine/src/test/java/io/knotx/fragments/engine/FragmentsEngineOrderTest.java +++ b/handler/engine/src/test/java/io/knotx/fragments/engine/FragmentsEngineOrderTest.java @@ -19,7 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import io.knotx.fragments.api.Fragment; -import io.knotx.fragments.engine.graph.ActionNode; +import io.knotx.fragments.engine.graph.SingleNode; import io.knotx.fragments.engine.graph.Node; import io.knotx.fragments.handler.api.domain.FragmentContext; import io.knotx.fragments.handler.api.domain.FragmentResult; @@ -80,7 +80,7 @@ void expectCorrectOrder(VertxTestContext testContext, Vertx vertx) private FragmentEventContextTaskAware initFragmentEventContextTaskAware( String fragmentBody, Function> operation) { - Node graphNode = new ActionNode("id", operation, Collections.emptyMap()); + Node graphNode = new SingleNode("id", operation, Collections.emptyMap()); Fragment fragment = new Fragment("snippet", new JsonObject(), fragmentBody); return new FragmentEventContextTaskAware(new Task("task", graphNode), diff --git a/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineCompositeNodeTest.java b/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineCompositeNodeTest.java index efd1109d..0e3c8da1 100644 --- a/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineCompositeNodeTest.java +++ b/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineCompositeNodeTest.java @@ -12,14 +12,11 @@ * 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. - * - * The code comes from https://github.com/tomaszmichalak/vertx-rx-map-reduce. */ package io.knotx.fragments.engine; import static io.knotx.fragments.engine.FragmentEventLogVerifier.verifyAllLogEntries; import static io.knotx.fragments.engine.FragmentEventLogVerifier.verifyLogEntries; -import static io.knotx.fragments.engine.graph.CompositeNode.COMPOSITE_NODE_ID; import static io.knotx.fragments.engine.helpers.TestFunction.appendBody; import static io.knotx.fragments.engine.helpers.TestFunction.appendPayload; import static io.knotx.fragments.engine.helpers.TestFunction.failure; @@ -32,7 +29,7 @@ import io.knotx.fragments.api.Fragment; import io.knotx.fragments.engine.FragmentEvent.Status; import io.knotx.fragments.engine.FragmentEventLogVerifier.Operation; -import io.knotx.fragments.engine.graph.ActionNode; +import io.knotx.fragments.engine.graph.SingleNode; import io.knotx.fragments.engine.graph.CompositeNode; import io.knotx.fragments.engine.graph.Node; import io.knotx.fragments.handler.api.exception.ActionFatalException; @@ -57,6 +54,8 @@ @ExtendWith(VertxExtension.class) class TaskEngineCompositeNodeTest { + private static final String COMPOSITE_NODE_ID = "composite"; + private static final String INNER_COMPOSITE_NODE_ID = "innerComposite"; private static final String INITIAL_BODY = "initial body"; private static final Map NO_TRANSITIONS = Collections.emptyMap(); @@ -74,7 +73,7 @@ void setUp() { void expectUnprocessedWhenEmptyParallelEnds(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, Collections.emptyList(), null, null @@ -93,9 +92,9 @@ void expectUnprocessedWhenEmptyParallelEnds(VertxTestContext testContext, Vertx void expectSuccessWhenSingleProcessingEnds(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("action", success(), NO_TRANSITIONS) + new SingleNode("action", success(), NO_TRANSITIONS) ), null, null ); @@ -112,9 +111,9 @@ void expectSuccessWhenSingleProcessingEnds(VertxTestContext testContext, Vertx v void expectSuccessEventLogEntry(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("action", success(), NO_TRANSITIONS) + new SingleNode("action", success(), NO_TRANSITIONS) ), null, null ); // when @@ -134,9 +133,9 @@ void expectSuccessEventLogEntry(VertxTestContext testContext, Vertx vertx) void expectErrorWhenSingleProcessingFails(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("action", failure(), NO_TRANSITIONS) + new SingleNode("action", failure(), NO_TRANSITIONS) ), null, null ); @@ -153,12 +152,12 @@ void expectErrorWhenSingleProcessingFails(VertxTestContext testContext, Vertx ve void expectErrorEventLogEntry(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("action", failure(), NO_TRANSITIONS) + new SingleNode("action", failure(), NO_TRANSITIONS) ), null, - new ActionNode("action", success(), NO_TRANSITIONS) + new SingleNode("action", success(), NO_TRANSITIONS) ); // when @@ -176,9 +175,9 @@ void expectErrorEventLogEntry(VertxTestContext testContext, Vertx vertx) void expectUnsupportedEventLogEntryWhenError(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("action", failure(), NO_TRANSITIONS) + new SingleNode("action", failure(), NO_TRANSITIONS) ), null, null ); // when @@ -196,9 +195,9 @@ void expectUnsupportedEventLogEntryWhenError(VertxTestContext testContext, Vertx void expectExceptionWhenSingleProcessingThrowsFatal(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("action", fatal(eventContext.getFragmentEvent().getFragment()), + new SingleNode("action", fatal(eventContext.getFragmentEvent().getFragment()), NO_TRANSITIONS) ), null, null ); @@ -220,9 +219,9 @@ void expectPayloadUpdatedInParallelProcessing(VertxTestContext testContext, Vert // given JsonObject taskAPayload = new JsonObject().put("key", "taskAOperation"); - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("A", appendPayload("A", taskAPayload), NO_TRANSITIONS) + new SingleNode("A", appendPayload("A", taskAPayload), NO_TRANSITIONS) ), null, null ); @@ -240,11 +239,11 @@ void expectPayloadUpdatedInParallelProcessing(VertxTestContext testContext, Vert @DisplayName("Expect success status when parallel inside parallel ends successfully") void inception(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new CompositeNode( + new CompositeNode(INNER_COMPOSITE_NODE_ID, parallel( - new ActionNode("action", success(), NO_TRANSITIONS) + new SingleNode("action", success(), NO_TRANSITIONS) ), null, null) ), null, null); @@ -262,11 +261,11 @@ void expectPayloadUpdatedInParallelInsideParallelProcessing(VertxTestContext tes Vertx vertx) throws Throwable { // given JsonObject taskAPayload = new JsonObject().put("key", "taskAOperation"); - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new CompositeNode( + new CompositeNode(INNER_COMPOSITE_NODE_ID, parallel( - new ActionNode("action", appendPayload("A", taskAPayload), NO_TRANSITIONS) + new SingleNode("action", appendPayload("A", taskAPayload), NO_TRANSITIONS) ), null, null) ), null, null); @@ -284,10 +283,10 @@ void expectPayloadUpdatedInParallelInsideParallelProcessing(VertxTestContext tes void expectSuccessWhenParallelConsistsOfEmptyAndSuccessActions(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new CompositeNode(parallel(), null, null), - new ActionNode("action", success(), NO_TRANSITIONS) + new CompositeNode(INNER_COMPOSITE_NODE_ID, parallel(), null, null), + new SingleNode("action", success(), NO_TRANSITIONS) ), null, null); // when @@ -302,10 +301,10 @@ void expectSuccessWhenParallelConsistsOfEmptyAndSuccessActions(VertxTestContext @DisplayName("Expect error when one of parallel actions ends with error") void expectError(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("failing", failure(), NO_TRANSITIONS), - new ActionNode("success", success(), NO_TRANSITIONS) + new SingleNode("failing", failure(), NO_TRANSITIONS), + new SingleNode("success", success(), NO_TRANSITIONS) ), null, null); // when @@ -321,10 +320,10 @@ void expectError(VertxTestContext testContext, Vertx vertx) throws Throwable { void expectLogEntriesOfAllParallelActions(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("failing", failure(), NO_TRANSITIONS), - new ActionNode("success", success(), NO_TRANSITIONS) + new SingleNode("failing", failure(), NO_TRANSITIONS), + new SingleNode("success", success(), NO_TRANSITIONS) ), null, null); // when @@ -341,16 +340,39 @@ void expectLogEntriesOfAllParallelActions(VertxTestContext testContext, Vertx ve )); } + @Test + @DisplayName("Expect inner composite node log entry") + void expectNamedInnerCompositeLogEntry(VertxTestContext testContext, + Vertx vertx) throws Throwable { + // given + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, + parallel( + new CompositeNode(INNER_COMPOSITE_NODE_ID, + parallel( + new SingleNode("success", success(), NO_TRANSITIONS) + ), null, null) + ), null, null); + + // when + Single result = new TaskEngine(vertx).start("task", rootNode, eventContext); + + // then + verifyExecution(result, testContext, + event -> verifyLogEntries(event.getLogAsJson(), + Operation.exact("task", INNER_COMPOSITE_NODE_ID, "SUCCESS", 2) + )); + } + @Test @DisplayName("Expect success status when parallel processing and one of parallel actions returns error that is handled by parallel section fallback") void expectFallbackAppliedAfterParallelProcessing(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("A", success(), NO_TRANSITIONS), - new ActionNode("B", failure(), NO_TRANSITIONS) - ), null, new ActionNode("fallback", success(), NO_TRANSITIONS) + new SingleNode("A", success(), NO_TRANSITIONS), + new SingleNode("B", failure(), NO_TRANSITIONS) + ), null, new SingleNode("fallback", success(), NO_TRANSITIONS) ); // when @@ -366,11 +388,11 @@ void expectFallbackAppliedAfterParallelProcessing(VertxTestContext testContext, void expectFallbackAppliedDuringParallelProcessing(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("A", success(), NO_TRANSITIONS), - new ActionNode("B", failure(), Collections.singletonMap( - ERROR_TRANSITION, new ActionNode("fallback", success(), NO_TRANSITIONS) + new SingleNode("A", success(), NO_TRANSITIONS), + new SingleNode("B", failure(), Collections.singletonMap( + ERROR_TRANSITION, new SingleNode("fallback", success(), NO_TRANSITIONS) )) ), null, null ); @@ -391,12 +413,12 @@ ERROR_TRANSITION, new ActionNode("fallback", success(), NO_TRANSITIONS) void expectFallbackPayloadWhenParallelProcessingFails(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("A", failure(), NO_TRANSITIONS), - new ActionNode("B", failure(), Collections.singletonMap( + new SingleNode("A", failure(), NO_TRANSITIONS), + new SingleNode("B", failure(), Collections.singletonMap( ERROR_TRANSITION, - new ActionNode("fallback", appendPayload("fallback", "value"), NO_TRANSITIONS) + new SingleNode("fallback", appendPayload("fallback", "value"), NO_TRANSITIONS) )) ), null, null ); @@ -406,7 +428,8 @@ void expectFallbackPayloadWhenParallelProcessingFails(VertxTestContext testConte // then verifyExecution(result, testContext, - fragmentEvent -> assertTrue(fragmentEvent.getFragment().getPayload().containsKey("fallback"))); + fragmentEvent -> assertTrue( + fragmentEvent.getFragment().getPayload().containsKey("fallback"))); } @Test @@ -414,11 +437,11 @@ void expectFallbackPayloadWhenParallelProcessingFails(VertxTestContext testConte void expectSuccessAppliedAfterParallelProcessingSuccess(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("A", success(), NO_TRANSITIONS), - new ActionNode("B", success(), NO_TRANSITIONS) - ), new ActionNode("last", appendBody(":last"), NO_TRANSITIONS), null + new SingleNode("A", success(), NO_TRANSITIONS), + new SingleNode("B", success(), NO_TRANSITIONS) + ), new SingleNode("last", appendBody(":last"), NO_TRANSITIONS), null ); // when @@ -437,13 +460,13 @@ void expectSuccessAfterParallelProcessingAppliedAfterSuccessParallel(VertxTestCo // given JsonObject expectedPayload = new JsonObject().put("key", "value"); - Node rootNode = new CompositeNode( + Node rootNode = new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("A", success(), NO_TRANSITIONS), - new ActionNode("B", success(), NO_TRANSITIONS) - ), new CompositeNode( + new SingleNode("A", success(), NO_TRANSITIONS), + new SingleNode("B", success(), NO_TRANSITIONS) + ), new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("last", appendPayload("last", expectedPayload), NO_TRANSITIONS) + new SingleNode("last", appendPayload("last", expectedPayload), NO_TRANSITIONS) ), null, null), null ); @@ -461,6 +484,7 @@ private List parallel(Node... nodes) { return Arrays.asList(nodes); } + @SuppressWarnings("ResultOfMethodCallIgnored") private void verifyExecution(Single result, VertxTestContext testContext, Consumer successConsumer) throws Throwable { // execute @@ -477,6 +501,7 @@ private void verifyExecution(Single result, VertxTestContext test } } + @SuppressWarnings("ResultOfMethodCallIgnored") private void verifyError(Single result, VertxTestContext testContext, Consumer errorConsumer) throws Throwable { // execute diff --git a/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineScenariosTest.java b/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineScenariosTest.java index 3b164712..a4569c74 100644 --- a/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineScenariosTest.java +++ b/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineScenariosTest.java @@ -12,13 +12,10 @@ * 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. - * - * The code comes from https://github.com/tomaszmichalak/vertx-rx-map-reduce. */ package io.knotx.fragments.engine; import static io.knotx.fragments.engine.FragmentEventLogVerifier.verifyAllLogEntries; -import static io.knotx.fragments.engine.graph.CompositeNode.COMPOSITE_NODE_ID; import static io.knotx.fragments.engine.helpers.TestFunction.appendBody; import static io.knotx.fragments.engine.helpers.TestFunction.appendBodyWithPayload; import static io.knotx.fragments.engine.helpers.TestFunction.appendPayload; @@ -34,7 +31,7 @@ import io.knotx.fragments.api.Fragment; import io.knotx.fragments.engine.FragmentEvent.Status; import io.knotx.fragments.engine.FragmentEventLogVerifier.Operation; -import io.knotx.fragments.engine.graph.ActionNode; +import io.knotx.fragments.engine.graph.SingleNode; import io.knotx.fragments.engine.graph.CompositeNode; import io.knotx.fragments.engine.graph.Node; import io.knotx.server.api.context.ClientRequest; @@ -58,6 +55,7 @@ @ExtendWith(VertxExtension.class) class TaskEngineScenariosTest { + private static final String COMPOSITE_NODE_ID = "composite"; private static final String INITIAL_BODY = "initial body"; private static final Map NO_TRANSITIONS = Collections.emptyMap(); @@ -81,14 +79,14 @@ void expectSuccessParallelProcessing(VertxTestContext testContext, Vertx vertx) JsonObject taskBPayload = new JsonObject().put("key", "taskBOperation"); JsonObject taskCPayload = new JsonObject().put("key", "taskCOperation"); - Node rootNode = new ActionNode("first", appendBody(":first"), - successTransition(new CompositeNode( + Node rootNode = new SingleNode("first", appendBody(":first"), + successTransition(new CompositeNode("composite", parallel( - new ActionNode("A", appendPayload("A", taskAPayload), NO_TRANSITIONS), - new ActionNode("B", appendPayload("B", taskBPayload), NO_TRANSITIONS), - new ActionNode("C", appendPayload("C", taskCPayload), NO_TRANSITIONS) + new SingleNode("A", appendPayload("A", taskAPayload), NO_TRANSITIONS), + new SingleNode("B", appendPayload("B", taskBPayload), NO_TRANSITIONS), + new SingleNode("C", appendPayload("C", taskCPayload), NO_TRANSITIONS) ), - new ActionNode("last", appendBody(":last"), NO_TRANSITIONS), + new SingleNode("last", appendBody(":last"), NO_TRANSITIONS), null ) )); @@ -119,21 +117,21 @@ void expectSuccessParallelProcessing(VertxTestContext testContext, Vertx vertx) @DisplayName("Expect body updated after complex processing") void expectSuccessMultipleParallel(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new ActionNode("first", success(), - successTransition(new CompositeNode( + Node rootNode = new SingleNode("first", success(), + successTransition(new CompositeNode("composite", parallel( - new ActionNode("A", appendPayload("A", ":payloadA"), NO_TRANSITIONS), - new ActionNode("B", appendPayload("B", ":payloadB"), NO_TRANSITIONS) + new SingleNode("A", appendPayload("A", ":payloadA"), NO_TRANSITIONS), + new SingleNode("B", appendPayload("B", ":payloadB"), NO_TRANSITIONS) ), - new ActionNode("middle", success(), - successTransition(new CompositeNode( + new SingleNode("middle", success(), + successTransition(new CompositeNode("composite", parallel( - new ActionNode("X", + new SingleNode("X", appendPayloadBasingOnContext("A", "X", "withX"), NO_TRANSITIONS), - new ActionNode("Y", + new SingleNode("Y", appendPayloadBasingOnContext("B", "Y", "withY"), NO_TRANSITIONS) ), - new ActionNode("last", appendBodyWithPayload("X", "Y"), NO_TRANSITIONS), + new SingleNode("last", appendBodyWithPayload("X", "Y"), NO_TRANSITIONS), null ))), null @@ -159,26 +157,26 @@ void expectSuccessMultipleParallel(VertxTestContext testContext, Vertx vertx) th @DisplayName("Expect logs in order after complex processing") void expectProcessingLogsInOrder(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new ActionNode("first", success(), - successTransition(new CompositeNode( + Node rootNode = new SingleNode("first", success(), + successTransition(new CompositeNode("composite", parallel( - new ActionNode("A1", success(), successTransition( - new ActionNode("A2", failure(), errorTransition( - new ActionNode("A3-fallback", success(), NO_TRANSITIONS) + new SingleNode("A1", success(), successTransition( + new SingleNode("A2", failure(), errorTransition( + new SingleNode("A3-fallback", success(), NO_TRANSITIONS) )) )), - new ActionNode("B", success(), NO_TRANSITIONS) + new SingleNode("B", success(), NO_TRANSITIONS) ), - new ActionNode("middle", success(), - successTransition(new CompositeNode( + new SingleNode("middle", success(), + successTransition(new CompositeNode(COMPOSITE_NODE_ID, parallel( - new ActionNode("X", success(), NO_TRANSITIONS), - new ActionNode("Y1", success(), successTransition( - new ActionNode("Y2", success(), NO_TRANSITIONS) + new SingleNode("X", success(), NO_TRANSITIONS), + new SingleNode("Y1", success(), successTransition( + new SingleNode("Y2", success(), NO_TRANSITIONS) )) ), - new ActionNode("last", success(), NO_TRANSITIONS), + new SingleNode("last", success(), NO_TRANSITIONS), null ))), null @@ -197,12 +195,12 @@ void expectProcessingLogsInOrder(VertxTestContext testContext, Vertx vertx) thro Operation.range("task", "A2", "ERROR", 1, 4), Operation.range("task", "A3-fallback", "SUCCESS", 1, 4), Operation.range("task", "B", "SUCCESS", 1, 4), - Operation.exact("task", COMPOSITE_NODE_ID, "SUCCESS", 5), + Operation.exact("task", "composite", "SUCCESS", 5), Operation.exact("task", "middle", "SUCCESS", 6), Operation.range("task", "X", "SUCCESS", 7, 9), Operation.range("task", "Y1", "SUCCESS", 7, 9), Operation.range("task", "Y2", "SUCCESS", 7, 9), - Operation.exact("task", COMPOSITE_NODE_ID, "SUCCESS", 10), + Operation.exact("task", "composite", "SUCCESS", 10), Operation.exact("task", "last", "SUCCESS", 11) ); }); @@ -227,14 +225,14 @@ void ensureBodyImmutableDuringParallelProcessing(VertxTestContext testContext, V @DisplayName("Expect parallel nodes processed in parallel when delays") void verifyParallelExecution(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new ActionNode("first", success(), - successTransition(new CompositeNode( + Node rootNode = new SingleNode("first", success(), + successTransition(new CompositeNode("composite", parallel( - new ActionNode("A", successWithDelay(500), NO_TRANSITIONS), - new ActionNode("B", successWithDelay(500), NO_TRANSITIONS), - new ActionNode("C", successWithDelay(500), NO_TRANSITIONS) + new SingleNode("A", successWithDelay(500), NO_TRANSITIONS), + new SingleNode("B", successWithDelay(500), NO_TRANSITIONS), + new SingleNode("C", successWithDelay(500), NO_TRANSITIONS) ), - new ActionNode("last", success(), NO_TRANSITIONS), + new SingleNode("last", success(), NO_TRANSITIONS), null ) )); @@ -256,23 +254,23 @@ void verifyParallelExecution(VertxTestContext testContext, Vertx vertx) throws T @DisplayName("Expect nested parallel nodes processed in parallel when delays") void verifyNestedParallelExecution(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new ActionNode("first", success(), - successTransition(new CompositeNode( + Node rootNode = new SingleNode("first", success(), + successTransition(new CompositeNode("composite", parallel( - new ActionNode("A", successWithDelay(500), NO_TRANSITIONS), - new CompositeNode( + new SingleNode("A", successWithDelay(500), NO_TRANSITIONS), + new CompositeNode("composite", parallel( - new ActionNode("B", successWithDelay(500), - successTransition(new ActionNode("B1", appendPayload("B1", "B1Payload"), + new SingleNode("B", successWithDelay(500), + successTransition(new SingleNode("B1", appendPayload("B1", "B1Payload"), NO_TRANSITIONS))), - new ActionNode("C", successWithDelay(500), NO_TRANSITIONS) + new SingleNode("C", successWithDelay(500), NO_TRANSITIONS) ), null, null ), - new ActionNode("D", successWithDelay(500), NO_TRANSITIONS) + new SingleNode("D", successWithDelay(500), NO_TRANSITIONS) ), - new ActionNode("last", success(), NO_TRANSITIONS), + new SingleNode("last", success(), NO_TRANSITIONS), null ) )); diff --git a/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineSingleOperationTest.java b/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineSingleOperationTest.java index f843edb6..e328011b 100644 --- a/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineSingleOperationTest.java +++ b/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineSingleOperationTest.java @@ -12,8 +12,6 @@ * 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. - * - * The code comes from https://github.com/tomaszmichalak/vertx-rx-map-reduce. */ package io.knotx.fragments.engine; @@ -28,8 +26,8 @@ import io.knotx.fragments.api.Fragment; import io.knotx.fragments.engine.FragmentEvent.Status; import io.knotx.fragments.engine.FragmentEventLogVerifier.Operation; -import io.knotx.fragments.engine.graph.ActionNode; import io.knotx.fragments.engine.graph.Node; +import io.knotx.fragments.engine.graph.SingleNode; import io.knotx.fragments.handler.api.domain.FragmentContext; import io.knotx.fragments.handler.api.domain.FragmentResult; import io.knotx.server.api.context.ClientRequest; @@ -71,7 +69,7 @@ void setUp() { void expectEvaluatedFragment(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - Node rootNode = new ActionNode("first", appendBody(":updated"), NO_TRANSITIONS); + Node rootNode = new SingleNode("first", appendBody(":updated"), NO_TRANSITIONS); // when Single result = new TaskEngine(vertx).start("task", rootNode, eventContext); @@ -86,7 +84,7 @@ void expectEvaluatedFragment(VertxTestContext testContext, Vertx vertx) void expectInitialFragment(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - ActionNode rootNode = new ActionNode("first", failure(), NO_TRANSITIONS); + SingleNode rootNode = new SingleNode("first", failure(), NO_TRANSITIONS); // when Single result = new TaskEngine(vertx).start("task", rootNode, eventContext); @@ -101,9 +99,9 @@ void expectInitialFragment(VertxTestContext testContext, Vertx vertx) void expectrootNodeOperations(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - ActionNode rootNode = new ActionNode("first", appendBody(":A"), + SingleNode rootNode = new SingleNode("first", appendBody(":A"), Collections.singletonMap(SUCCESS_TRANSITION, - new ActionNode("second", appendBody(":B")))); + new SingleNode("second", appendBody(":B")))); // when Single result = new TaskEngine(vertx).start("task", rootNode, eventContext); @@ -121,7 +119,7 @@ void expectrootNodeOperations(VertxTestContext testContext, Vertx vertx) void expectSuccessEventWhenOperationEnds(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - ActionNode rootNode = new ActionNode("first", success(), + SingleNode rootNode = new SingleNode("first", success(), NO_TRANSITIONS); // when @@ -136,9 +134,9 @@ void expectSuccessEventWhenOperationEnds(VertxTestContext testContext, Vertx ver void expectSuccessEventWhenAllOperationsEnds(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - ActionNode rootNode = new ActionNode("first", success(), + SingleNode rootNode = new SingleNode("first", success(), Collections.singletonMap(SUCCESS_TRANSITION, - new ActionNode("second", success()))); + new SingleNode("second", success()))); // when Single result = new TaskEngine(vertx).start("task", rootNode, eventContext); @@ -152,7 +150,7 @@ void expectSuccessEventWhenAllOperationsEnds(VertxTestContext testContext, Vertx void expectFailureEventWhenUnhandledException(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - ActionNode rootNode = new ActionNode("first", failure(), + SingleNode rootNode = new SingleNode("first", failure(), NO_TRANSITIONS); // when @@ -167,9 +165,9 @@ void expectFailureEventWhenUnhandledException(VertxTestContext testContext, Vert void expectSuccessEventWhenExceptionHandled(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - ActionNode rootNode = new ActionNode("first", failure(), + SingleNode rootNode = new SingleNode("first", failure(), Collections.singletonMap(ERROR_TRANSITION, - new ActionNode("second", success()))); + new SingleNode("second", success()))); // when Single result = new TaskEngine(vertx).start("task", rootNode, eventContext); @@ -185,7 +183,7 @@ void executeEventWithInvalidAddressInKnotFlow(VertxTestContext testContext, Vert // given Function> operation = context -> Single .just(new FragmentResult(context.getFragment(), "customTransition")); - ActionNode rootNode = new ActionNode("knotx.knot.successKnot", + SingleNode rootNode = new SingleNode("knotx.knot.successKnot", operation, NO_TRANSITIONS); @@ -201,7 +199,7 @@ void executeEventWithInvalidAddressInKnotFlow(VertxTestContext testContext, Vert void expectSuccessEventLogEntry(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - ActionNode rootNode = new ActionNode("first", success(), + SingleNode rootNode = new SingleNode("first", success(), NO_TRANSITIONS); // when @@ -219,7 +217,7 @@ void expectSuccessEventLogEntry(VertxTestContext testContext, Vertx vertx) void expectUnsupportedEventLogEntryWhenError(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - ActionNode rootNode = new ActionNode("first", failure(), + SingleNode rootNode = new SingleNode("first", failure(), NO_TRANSITIONS); // when @@ -240,7 +238,7 @@ void expectUnsupportedEventLogEntryWhenCustomTransition(VertxTestContext testCon // given Function> operation = context -> Single .just(new FragmentResult(context.getFragment(), "customTransition")); - ActionNode rootNode = new ActionNode("first", operation, + SingleNode rootNode = new SingleNode("first", operation, NO_TRANSITIONS); // when @@ -259,9 +257,9 @@ void expectUnsupportedEventLogEntryWhenCustomTransition(VertxTestContext testCon void expectErrorAndSuccessEventLogEntries(VertxTestContext testContext, Vertx vertx) throws Throwable { // given - ActionNode rootNode = new ActionNode("first", failure(), + SingleNode rootNode = new SingleNode("first", failure(), Collections.singletonMap(ERROR_TRANSITION, - new ActionNode("second", success(), NO_TRANSITIONS))); + new SingleNode("second", success(), NO_TRANSITIONS))); // when Single result = new TaskEngine(vertx).start("task", rootNode, eventContext); From 6949abe3ed20525ff498810520dd2091fdeeaa3d Mon Sep 17 00:00:00 2001 From: Tomasz Michalak Date: Tue, 5 Nov 2019 11:36:23 +0100 Subject: [PATCH 2/5] Readme update --- handler/engine/README.md | 78 +++++++++++------- handler/engine/assets/images/a_custom.png | Bin 11752 -> 0 bytes .../images/a_custom_no_configuration.png | Bin 0 -> 16161 bytes handler/engine/assets/images/a_error_c.png | Bin 10034 -> 0 bytes .../engine/assets/images/a_error_c_error.png | Bin 9948 -> 14199 bytes .../assets/images/a_error_c_success.png | Bin 0 -> 14019 bytes handler/engine/assets/images/a_next_b.png | Bin 9949 -> 0 bytes .../engine/assets/images/a_next_b_error.png | Bin 9981 -> 0 bytes .../assets/images/a_success_b_error.png | Bin 0 -> 14017 bytes .../assets/images/a_success_b_success.png | Bin 0 -> 13796 bytes handler/engine/assets/images/graph_node.png | Bin 9141 -> 13082 bytes 11 files changed, 47 insertions(+), 31 deletions(-) delete mode 100644 handler/engine/assets/images/a_custom.png create mode 100644 handler/engine/assets/images/a_custom_no_configuration.png delete mode 100644 handler/engine/assets/images/a_error_c.png create mode 100644 handler/engine/assets/images/a_error_c_success.png delete mode 100644 handler/engine/assets/images/a_next_b.png delete mode 100644 handler/engine/assets/images/a_next_b_error.png create mode 100644 handler/engine/assets/images/a_success_b_error.png create mode 100644 handler/engine/assets/images/a_success_b_success.png diff --git a/handler/engine/README.md b/handler/engine/README.md index d3f47823..dc309ef6 100644 --- a/handler/engine/README.md +++ b/handler/engine/README.md @@ -17,23 +17,30 @@ The node definition is abstract. It allows to define simple processing nodes but structures such as a list of subgraphs. There are two **node** types: - - **simple nodes** that are simple operations that do some fragment modifications (called [Single Node](#single-node)), - - **parallel complex nodes** that represent a list of subgraphs (called [Composite Node](#composite-node)). + - **single nodes** that are simple operations that do some fragments modifications (called [Single Node](#single-node)), + - **parallel complex nodes** that are lists of subgraphs (called [Composite Node](#composite-node)). ### Single Node -The node represents a single operation that transforms one Fragment into another. +A node represents a single operation that transforms one Fragment into another. The operation can +produce multiple **custom** transitions that indicate various business decisions. -This operation can respond with **custom** transitions. +The example of this node is calling an authentication RESTful API. The node implements a communication +logic and reacts to different API responses such as HTTP 200/401/404 status codes. +Each status code may represent various decisions such as a successful authentication, a user not +found or even expired password. Those responses can be easily converted into custom transitions. ### Composite Node -This node may consist of other Composite Nodes or Single Nodes or a mix of both. -It enables parallel processing of independent nodes (e.g. calling two external data sources). -Composite Node may define only two transitions: +A node defines a list of subgrahs to evaluate. It may consist of other Composite Nodes or Single Nodes +or a mix of both. It enables parallel processing of independent nodes/subgraphs (e.g. calling two +external independent data sources). + +Composite Node may respond with only two default transitions: - `_success` - the default one, means that operation ends without any exception - `_error` - when operation throws an exception > Important note! -> Single Nodes inside the Composite Node may only modify the Fragment's payload and should not modify the Fragment's body. +> Single Nodes inside the Composite Node may only modify the Fragment's payload and should not modify +>the Fragment's body. ### Transition A directed graph consists of nodes and edges. Edges are called transitions. Transition is a simple text. @@ -51,45 +58,54 @@ There are two important rules to remember: > If a node responds with a not configured transition, the "Unsupported Transition" error occurs. -#### States +### Fragment's status +During fragment's processing, a fragment's status is calculated. Each node responds with a transition. +Fragments Engine validates node responses and set one of the fragment's statuses: +- `unprocessed` +- `success` +- `failure` + +The engine accepts a list of fragments to process and responds with a list of processed fragments +containing fragment's data, the processing status and log. The decision what should happen when some +fragment's statuses are `failure` is not taken in the engine. + +Let's see the example below to understand when the fragment's status is `success` or `failure`. ![Node with exits](assets/images/graph_node.png) -If a node does not declare a `_success` transition, processing is finished and Task Engine responds with -`SUCCESS` status. +> The *A* node declares two transitions: `_success` and `_error`. If the processing of the *A* node +>finishes correctly, it responds with the `_success` transition and then the *B* node will continue +>processing. + +> If the `B` node completes successfully, it ends fragment processing with the `SUCCESS` status. +> Otherwise, it returns the `_error` transition and the fragment's status is `FAILURE`. -Let's see the example above. *Node A* declares two transitions: `_success` and `_error`. -If the transformation logic defined in *Node A* ends correctly, then the `_success` transition -is set by default (unless *Node A* has set a custom transition) and *Node B* will continue processing. -If *Node B* ends correctly then Task Engine responds with the `SUCCESS` status. Otherwise, the `_error` -transition is set, *Node B* does not declare it so the `FAILURE` state is returned. -If the transformation logic from *Node A* raises an exception, the `_error` transition is set and -*Node C* continues processing. *Node C* may end correctly, and then the entire processing is marked -with the `SUCCESS` state. +> If the processing of the `A` node throws an exception, then the `_error` transition is set, and +>the `C` node continues processing. The images below illustrates the above rules. -`SUCCESS` states: +`SUCCESS` statuses: -* *Node A* and *Node B* ends correctly: +* `A` and `B` ends correctly -![Node with exits](assets/images/a_next_b.png) +![A and B ends correctly](assets/images/a_success_b_success.png) -* *Node A* raises an exception, *Node B* ends correctly +* `A` raises an exception (or responds with `_error`), then `B` ends correctly -![Node with exits](assets/images/a_error_c.png) +![A ends with error, C ends correctly](assets/images/a_error_c_success.png) -`FAILURE` states: +`FAILURE` status: -* *Node A* ends correctly and *Node B* raises exception: +* `A` ends correctly, however `B` raises an exception (or responds with `_error`) -![Node with exits](assets/images/a_next_b_error.png) +![Node with exits](assets/images/a_success_b_error.png) -* *Node A* and *Node C* raises exceptions: +* `A` and `C` raise exceptions (or respond with `_error`) ![Node with exits](assets/images/a_error_c_error.png) -A node can also declare its own exits (transitions) but then we need to configure them in a graph. -Otherwise, if the custom transition is set but is not declared, then the `FAILURE` status is returned: +* `A` node can also respond with its custom transitions. Then we have to configure them in a graph. +Otherwise, if the `custom` transition is set but is not declared, then the `FAILURE` status is returned -![Node with exits](assets/images/a_custom.png) \ No newline at end of file +![Node with exits](assets/images/a_custom_no_configuration.png) \ No newline at end of file diff --git a/handler/engine/assets/images/a_custom.png b/handler/engine/assets/images/a_custom.png deleted file mode 100644 index e59f01d6e55f6121adb01af6002dab7d76026e82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11752 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 Y>A?nf5@90zukQ#-0Cl-?nUA0T4{RqwiU0rr literal 0 HcmV?d00001 diff --git a/handler/engine/assets/images/a_error_c.png b/handler/engine/assets/images/a_error_c.png deleted file mode 100644 index 996df2a70337cfd27e10f3d50298690e1d9012c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10034 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 diff --git a/handler/engine/assets/images/a_error_c_error.png b/handler/engine/assets/images/a_error_c_error.png index 3e22dc9a0ff4c59083b7c78ef976f9350d45ec1b..14c48887580ac7412806a7b3a50a8ef4b8b8031b 100644 GIT binary patch literal 14199 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 literal 9948 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 diff --git a/handler/engine/assets/images/a_error_c_success.png b/handler/engine/assets/images/a_error_c_success.png new file mode 100644 index 0000000000000000000000000000000000000000..35aa40b5c1c9a879077d63a595ed001be372dfe2 GIT binary patch literal 14019 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 z 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 z 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 P!%~*lkSmoj|L}hRAR=ne literal 0 HcmV?d00001 diff --git a/handler/engine/assets/images/a_success_b_success.png b/handler/engine/assets/images/a_success_b_success.png new file mode 100644 index 0000000000000000000000000000000000000000..db4df32913990381c1e89d3a2612528583fde621 GIT binary patch literal 13796 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 LEMEyUfBSy`M3@ul literal 0 HcmV?d00001 diff --git a/handler/engine/assets/images/graph_node.png b/handler/engine/assets/images/graph_node.png index 63bb399c1656175c094e532104fb64b0ed738402..6b9912c6cd516fa4ef27ae1887dd7af1efe63907 100644 GIT binary patch literal 13082 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 literal 9141 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 From 0c14e3dc1526d2ef030b1607a941c771558e12f6 Mon Sep 17 00:00:00 2001 From: Tomasz Michalak Date: Tue, 5 Nov 2019 11:44:41 +0100 Subject: [PATCH 3/5] Apply suggestions from code review Co-Authored-By: Mateusz Hinc --- .../task/ConfigurationTaskProviderTest.java | 10 +++++----- handler/engine/README.md | 10 +++++----- .../engine/TaskEngineScenariosTest.java | 20 +++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/handler/core/src/test/java/io/knotx/fragments/task/ConfigurationTaskProviderTest.java b/handler/core/src/test/java/io/knotx/fragments/task/ConfigurationTaskProviderTest.java index a403c3ab..acafd19b 100644 --- a/handler/core/src/test/java/io/knotx/fragments/task/ConfigurationTaskProviderTest.java +++ b/handler/core/src/test/java/io/knotx/fragments/task/ConfigurationTaskProviderTest.java @@ -176,7 +176,7 @@ void expectSingleCompositeNodeGraphWithNoEdges() { assertTrue(task.getRootNode().isPresent()); Node rootNode = task.getRootNode().get(); assertTrue(rootNode instanceof CompositeNode); - assertEquals("composite", rootNode.getId()); + assertEquals(COMPOSITE_NODE_ID, rootNode.getId()); assertFalse(rootNode.next(SUCCESS_TRANSITION).isPresent()); assertFalse(rootNode.next(ERROR_TRANSITION).isPresent()); @@ -209,7 +209,7 @@ void expectCompositeNodeWithSingleNodeOnSuccessGraph() { assertTrue(task.getRootNode().isPresent()); Node rootNode = task.getRootNode().get(); assertTrue(rootNode instanceof CompositeNode); - assertEquals("composite", rootNode.getId()); + assertEquals(COMPOSITE_NODE_ID, rootNode.getId()); Optional onSuccess = rootNode.next(SUCCESS_TRANSITION); assertTrue(onSuccess.isPresent()); Node onSuccessNode = onSuccess.get(); @@ -238,7 +238,7 @@ void expectCompositeNodeWithSingleNodeOnErrorGraph() { assertTrue(task.getRootNode().isPresent()); Node rootNode = task.getRootNode().get(); assertTrue(rootNode instanceof CompositeNode); - assertEquals("composite", rootNode.getId()); + assertEquals(COMPOSITE_NODE_ID, rootNode.getId()); Optional onError = rootNode.next(ERROR_TRANSITION); assertTrue(onError.isPresent()); Node onErrorNode = onError.get(); @@ -291,7 +291,7 @@ void expectNestedCompositeNodesGraph() { assertTrue(task.getRootNode().isPresent()); Node rootNode = task.getRootNode().get(); assertTrue(rootNode instanceof CompositeNode); - assertEquals("composite", rootNode.getId()); + assertEquals(COMPOSITE_NODE_ID, rootNode.getId()); CompositeNode compositeRootNode = (CompositeNode) rootNode; assertEquals(1, compositeRootNode.getNodes().size()); @@ -314,4 +314,4 @@ private Task getTask(GraphNodeOptions graph) { private List subTasks(GraphNodeOptions... nodes) { return Arrays.asList(nodes); } -} \ No newline at end of file +} diff --git a/handler/engine/README.md b/handler/engine/README.md index dc309ef6..cab0d7d4 100644 --- a/handler/engine/README.md +++ b/handler/engine/README.md @@ -43,17 +43,17 @@ Composite Node may respond with only two default transitions: >the Fragment's body. ### Transition -A directed graph consists of nodes and edges. Edges are called transitions. Transition is a simple text. +A directed graph consists of nodes and edges. Edges are called transitions. Transition is identified by a string. The pre-defined transitions are: - `_success` - the default one, indicates that operation completes successfully (no exception) - `_error` - means that operation has throw an exception There are two important rules to remember: -> If a node responds with *_success* transition, but the transition is not configured, then ->processing is finished. +> If a node responds with *_success* transition, but the `_success` transition is not configured, then +>processing of the graph/subgraph is finished. -> If a node responds with *_error* transition, but the transition is not configured, then an +> If a node responds with *_error* transition, but the `_error` transition is not configured, then an >exception is returned. > If a node responds with a not configured transition, the "Unsupported Transition" error occurs. @@ -108,4 +108,4 @@ The images below illustrates the above rules. * `A` node can also respond with its custom transitions. Then we have to configure them in a graph. Otherwise, if the `custom` transition is set but is not declared, then the `FAILURE` status is returned -![Node with exits](assets/images/a_custom_no_configuration.png) \ No newline at end of file +![Node with exits](assets/images/a_custom_no_configuration.png) diff --git a/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineScenariosTest.java b/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineScenariosTest.java index a4569c74..7193f61e 100644 --- a/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineScenariosTest.java +++ b/handler/engine/src/test/java/io/knotx/fragments/engine/TaskEngineScenariosTest.java @@ -80,7 +80,7 @@ void expectSuccessParallelProcessing(VertxTestContext testContext, Vertx vertx) JsonObject taskCPayload = new JsonObject().put("key", "taskCOperation"); Node rootNode = new SingleNode("first", appendBody(":first"), - successTransition(new CompositeNode("composite", + successTransition(new CompositeNode(COMPOSITE_NODE_ID, parallel( new SingleNode("A", appendPayload("A", taskAPayload), NO_TRANSITIONS), new SingleNode("B", appendPayload("B", taskBPayload), NO_TRANSITIONS), @@ -118,13 +118,13 @@ void expectSuccessParallelProcessing(VertxTestContext testContext, Vertx vertx) void expectSuccessMultipleParallel(VertxTestContext testContext, Vertx vertx) throws Throwable { // given Node rootNode = new SingleNode("first", success(), - successTransition(new CompositeNode("composite", + successTransition(new CompositeNode(COMPOSITE_NODE_ID, parallel( new SingleNode("A", appendPayload("A", ":payloadA"), NO_TRANSITIONS), new SingleNode("B", appendPayload("B", ":payloadB"), NO_TRANSITIONS) ), new SingleNode("middle", success(), - successTransition(new CompositeNode("composite", + successTransition(new CompositeNode(COMPOSITE_NODE_ID, parallel( new SingleNode("X", appendPayloadBasingOnContext("A", "X", "withX"), NO_TRANSITIONS), @@ -158,7 +158,7 @@ void expectSuccessMultipleParallel(VertxTestContext testContext, Vertx vertx) th void expectProcessingLogsInOrder(VertxTestContext testContext, Vertx vertx) throws Throwable { // given Node rootNode = new SingleNode("first", success(), - successTransition(new CompositeNode("composite", + successTransition(new CompositeNode(COMPOSITE_NODE_ID, parallel( new SingleNode("A1", success(), successTransition( new SingleNode("A2", failure(), errorTransition( @@ -195,12 +195,12 @@ void expectProcessingLogsInOrder(VertxTestContext testContext, Vertx vertx) thro Operation.range("task", "A2", "ERROR", 1, 4), Operation.range("task", "A3-fallback", "SUCCESS", 1, 4), Operation.range("task", "B", "SUCCESS", 1, 4), - Operation.exact("task", "composite", "SUCCESS", 5), + Operation.exact("task", COMPOSITE_NODE_ID, "SUCCESS", 5), Operation.exact("task", "middle", "SUCCESS", 6), Operation.range("task", "X", "SUCCESS", 7, 9), Operation.range("task", "Y1", "SUCCESS", 7, 9), Operation.range("task", "Y2", "SUCCESS", 7, 9), - Operation.exact("task", "composite", "SUCCESS", 10), + Operation.exact("task", COMPOSITE_NODE_ID, "SUCCESS", 10), Operation.exact("task", "last", "SUCCESS", 11) ); }); @@ -226,7 +226,7 @@ void ensureBodyImmutableDuringParallelProcessing(VertxTestContext testContext, V void verifyParallelExecution(VertxTestContext testContext, Vertx vertx) throws Throwable { // given Node rootNode = new SingleNode("first", success(), - successTransition(new CompositeNode("composite", + successTransition(new CompositeNode(COMPOSITE_NODE_ID, parallel( new SingleNode("A", successWithDelay(500), NO_TRANSITIONS), new SingleNode("B", successWithDelay(500), NO_TRANSITIONS), @@ -255,10 +255,10 @@ void verifyParallelExecution(VertxTestContext testContext, Vertx vertx) throws T void verifyNestedParallelExecution(VertxTestContext testContext, Vertx vertx) throws Throwable { // given Node rootNode = new SingleNode("first", success(), - successTransition(new CompositeNode("composite", + successTransition(new CompositeNode(COMPOSITE_NODE_ID, parallel( new SingleNode("A", successWithDelay(500), NO_TRANSITIONS), - new CompositeNode("composite", + new CompositeNode(COMPOSITE_NODE_ID, parallel( new SingleNode("B", successWithDelay(500), successTransition(new SingleNode("B1", appendPayload("B1", "B1Payload"), @@ -319,4 +319,4 @@ private void verifyExecution(Single result, VertxTestContext test } } -} \ No newline at end of file +} From ddfb0b8ffaa1240d9f7ffe09fdac7d00d40e2314 Mon Sep 17 00:00:00 2001 From: Tomasz Michalak Date: Tue, 5 Nov 2019 11:47:10 +0100 Subject: [PATCH 4/5] CR fixes --- .../io/knotx/fragments/task/ConfigurationTaskProvider.java | 7 ++++++- .../main/java/io/knotx/fragments/engine/TaskEngine.java | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/handler/core/src/main/java/io/knotx/fragments/task/ConfigurationTaskProvider.java b/handler/core/src/main/java/io/knotx/fragments/task/ConfigurationTaskProvider.java index 3071a29e..850b1812 100644 --- a/handler/core/src/main/java/io/knotx/fragments/task/ConfigurationTaskProvider.java +++ b/handler/core/src/main/java/io/knotx/fragments/task/ConfigurationTaskProvider.java @@ -82,7 +82,12 @@ private Node buildCompositeNode(GraphNodeOptions options, Map edge List nodes = config.getSubtasks().stream() .map(this::initGraphRootNode) .collect(Collectors.toList()); - return new CompositeNode("composite", nodes, edges.get(SUCCESS_TRANSITION), edges.get(ERROR_TRANSITION)); + return new CompositeNode(getNodeId(), nodes, edges.get(SUCCESS_TRANSITION), edges.get(ERROR_TRANSITION)); + } + + private String getNodeId() { + // TODO this value should be calculated based on graph, the behaviour now is not changed + return "composite"; } private Function> toRxFunction( diff --git a/handler/engine/src/main/java/io/knotx/fragments/engine/TaskEngine.java b/handler/engine/src/main/java/io/knotx/fragments/engine/TaskEngine.java index 59729d50..81945154 100644 --- a/handler/engine/src/main/java/io/knotx/fragments/engine/TaskEngine.java +++ b/handler/engine/src/main/java/io/knotx/fragments/engine/TaskEngine.java @@ -60,7 +60,7 @@ private Single processTask(TaskExecutionContext context, N } private Single getResult(TaskExecutionContext context) { - return NodeType.COMPOSITE.equals(context.getCurrentNode().getType()) + return NodeType.COMPOSITE == context.getCurrentNode().getType() ? mapReduce(context) : execute(context); } From 87838f9ad5d922f7ca053112444298eea440085d Mon Sep 17 00:00:00 2001 From: Tomasz Michalak Date: Tue, 5 Nov 2019 12:41:57 +0100 Subject: [PATCH 5/5] Some Azure test commit. --- handler/engine/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler/engine/README.md b/handler/engine/README.md index cab0d7d4..0a778bea 100644 --- a/handler/engine/README.md +++ b/handler/engine/README.md @@ -108,4 +108,4 @@ The images below illustrates the above rules. * `A` node can also respond with its custom transitions. Then we have to configure them in a graph. Otherwise, if the `custom` transition is set but is not declared, then the `FAILURE` status is returned -![Node with exits](assets/images/a_custom_no_configuration.png) +![Node with exits](assets/images/a_custom_no_configuration.png) \ No newline at end of file