mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
fix(core)!: $(...).[last,first,all]()
defaulting to the first output instead of the output that connects the nodes (#9760)
This commit is contained in:
parent
77bf16667b
commit
4ac9266820
|
@ -2,6 +2,18 @@
|
||||||
|
|
||||||
This list shows all the versions which include breaking changes and how to upgrade.
|
This list shows all the versions which include breaking changes and how to upgrade.
|
||||||
|
|
||||||
|
## 1.47.0
|
||||||
|
|
||||||
|
### What changed?
|
||||||
|
|
||||||
|
Calling `$(...).last()` (or `$(...).first()` or `$(...).all()` respectively) without arguments is returning the the last item (or first or all items) of the output that connects the two nodes. Before it was returning the item/items of the first output of that node.
|
||||||
|
|
||||||
|
### When is action necessary?
|
||||||
|
|
||||||
|
If you are using `$(...).last()` (or `$(...).first()` or `$(...)all()` respectively) without arguments for nodes that have multiple outputs (e.g. `If`, `Switch`, `Compare Datasets`, etc.) and you want it to default to the first output. In that case change it to `$(...).last(0)` (or `first` or `all` respectively).
|
||||||
|
|
||||||
|
This does not affect the Array functions `[].last()`, `[].first()`.
|
||||||
|
|
||||||
## 1.40.0
|
## 1.40.0
|
||||||
|
|
||||||
### What changed?
|
### What changed?
|
||||||
|
|
|
@ -619,18 +619,9 @@ export class WorkflowDataProxy {
|
||||||
getDataProxy(): IWorkflowDataProxyData {
|
getDataProxy(): IWorkflowDataProxyData {
|
||||||
const that = this;
|
const that = this;
|
||||||
|
|
||||||
const getNodeOutput = (nodeName?: string, branchIndex?: number, runIndex?: number) => {
|
const getNodeOutput = (nodeName: string, branchIndex: number, runIndex?: number) => {
|
||||||
let executionData: INodeExecutionData[];
|
|
||||||
|
|
||||||
if (nodeName === undefined) {
|
|
||||||
executionData = that.connectionInputData;
|
|
||||||
} else {
|
|
||||||
branchIndex = branchIndex || 0;
|
|
||||||
runIndex = runIndex === undefined ? -1 : runIndex;
|
runIndex = runIndex === undefined ? -1 : runIndex;
|
||||||
executionData = that.getNodeExecutionData(nodeName, false, branchIndex, runIndex);
|
return that.getNodeExecutionData(nodeName, false, branchIndex, runIndex);
|
||||||
}
|
|
||||||
|
|
||||||
return executionData;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// replacing proxies with the actual data.
|
// replacing proxies with the actual data.
|
||||||
|
@ -1073,6 +1064,12 @@ export class WorkflowDataProxy {
|
||||||
if (property === 'first') {
|
if (property === 'first') {
|
||||||
ensureNodeExecutionData();
|
ensureNodeExecutionData();
|
||||||
return (branchIndex?: number, runIndex?: number) => {
|
return (branchIndex?: number, runIndex?: number) => {
|
||||||
|
branchIndex =
|
||||||
|
branchIndex ??
|
||||||
|
// default to the output the active node is connected to
|
||||||
|
that.workflow.getNodeConnectionIndexes(that.activeNodeName, nodeName)
|
||||||
|
?.sourceIndex ??
|
||||||
|
0;
|
||||||
const executionData = getNodeOutput(nodeName, branchIndex, runIndex);
|
const executionData = getNodeOutput(nodeName, branchIndex, runIndex);
|
||||||
if (executionData[0]) return executionData[0];
|
if (executionData[0]) return executionData[0];
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -1081,6 +1078,12 @@ export class WorkflowDataProxy {
|
||||||
if (property === 'last') {
|
if (property === 'last') {
|
||||||
ensureNodeExecutionData();
|
ensureNodeExecutionData();
|
||||||
return (branchIndex?: number, runIndex?: number) => {
|
return (branchIndex?: number, runIndex?: number) => {
|
||||||
|
branchIndex =
|
||||||
|
branchIndex ??
|
||||||
|
// default to the output the active node is connected to
|
||||||
|
that.workflow.getNodeConnectionIndexes(that.activeNodeName, nodeName)
|
||||||
|
?.sourceIndex ??
|
||||||
|
0;
|
||||||
const executionData = getNodeOutput(nodeName, branchIndex, runIndex);
|
const executionData = getNodeOutput(nodeName, branchIndex, runIndex);
|
||||||
if (!executionData.length) return undefined;
|
if (!executionData.length) return undefined;
|
||||||
if (executionData[executionData.length - 1]) {
|
if (executionData[executionData.length - 1]) {
|
||||||
|
@ -1091,8 +1094,15 @@ export class WorkflowDataProxy {
|
||||||
}
|
}
|
||||||
if (property === 'all') {
|
if (property === 'all') {
|
||||||
ensureNodeExecutionData();
|
ensureNodeExecutionData();
|
||||||
return (branchIndex?: number, runIndex?: number) =>
|
return (branchIndex?: number, runIndex?: number) => {
|
||||||
getNodeOutput(nodeName, branchIndex, runIndex);
|
branchIndex =
|
||||||
|
branchIndex ??
|
||||||
|
// default to the output the active node is connected to
|
||||||
|
that.workflow.getNodeConnectionIndexes(that.activeNodeName, nodeName)
|
||||||
|
?.sourceIndex ??
|
||||||
|
0;
|
||||||
|
return getNodeOutput(nodeName, branchIndex, runIndex);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (property === 'context') {
|
if (property === 'context') {
|
||||||
return that.nodeContextGetter(nodeName);
|
return that.nodeContextGetter(nodeName);
|
||||||
|
|
|
@ -53,6 +53,46 @@ const getProxyFromFixture = (workflow: IWorkflowBase, run: IRun | null, activeNo
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('WorkflowDataProxy', () => {
|
describe('WorkflowDataProxy', () => {
|
||||||
|
describe('$(If))', () => {
|
||||||
|
const fixture = loadFixture('multiple_outputs');
|
||||||
|
const proxy = getProxyFromFixture(fixture.workflow, fixture.run, 'Edit Fields');
|
||||||
|
|
||||||
|
test('last() should use the output the node is connected to by default', () => {
|
||||||
|
expect(proxy.$('If').last().json.code).toEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('last(0) should use the first output', () => {
|
||||||
|
expect(proxy.$('If').last(0)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('last(1) should use the second output', () => {
|
||||||
|
expect(proxy.$('If').last(1).json.code).toEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('first() should use the output the node is connected to by default', () => {
|
||||||
|
expect(proxy.$('If').first().json.code).toEqual(1);
|
||||||
|
});
|
||||||
|
test('first(0) should use the output the node is connected to by default', () => {
|
||||||
|
expect(proxy.$('If').first(0)).toBeUndefined();
|
||||||
|
});
|
||||||
|
test('first(1) should use the output the node is connected to by default', () => {
|
||||||
|
expect(proxy.$('If').first(1).json.code).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('all() should use the output the node is connected to by default', () => {
|
||||||
|
expect(proxy.$('If').all()[0].json.code).toEqual(1);
|
||||||
|
expect(proxy.$('If').all()[1].json.code).toEqual(2);
|
||||||
|
});
|
||||||
|
test('all(0) should use the output the node is connected to by default', () => {
|
||||||
|
expect(proxy.$('If').all(0)[0]).toBeUndefined();
|
||||||
|
expect(proxy.$('If').all(0)[1]).toBeUndefined();
|
||||||
|
});
|
||||||
|
test('all(1) should use the output the node is connected to by default', () => {
|
||||||
|
expect(proxy.$('If').all(1)[0].json.code).toEqual(1);
|
||||||
|
expect(proxy.$('If').all(1)[1].json.code).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Base', () => {
|
describe('Base', () => {
|
||||||
const fixture = loadFixture('base');
|
const fixture = loadFixture('base');
|
||||||
const proxy = getProxyFromFixture(fixture.workflow, fixture.run, 'End');
|
const proxy = getProxyFromFixture(fixture.workflow, fixture.run, 'End');
|
||||||
|
|
84
packages/workflow/test/fixtures/WorkflowDataProxy/multiple_outputs_run.json
vendored
Normal file
84
packages/workflow/test/fixtures/WorkflowDataProxy/multiple_outputs_run.json
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"startData": {},
|
||||||
|
"resultData": {
|
||||||
|
"runData": {
|
||||||
|
"When clicking ‘Test workflow’": [
|
||||||
|
{
|
||||||
|
"hints": [],
|
||||||
|
"startTime": 1718369813697,
|
||||||
|
"executionTime": 0,
|
||||||
|
"source": [],
|
||||||
|
"executionStatus": "success",
|
||||||
|
"data": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"name": "First item",
|
||||||
|
"code": 1
|
||||||
|
},
|
||||||
|
"pairedItem": {
|
||||||
|
"item": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"name": "Second item",
|
||||||
|
"code": 2
|
||||||
|
},
|
||||||
|
"pairedItem": {
|
||||||
|
"item": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"If": [
|
||||||
|
{
|
||||||
|
"hints": [],
|
||||||
|
"startTime": 1718369813698,
|
||||||
|
"executionTime": 1,
|
||||||
|
"source": [
|
||||||
|
{
|
||||||
|
"previousNode": "When clicking ‘Test workflow’"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"executionStatus": "success",
|
||||||
|
"data": {
|
||||||
|
"main": [
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"name": "First item",
|
||||||
|
"code": 1
|
||||||
|
},
|
||||||
|
"pairedItem": {
|
||||||
|
"item": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"name": "Second item",
|
||||||
|
"code": 2
|
||||||
|
},
|
||||||
|
"pairedItem": {
|
||||||
|
"item": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mode": "manual",
|
||||||
|
"startedAt": "2024-02-08T15:45:18.848Z",
|
||||||
|
"stoppedAt": "2024-02-08T15:45:18.862Z",
|
||||||
|
"status": "running"
|
||||||
|
}
|
84
packages/workflow/test/fixtures/WorkflowDataProxy/multiple_outputs_workflow.json
vendored
Normal file
84
packages/workflow/test/fixtures/WorkflowDataProxy/multiple_outputs_workflow.json
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"instanceId": "060d2be233778dc6349e0f3fa8d972652e8dff467638325ffc56812c6b66ef1a"
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "656d6d8d-1af6-4af7-ab53-7c4e495ee51c",
|
||||||
|
"name": "When clicking ‘Test workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [-300, 1880]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"leftValue": "",
|
||||||
|
"typeValidation": "strict"
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "1fff886f-3d13-4fbf-b0fb-7e2f845937c0",
|
||||||
|
"leftValue": "={{ false }}",
|
||||||
|
"rightValue": "",
|
||||||
|
"operator": {
|
||||||
|
"type": "boolean",
|
||||||
|
"operation": "true",
|
||||||
|
"singleValue": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "204feab7-f5e9-458f-8fdb-4b762b184147",
|
||||||
|
"name": "If",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [40, 1880]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"assignments": {
|
||||||
|
"assignments": []
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "ab122060-f8da-475e-b6c6-d9e486289e1f",
|
||||||
|
"name": "Edit Fields",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.3,
|
||||||
|
"position": [360, 2080]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"When clicking ‘Test workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "If",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"If": {
|
||||||
|
"main": [
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Edit Fields",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pinData": {}
|
||||||
|
}
|
Loading…
Reference in a new issue