diff --git a/docs/node-basics.md b/docs/node-basics.md index dae607fb82..0895565d86 100644 --- a/docs/node-basics.md +++ b/docs/node-basics.md @@ -48,11 +48,13 @@ the value would be: "My name is: Jim" The following special variables are available: - **$binary**: Incoming binary data of a node - - **$data**: Incoming JSON data of a node - **$evaluateExpression**: Evaluates a string as expression - **$env**: Environment variables - - **$node**: Data of other nodes (output-data, parameters) + - **$items**: Environment variables + - **$json**: Incoming JSON data of a node + - **$node**: Data of other nodes (context, output-data, parameters) - **$parameters**: Parameters of the current node + - **$workflow**: Returns workflow metadata like: active, id, name Normally it is not needed to write the JavaScript variables manually as they can be simply selected with the help of the Expression Editor. diff --git a/docs/nodes.md b/docs/nodes.md index a3b0fc7b2e..95d9105383 100644 --- a/docs/nodes.md +++ b/docs/nodes.md @@ -59,7 +59,7 @@ return newItems; ``` -#### Method: $item(index) +#### Method: $item(index: number) With `$item` it is possible to access the data of parent nodes. That can be the item data but also the parameters. It expects as input an index of the item the data should be returned for. This is @@ -88,6 +88,28 @@ const channel = $item(9).$node["Slack"].parameter["channel"]; ``` +#### Method: $items(nodeName?: string, outputIndex?: number, runIndex?: number) + +Gives access to all the items of current or parent nodes. If no parameters get supplied +it returns all the items of the current node. +If a node-name is given, it returns the items the give node did output. By default of the +first output (index: 0, most nodes only have one output, exceptions are IF and Switch-Node) +and the current run. + +Example: + +```typescript +// Returns all the items of the current node and current run +const allItems = $items(); + +// Returns all items the node "IF" outputs (index: 0 which is Output "true" of current run) +const allItems = $items("IF"); + +// Returns all items the node "IF" outputs (index: 1 which is Output "false" of run 0 which is the first one) +const allItems = $items("IF", 1, 0); +``` + + #### Variable: $node Works exactly like `$item` with the difference that it will always return the data of the first item. diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 2d0d92d835..45769bb290 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -534,6 +534,7 @@ export interface IWorkflowDataProxyData { $env: any; // tslint:disable-line:no-any $evaluateExpression: any; // tslint:disable-line:no-any $item: any; // tslint:disable-line:no-any + $items: any; // tslint:disable-line:no-any $json: any; // tslint:disable-line:no-any $node: any; // tslint:disable-line:no-any $parameter: any; // tslint:disable-line:no-any diff --git a/packages/workflow/src/WorkflowDataProxy.ts b/packages/workflow/src/WorkflowDataProxy.ts index 527b186196..edf9f264f7 100644 --- a/packages/workflow/src/WorkflowDataProxy.ts +++ b/packages/workflow/src/WorkflowDataProxy.ts @@ -104,6 +104,79 @@ export class WorkflowDataProxy { } + /** + * Returns the node ExecutionData + * + * @private + * @param {string} nodeName The name of the node query data from + * @param {boolean} [shortSyntax=false] If short syntax got used + * @param {number} [outputIndex] The index of the output, if not given the first one gets used + * @param {number} [runIndex] The index of the run, if not given the current one does get used + * @returns {INodeExecutionData[]} + * @memberof WorkflowDataProxy + */ + private getNodeExecutionData(nodeName: string, shortSyntax = false, outputIndex?: number, runIndex?: number): INodeExecutionData[] { + const that = this; + + let executionData: INodeExecutionData[]; + if (shortSyntax === false) { + // Long syntax got used to return data from node in path + + if (that.runExecutionData === null) { + throw new Error(`Workflow did not run so do not have any execution-data.`); + } + + if (!that.runExecutionData.resultData.runData.hasOwnProperty(nodeName)) { + throw new Error(`No execution data found for node "${nodeName}"`); + } + + runIndex = runIndex === undefined ? that.runIndex : 0; + + if (that.runExecutionData.resultData.runData[nodeName].length < runIndex) { + throw new Error(`No execution data found for run "${runIndex}" of node "${nodeName}"`); + } + + const taskData = that.runExecutionData.resultData.runData[nodeName][runIndex].data!; + + if (taskData.main === null || !taskData.main.length || taskData.main[0] === null) { + // throw new Error(`No data found for item-index: "${itemIndex}"`); + throw new Error(`No data found from "main" input.`); + } + + // Check from which output to read the data. + // Depends on how the nodes are connected. + // (example "IF" node. If node is connected to "true" or to "false" output) + if (outputIndex === undefined) { + const outputIndex = that.workflow.getNodeConnectionOutputIndex(that.activeNodeName, nodeName, 'main'); + + if (outputIndex === undefined) { + throw new Error(`The node "${that.activeNodeName}" is not connected with node "${nodeName}" so no data can get returned from it.`); + } + } + + if (outputIndex === undefined) { + outputIndex = 0; + } + + if (taskData.main.length < outputIndex) { + throw new Error(`No data found from "main" input with index "${outputIndex}" via which node is connected with.`); + } + + executionData = taskData.main[outputIndex] as INodeExecutionData[]; + } else { + // Short syntax got used to return data from active node + + // TODO: Here have to generate connection Input data for the current node by itself + // Data needed: + // #- the run-index + // - node which did send data (has to be the one from last recent execution) + // - later also the name of the input and its index (currently not needed as it is always "main" and index "0") + executionData = that.connectionInputData; + } + + return executionData; + } + /** * Returns a proxy which allows to query data of a given node @@ -128,53 +201,7 @@ export class WorkflowDataProxy { name = name.toString(); if (['binary', 'data', 'json'].includes(name)) { - let executionData: INodeExecutionData[]; - if (shortSyntax === false) { - // Long syntax got used to return data from node in path - - if (that.runExecutionData === null) { - throw new Error(`Workflow did not run so do not have any execution-data.`); - } - - if (!that.runExecutionData.resultData.runData.hasOwnProperty(nodeName)) { - throw new Error(`No execution data found for node "${nodeName}"`); - } - - if (that.runExecutionData.resultData.runData[nodeName].length < that.runIndex) { - throw new Error(`No execution data found for run "${that.runIndex}" of node "${nodeName}"`); - } - - const taskData = that.runExecutionData.resultData.runData[nodeName][that.runIndex].data!; - - if (taskData.main === null || !taskData.main.length || taskData.main[0] === null) { - // throw new Error(`No data found for item-index: "${itemIndex}"`); - throw new Error(`No data found from "main" input.`); - } - - // Check from which output to read the data. - // Depends on how the nodes are connected. - // (example "IF" node. If node is connected to "true" or to "false" output) - const outputIndex = that.workflow.getNodeConnectionOutputIndex(that.activeNodeName, nodeName, 'main'); - - if (outputIndex === undefined) { - throw new Error(`The node "${that.activeNodeName}" is not connected with node "${nodeName}" so no data can get returned from it.`); - } - - if (taskData.main.length < outputIndex) { - throw new Error(`No data found from "main" input with index "${outputIndex}" via which node is connected with.`); - } - - executionData = taskData.main[outputIndex] as INodeExecutionData[]; - } else { - // Short syntax got used to return data from active node - - // TODO: Here have to generate connection Input data for the current node by itself - // Data needed: - // #- the run-index - // - node which did send data (has to be the one from last recent execution) - // - later also the name of the input and its index (currently not needed as it is always "main" and index "0") - executionData = that.connectionInputData; - } + const executionData = that.getNodeExecutionData(name, shortSyntax); if (executionData.length <= that.itemIndex) { throw new Error(`No data found for item-index: "${that.itemIndex}"`); @@ -305,6 +332,18 @@ export class WorkflowDataProxy { const dataProxy = new WorkflowDataProxy(this.workflow, this.runExecutionData, this.runIndex, itemIndex, this.activeNodeName, this.connectionInputData); return dataProxy.getDataProxy(); }, + $items: (nodeName?: string, outputIndex?: number, runIndex?: number) => { + let executionData: INodeExecutionData[]; + + if (nodeName === undefined) { + executionData = that.connectionInputData; + } else { + outputIndex = outputIndex || 0; + executionData = that.getNodeExecutionData(nodeName, false, outputIndex, runIndex); + } + + return executionData; + }, $json: {}, // Placeholder $node: this.nodeGetter(), $parameter: this.nodeParameterGetter(this.activeNodeName),