Add functionality to evaluate expression

This commit is contained in:
Jan Oberhauser 2020-03-21 17:25:29 +01:00
parent e9c6c64290
commit c1853a6ff5
8 changed files with 43 additions and 2 deletions

View file

@ -49,6 +49,7 @@ 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 - **$data**: Incoming JSON data of a node
- **$evaluateExpression**: Evaluates a string as expression
- **$env**: Environment variables - **$env**: Environment variables
- **$node**: Data of other nodes (output-data, parameters) - **$node**: Data of other nodes (output-data, parameters)
- **$parameters**: Parameters of the current node - **$parameters**: Parameters of the current node

View file

@ -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) #### Method: getWorkflowStaticData(type)
Gives access to the static workflow data. 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: Example:
```typescript ```javascript
// Get the global workflow static data // Get the global workflow static data
const staticData = getWorkflowStaticData('global'); const staticData = getWorkflowStaticData('global');
// Get the static data of the node // Get the static data of the node

View file

@ -490,6 +490,9 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx
continueOnFail: () => { continueOnFail: () => {
return continueOnFail(node); 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<any> { // tslint:disable-line:no-any async executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any> { // tslint:disable-line:no-any
return additionalData.executeWorkflow(workflowInfo, additionalData, inputData); return additionalData.executeWorkflow(workflowInfo, additionalData, inputData);
}, },
@ -578,6 +581,10 @@ export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData:
continueOnFail: () => { continueOnFail: () => {
return continueOnFail(node); 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 { getContext(type: string): IContextObject {
return NodeHelpers.getContext(runExecutionData, type, node); return NodeHelpers.getContext(runExecutionData, type, node);
}, },

View file

@ -47,6 +47,9 @@ export class Function implements INodeType {
// Define the global objects for the custom function // Define the global objects for the custom function
const sandbox = { const sandbox = {
evaluateExpression: (expression: string, itemIndex = 0) => {
return this.evaluateExpression(expression, itemIndex);
},
getNodeParameter: this.getNodeParameter, getNodeParameter: this.getNodeParameter,
getWorkflowStaticData: this.getWorkflowStaticData, getWorkflowStaticData: this.getWorkflowStaticData,
helpers: this.helpers, helpers: this.helpers,

View file

@ -48,6 +48,9 @@ export class FunctionItem implements INodeType {
// Define the global objects for the custom function // Define the global objects for the custom function
const sandbox = { const sandbox = {
evaluateExpression: (expression: string, itemIndex: number | undefined) => {
return this.evaluateExpression(expression, itemIndex);
},
getBinaryData: (): IBinaryKeyData | undefined => { getBinaryData: (): IBinaryKeyData | undefined => {
return item.binary; return item.binary;
}, },

View file

@ -155,6 +155,7 @@ export interface IExecuteContextData {
export interface IExecuteFunctions { export interface IExecuteFunctions {
continueOnFail(): boolean; continueOnFail(): boolean;
evaluateExpression(expression: string, itemIndex: number): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any>; // tslint:disable-line:no-any executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any>; // tslint:disable-line:no-any
getContext(type: string): IContextObject; getContext(type: string): IContextObject;
getCredentials(type: string): ICredentialDataDecryptedObject | undefined; getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
@ -176,6 +177,7 @@ export interface IExecuteFunctions {
export interface IExecuteSingleFunctions { export interface IExecuteSingleFunctions {
continueOnFail(): boolean; continueOnFail(): boolean;
evaluateExpression(expression: string, itemIndex: number | undefined): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
getContext(type: string): IContextObject; getContext(type: string): IContextObject;
getCredentials(type: string): ICredentialDataDecryptedObject | undefined; getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
getInputData(inputIndex?: number, inputName?: string): INodeExecutionData; getInputData(inputIndex?: number, inputName?: string): INodeExecutionData;
@ -530,8 +532,12 @@ export interface IWorkflowDataProxyData {
$binary: any; // tslint:disable-line:no-any $binary: any; // tslint:disable-line:no-any
$data: any; // tslint:disable-line:no-any $data: any; // tslint:disable-line:no-any
$env: 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 $node: any; // tslint:disable-line:no-any
$parameter: any; // tslint:disable-line:no-any $parameter: any; // tslint:disable-line:no-any
$workflow: any; // tslint:disable-line:no-any
} }
export interface IWorkflowMetadata { export interface IWorkflowMetadata {

View file

@ -897,10 +897,14 @@ export class Workflow {
// Generate a data proxy which allows to query workflow data // Generate a data proxy which allows to query workflow data
const dataProxy = new WorkflowDataProxy(this, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData); 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 // Execute the expression
try { try {
const returnValue = tmpl.tmpl(parameterValue, dataProxy.getDataProxy()); const returnValue = tmpl.tmpl(parameterValue, data);
if (returnValue !== null && typeof returnValue === 'object') { if (returnValue !== null && typeof returnValue === 'object') {
if (Object.keys(returnValue).length === 0) { if (Object.keys(returnValue).length === 0) {
// When expression is incomplete it returns a Proxy which causes problems. // When expression is incomplete it returns a Proxy which causes problems.

View file

@ -300,6 +300,7 @@ export class WorkflowDataProxy {
$binary: {}, // Placeholder $binary: {}, // Placeholder
$data: {}, // Placeholder $data: {}, // Placeholder
$env: this.envGetter(), $env: this.envGetter(),
$evaluateExpression: (expression: string) => { }, // Placeholder
$item: (itemIndex: number) => { $item: (itemIndex: number) => {
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();