diff --git a/docs/node-basics.md b/docs/node-basics.md index ff319fd4d7..dae607fb82 100644 --- a/docs/node-basics.md +++ b/docs/node-basics.md @@ -49,6 +49,7 @@ 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) - **$parameters**: Parameters of the current node diff --git a/docs/nodes.md b/docs/nodes.md index 03620ab01e..a3b0fc7b2e 100644 --- a/docs/nodes.md +++ b/docs/nodes.md @@ -99,6 +99,22 @@ const channel = $node["Slack"].parameter["channel"]; ``` +#### Method: evaluateExpression(expression: string, itemIndex: number) + +Evaluates a given string as expression. +If no `itemIndex` is provided it uses by default in the Function-Node the data of item 0 and +in the Function Item-Node the data of the current item. + +Example: + +```javascript +items[0].json.variable1 = evaluateExpression('{{1+2}}'); +items[0].json.variable2 = evaluateExpression($node["Set"].json["myExpression"], 1); + +return items; +``` + + #### Method: getWorkflowStaticData(type) Gives access to the static workflow data. @@ -114,7 +130,7 @@ same in the whole workflow. And every node in the workflow can access it. The no Example: -```typescript +```javascript // Get the global workflow static data const staticData = getWorkflowStaticData('global'); // Get the static data of the node diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 212f8140b1..5b31f7bed8 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -490,6 +490,9 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx continueOnFail: () => { return continueOnFail(node); }, + evaluateExpression: (expression: string, itemIndex: number) => { + return workflow.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, itemIndex, node.name, connectionInputData); + }, async executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise { // tslint:disable-line:no-any return additionalData.executeWorkflow(workflowInfo, additionalData, inputData); }, @@ -578,6 +581,10 @@ export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData: continueOnFail: () => { return continueOnFail(node); }, + evaluateExpression: (expression: string, evaluateItemIndex: number | undefined) => { + evaluateItemIndex = evaluateItemIndex === undefined ? itemIndex : evaluateItemIndex; + return workflow.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, evaluateItemIndex, node.name, connectionInputData); + }, getContext(type: string): IContextObject { return NodeHelpers.getContext(runExecutionData, type, node); }, diff --git a/packages/nodes-base/nodes/Function.node.ts b/packages/nodes-base/nodes/Function.node.ts index 7f5ea4c1ea..253a216650 100644 --- a/packages/nodes-base/nodes/Function.node.ts +++ b/packages/nodes-base/nodes/Function.node.ts @@ -47,6 +47,9 @@ export class Function implements INodeType { // Define the global objects for the custom function const sandbox = { + evaluateExpression: (expression: string, itemIndex = 0) => { + return this.evaluateExpression(expression, itemIndex); + }, getNodeParameter: this.getNodeParameter, getWorkflowStaticData: this.getWorkflowStaticData, helpers: this.helpers, diff --git a/packages/nodes-base/nodes/FunctionItem.node.ts b/packages/nodes-base/nodes/FunctionItem.node.ts index 6352fc3f98..356e419477 100644 --- a/packages/nodes-base/nodes/FunctionItem.node.ts +++ b/packages/nodes-base/nodes/FunctionItem.node.ts @@ -48,6 +48,9 @@ export class FunctionItem implements INodeType { // Define the global objects for the custom function const sandbox = { + evaluateExpression: (expression: string, itemIndex: number | undefined) => { + return this.evaluateExpression(expression, itemIndex); + }, getBinaryData: (): IBinaryKeyData | undefined => { return item.binary; }, diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 8e7fe082e9..44e4373e1d 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -155,6 +155,7 @@ export interface IExecuteContextData { export interface IExecuteFunctions { continueOnFail(): boolean; + evaluateExpression(expression: string, itemIndex: number): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[]; executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise; // tslint:disable-line:no-any getContext(type: string): IContextObject; getCredentials(type: string): ICredentialDataDecryptedObject | undefined; @@ -176,6 +177,7 @@ export interface IExecuteFunctions { export interface IExecuteSingleFunctions { continueOnFail(): boolean; + evaluateExpression(expression: string, itemIndex: number | undefined): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[]; getContext(type: string): IContextObject; getCredentials(type: string): ICredentialDataDecryptedObject | undefined; getInputData(inputIndex?: number, inputName?: string): INodeExecutionData; @@ -530,8 +532,12 @@ export interface IWorkflowDataProxyData { $binary: any; // tslint:disable-line:no-any $data: any; // tslint:disable-line:no-any $env: any; // tslint:disable-line:no-any + $evaluateExpression: any; // tslint:disable-line:no-any + $item: 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 + $workflow: any; // tslint:disable-line:no-any } export interface IWorkflowMetadata { diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index 19749c7920..a63060913c 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -897,10 +897,14 @@ export class Workflow { // Generate a data proxy which allows to query workflow data const dataProxy = new WorkflowDataProxy(this, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData); + const data = dataProxy.getDataProxy(); + data.$evaluateExpression = (expression: string) => { + return this.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString); + }; // Execute the expression try { - const returnValue = tmpl.tmpl(parameterValue, dataProxy.getDataProxy()); + const returnValue = tmpl.tmpl(parameterValue, data); if (returnValue !== null && typeof returnValue === 'object') { if (Object.keys(returnValue).length === 0) { // When expression is incomplete it returns a Proxy which causes problems. diff --git a/packages/workflow/src/WorkflowDataProxy.ts b/packages/workflow/src/WorkflowDataProxy.ts index 6a0ac34787..527b186196 100644 --- a/packages/workflow/src/WorkflowDataProxy.ts +++ b/packages/workflow/src/WorkflowDataProxy.ts @@ -300,6 +300,7 @@ export class WorkflowDataProxy { $binary: {}, // Placeholder $data: {}, // Placeholder $env: this.envGetter(), + $evaluateExpression: (expression: string) => { }, // Placeholder $item: (itemIndex: number) => { const dataProxy = new WorkflowDataProxy(this.workflow, this.runExecutionData, this.runIndex, itemIndex, this.activeNodeName, this.connectionInputData); return dataProxy.getDataProxy();