mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
✨ Add $items to expression and Function-Nodes to get all items
This commit is contained in:
parent
135e467be9
commit
09e528565f
|
@ -48,11 +48,13 @@ the value would be: "My name is: Jim"
|
||||||
The following special variables are available:
|
The following special variables are available:
|
||||||
|
|
||||||
- **$binary**: Incoming binary data of a node
|
- **$binary**: Incoming binary data of a node
|
||||||
- **$data**: Incoming JSON data of a node
|
|
||||||
- **$evaluateExpression**: Evaluates a string as expression
|
- **$evaluateExpression**: Evaluates a string as expression
|
||||||
- **$env**: Environment variables
|
- **$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
|
- **$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.
|
Normally it is not needed to write the JavaScript variables manually as they can be simply selected with the help of the Expression Editor.
|
||||||
|
|
||||||
|
|
|
@ -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
|
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
|
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
|
#### Variable: $node
|
||||||
|
|
||||||
Works exactly like `$item` with the difference that it will always return the data of the first item.
|
Works exactly like `$item` with the difference that it will always return the data of the first item.
|
||||||
|
|
|
@ -534,6 +534,7 @@ export interface IWorkflowDataProxyData {
|
||||||
$env: any; // tslint:disable-line:no-any
|
$env: any; // tslint:disable-line:no-any
|
||||||
$evaluateExpression: any; // tslint:disable-line:no-any
|
$evaluateExpression: any; // tslint:disable-line:no-any
|
||||||
$item: 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
|
$json: any; // tslint:disable-line:no-any
|
||||||
$node: any; // tslint:disable-line:no-any
|
$node: any; // tslint:disable-line:no-any
|
||||||
$parameter: any; // tslint:disable-line:no-any
|
$parameter: any; // tslint:disable-line:no-any
|
||||||
|
|
|
@ -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
|
* Returns a proxy which allows to query data of a given node
|
||||||
|
@ -128,53 +201,7 @@ export class WorkflowDataProxy {
|
||||||
name = name.toString();
|
name = name.toString();
|
||||||
|
|
||||||
if (['binary', 'data', 'json'].includes(name)) {
|
if (['binary', 'data', 'json'].includes(name)) {
|
||||||
let executionData: INodeExecutionData[];
|
const executionData = that.getNodeExecutionData(name, shortSyntax);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executionData.length <= that.itemIndex) {
|
if (executionData.length <= that.itemIndex) {
|
||||||
throw new Error(`No data found for item-index: "${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);
|
const dataProxy = new WorkflowDataProxy(this.workflow, this.runExecutionData, this.runIndex, itemIndex, this.activeNodeName, this.connectionInputData);
|
||||||
return dataProxy.getDataProxy();
|
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
|
$json: {}, // Placeholder
|
||||||
$node: this.nodeGetter(),
|
$node: this.nodeGetter(),
|
||||||
$parameter: this.nodeParameterGetter(this.activeNodeName),
|
$parameter: this.nodeParameterGetter(this.activeNodeName),
|
||||||
|
|
Loading…
Reference in a new issue