|
@@ -1648,6 +1648,16 @@ const configSchema = {
|
|
|
type: 'string',
|
|
type: 'string',
|
|
|
title: 'Key Field',
|
|
title: 'Key Field',
|
|
|
description: 'Field to match on, for combine-by-key'
|
|
description: 'Field to match on, for combine-by-key'
|
|
|
|
|
+ },
|
|
|
|
|
+ path1: {
|
|
|
|
|
+ type: 'string',
|
|
|
|
|
+ title: 'Path In Input 1',
|
|
|
|
|
+ description: 'Dotted path to the array inside input 1. Leave empty to use the input as it arrives'
|
|
|
|
|
+ },
|
|
|
|
|
+ path2: {
|
|
|
|
|
+ type: 'string',
|
|
|
|
|
+ title: 'Path In Input 2',
|
|
|
|
|
+ description: 'Dotted path to the array inside input 2. Leave empty to use the input as it arrives'
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
@@ -1673,10 +1683,17 @@ function asArray(value) {
|
|
|
return Array.isArray(value) ? value : [value];
|
|
return Array.isArray(value) ? value : [value];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function selectInput(value, path) {
|
|
|
|
|
+ if (!path) {
|
|
|
|
|
+ return value;
|
|
|
|
|
+ }
|
|
|
|
|
+ return smartbotic.utils.getFieldValue(value, path);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
async function execute(config, input, context) {
|
|
async function execute(config, input, context) {
|
|
|
const mode = config.mode || 'append';
|
|
const mode = config.mode || 'append';
|
|
|
- const first = input ? input.input1 : undefined;
|
|
|
|
|
- const second = input ? input.input2 : undefined;
|
|
|
|
|
|
|
+ const first = selectInput(input ? input.input1 : undefined, config.path1);
|
|
|
|
|
+ const second = selectInput(input ? input.input2 : undefined, config.path2);
|
|
|
|
|
|
|
|
if (mode === 'choose-first') {
|
|
if (mode === 'choose-first') {
|
|
|
const chosen = first !== undefined && first !== null ? first : second;
|
|
const chosen = first !== undefined && first !== null ? first : second;
|
|
@@ -1742,20 +1759,14 @@ Two independent fixture branches landing on the two named inputs, combined on `i
|
|
|
"config": {"code": "return [{id: 1, name: 'one'}, {id: 2, name: 'two'}];"}},
|
|
"config": {"code": "return [{id: 1, name: 'one'}, {id: 2, name: 'two'}];"}},
|
|
|
{"id": "b", "name": "BranchB", "type": "code", "position": {"x": 100, "y": 100},
|
|
{"id": "b", "name": "BranchB", "type": "code", "position": {"x": 100, "y": 100},
|
|
|
"config": {"code": "return [{id: 2, extra: 'from-b'}, {id: 3, name: 'three'}];"}},
|
|
"config": {"code": "return [{id: 2, extra: 'from-b'}, {id: 3, name: 'three'}];"}},
|
|
|
- {"id": "sa", "name": "ShapeA", "type": "code", "position": {"x": -100, "y": 180},
|
|
|
|
|
- "config": {"code": "return input.data.result;"}},
|
|
|
|
|
- {"id": "sb", "name": "ShapeB", "type": "code", "position": {"x": 100, "y": 180},
|
|
|
|
|
- "config": {"code": "return input.data.result;"}},
|
|
|
|
|
- {"id": "m", "name": "Merge", "type": "merge", "position": {"x": 0, "y": 280},
|
|
|
|
|
- "config": {"mode": "combine-by-key", "key": "id"}}
|
|
|
|
|
|
|
+ {"id": "m", "name": "Merge", "type": "merge", "position": {"x": 0, "y": 220},
|
|
|
|
|
+ "config": {"mode": "combine-by-key", "key": "id", "path1": "result", "path2": "result"}}
|
|
|
],
|
|
],
|
|
|
"connections": [
|
|
"connections": [
|
|
|
{"sourceNodeId": "n1", "sourceOutput": "main", "targetNodeId": "a", "targetInput": "data"},
|
|
{"sourceNodeId": "n1", "sourceOutput": "main", "targetNodeId": "a", "targetInput": "data"},
|
|
|
{"sourceNodeId": "n1", "sourceOutput": "main", "targetNodeId": "b", "targetInput": "data"},
|
|
{"sourceNodeId": "n1", "sourceOutput": "main", "targetNodeId": "b", "targetInput": "data"},
|
|
|
- {"sourceNodeId": "a", "sourceOutput": "main", "targetNodeId": "sa", "targetInput": "data"},
|
|
|
|
|
- {"sourceNodeId": "b", "sourceOutput": "main", "targetNodeId": "sb", "targetInput": "data"},
|
|
|
|
|
- {"sourceNodeId": "sa", "sourceOutput": "main", "targetNodeId": "m", "targetInput": "input1"},
|
|
|
|
|
- {"sourceNodeId": "sb", "sourceOutput": "main", "targetNodeId": "m", "targetInput": "input2"}
|
|
|
|
|
|
|
+ {"sourceNodeId": "a", "sourceOutput": "main", "targetNodeId": "m", "targetInput": "input1"},
|
|
|
|
|
+ {"sourceNodeId": "b", "sourceOutput": "main", "targetNodeId": "m", "targetInput": "input2"}
|
|
|
],
|
|
],
|
|
|
"expect": {
|
|
"expect": {
|
|
|
"m": {"status": "completed", "output": {"count": 3}}
|
|
"m": {"status": "completed", "output": {"count": 3}}
|
|
@@ -1763,7 +1774,17 @@ Two independent fixture branches landing on the two named inputs, combined on `i
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-The `ShapeA`/`ShapeB` nodes exist because the `code` node wraps its return in `{result, executionTime}`; they unwrap it so Merge receives bare arrays.
|
|
|
|
|
|
|
+No intermediate unwrapping nodes: a connection from a `main` port delivers the
|
|
|
|
|
+source node's whole output object (`workflow_engine.cpp:1248`), and the `code`
|
|
|
|
|
+node wraps its return as `{result, executionTime}`. So Merge receives those
|
|
|
|
|
+wrapper objects and `path1`/`path2` select the arrays inside them. Trying to
|
|
|
|
|
+unwrap with an intermediate `code` node does not work - that node re-wraps its
|
|
|
|
|
+own return on the way out.
|
|
|
|
|
+
|
|
|
|
|
+A connection from a *named* port behaves differently: the engine reads
|
|
|
|
|
+`output[portName]` directly (`workflow_engine.cpp:1242`), so Filter's `kept`
|
|
|
|
|
+port hands Merge a bare array and needs no path at all. Both routes are
|
|
|
|
|
+supported, which is why the paths are optional.
|
|
|
|
|
|
|
|
- [ ] **Step 3: Run it**
|
|
- [ ] **Step 3: Run it**
|
|
|
|
|
|