feat: Only send needed data to task runner (no-changelog) (#11487)

This commit is contained in:
Tomi Turtiainen
2024-11-04 11:13:09 +02:00
committed by GitHub
parent 2104fa1733
commit e4aa1d01f3
24 changed files with 1511 additions and 252 deletions

View File

@@ -388,8 +388,13 @@ export class WorkflowDataProxy {
* @private
* @param {string} nodeName The name of the node query data from
* @param {boolean} [shortSyntax=false] If short syntax got used
* @param {boolean} [throwOnMissingExecutionData=true] If an error should get thrown if no execution data is available
*/
private nodeDataGetter(nodeName: string, shortSyntax = false) {
private nodeDataGetter(
nodeName: string,
shortSyntax = false,
throwOnMissingExecutionData = true,
) {
const that = this;
const node = this.workflow.nodes[nodeName];
@@ -416,6 +421,10 @@ export class WorkflowDataProxy {
shortSyntax,
});
if (executionData.length === 0 && !throwOnMissingExecutionData) {
return undefined;
}
if (executionData.length === 0) {
if (that.workflow.getParentNodes(nodeName).length === 0) {
throw new ExpressionError('No execution data available', {
@@ -613,7 +622,7 @@ export class WorkflowDataProxy {
* Returns the data proxy object which allows to query data from current run
*
*/
getDataProxy(): IWorkflowDataProxyData {
getDataProxy(opts?: { throwOnMissingExecutionData: boolean }): IWorkflowDataProxyData {
const that = this;
// replacing proxies with the actual data.
@@ -1367,6 +1376,7 @@ export class WorkflowDataProxy {
$nodeId: that.workflow.getNode(that.activeNodeName)?.id,
$webhookId: that.workflow.getNode(that.activeNodeName)?.webhookId,
};
const throwOnMissingExecutionData = opts?.throwOnMissingExecutionData ?? true;
return new Proxy(base, {
has: () => true,
@@ -1374,10 +1384,11 @@ export class WorkflowDataProxy {
if (name === 'isProxy') return true;
if (['$data', '$json'].includes(name as string)) {
return that.nodeDataGetter(that.contextNodeName, true)?.json;
return that.nodeDataGetter(that.contextNodeName, true, throwOnMissingExecutionData)?.json;
}
if (name === '$binary') {
return that.nodeDataGetter(that.contextNodeName, true)?.binary;
return that.nodeDataGetter(that.contextNodeName, true, throwOnMissingExecutionData)
?.binary;
}
return Reflect.get(target, name, receiver);

View File

@@ -26,6 +26,7 @@ const getProxyFromFixture = (
run: IRun | null,
activeNode: string,
mode?: WorkflowExecuteMode,
opts?: { throwOnMissingExecutionData: boolean },
) => {
const taskData = run?.data.resultData.runData[activeNode]?.[0];
const lastNodeConnectionInputData = taskData?.data?.main[0];
@@ -73,7 +74,7 @@ const getProxyFromFixture = (
executeData,
);
return dataProxy.getDataProxy();
return dataProxy.getDataProxy(opts);
};
describe('WorkflowDataProxy', () => {
@@ -404,4 +405,42 @@ describe('WorkflowDataProxy', () => {
expect(proxy.$node.PinnedSet.json.firstName).toBe('Joe');
});
});
describe('Partial data', () => {
const fixture = loadFixture('partial_data');
describe('Default behaviour (throw on missing execution data)', () => {
const proxy = getProxyFromFixture(fixture.workflow, fixture.run, 'End');
test('$binary', () => {
expect(() => proxy.$binary).toThrowError(ExpressionError);
});
test('$json', () => {
expect(() => proxy.$json).toThrowError(ExpressionError);
});
test('$data', () => {
expect(() => proxy.$data).toThrowError(ExpressionError);
});
});
describe("Don't throw on missing execution data)", () => {
const proxy = getProxyFromFixture(fixture.workflow, fixture.run, 'End', undefined, {
throwOnMissingExecutionData: false,
});
test('$binary', () => {
expect(proxy.$binary).toBeUndefined();
});
test('$json', () => {
expect(proxy.$json).toBeUndefined();
});
test('$data', () => {
expect(proxy.$data).toBeUndefined();
});
});
});
});

View File

@@ -0,0 +1,71 @@
{
"data": {
"startData": {},
"resultData": {
"runData": {
"Start": [
{
"startTime": 1,
"executionTime": 1,
"data": {
"main": [
[
{
"json": {}
}
]
]
},
"source": []
}
],
"Function": [
{
"startTime": 1,
"executionTime": 1,
"data": {
"main": [[]]
},
"source": [
{
"previousNode": "Start"
}
]
}
],
"Rename": [
{
"startTime": 1,
"executionTime": 1,
"data": {
"main": [[]]
},
"source": [
{
"previousNode": "Function"
}
]
}
],
"End": [
{
"startTime": 1,
"executionTime": 1,
"data": {
"main": [[]]
},
"source": [
{
"previousNode": "Rename"
}
]
}
]
}
}
},
"mode": "manual",
"startedAt": "2024-02-08T15:45:18.848Z",
"stoppedAt": "2024-02-08T15:45:18.862Z",
"status": "running"
}

View File

@@ -0,0 +1,86 @@
{
"name": "",
"nodes": [
{
"name": "Start",
"type": "test.set",
"parameters": {},
"typeVersion": 1,
"id": "uuid-1",
"position": [100, 200]
},
{
"name": "Function",
"type": "test.set",
"parameters": {
"functionCode": "// Code here will run only once, no matter how many input items there are.\n// More info and help: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.function/\nconst { DateTime, Duration, Interval } = require(\"luxon\");\n\nconst data = [\n {\n \"length\": 105\n },\n {\n \"length\": 160\n },\n {\n \"length\": 121\n },\n {\n \"length\": 275\n },\n {\n \"length\": 950\n },\n];\n\nreturn data.map(fact => ({json: fact}));"
},
"typeVersion": 1,
"id": "uuid-2",
"position": [280, 200]
},
{
"name": "Rename",
"type": "test.set",
"parameters": {
"value1": "data",
"value2": "initialName"
},
"typeVersion": 1,
"id": "uuid-3",
"position": [460, 200]
},
{
"name": "Set",
"type": "test.set",
"parameters": {},
"typeVersion": 1,
"id": "uuid-4",
"position": [640, 200]
},
{
"name": "End",
"type": "test.set",
"parameters": {},
"typeVersion": 1,
"id": "uuid-5",
"position": [640, 200]
}
],
"pinData": {},
"connections": {
"Start": {
"main": [
[
{
"node": "Function",
"type": "main",
"index": 0
}
]
]
},
"Function": {
"main": [
[
{
"node": "Rename",
"type": "main",
"index": 0
}
]
]
},
"Rename": {
"main": [
[
{
"node": "End",
"type": "main",
"index": 0
}
]
]
}
}
}