🎨 Set up linting and formatting (#2120)

* ⬆️ Upgrade TS to 4.3.5

* 👕 Add ESLint configs

* 🎨 Add Prettier config

* 📦 Add deps and commands

*  Adjust global .editorconfig to new ruleset

* 🔥 Remove unneeded local .editorconfig

* 📦 Update deps in editor-ui

* 🔨 Limit Prettier to only TS files

*  Add recommended VSCode extensions

* 👕 Fix build

* 🔥 Remove Vue setting from global config

*  Disable prefer-default-export per feedback

* ✏️ Add forgotten divider

* 👕 Disable no-plusplus

* 👕 Disable class-methods-use-this

* ✏️ Alphabetize overrides

* 👕 Add one-var consecutive override

*  Revert one-var consecutive override

This reverts commit b9252cf935659ba6d76727ad484a1d3c00008fcc.

* 🎨 👕 Lint and format workflow package (#2121)

* 🎨 Format /workflow package

* 👕 Lint /workflow package

* 🎨 Re-format /workflow package

* 👕 Re-lint /workflow package

* ✏️ Fix typo

*  Consolidate if-checks

* 🔥 Remove prefer-default-export exceptions

* 🔥 Remove no-plusplus exceptions

* 🔥 Remove class-methods-use-this exceptions

* 🎨 👕 Lint and format node-dev package (#2122)

* 🎨 Format /node-dev package

*  Exclude templates from ESLint config

This keeps the templates consistent with the codebase while preventing lint exceptions from being made part of the templates.

* 👕 Lint /node-dev package

* 🔥 Remove prefer-default-export exceptions

* 🔥 Remove no-plusplus exceptions

* 🎨 👕 Lint and format core package (#2123)

* 🎨 Format /core package

* 👕 Lint /core package

* 🎨 Re-format /core package

* 👕 Re-lint /core package

* 🔥 Remove prefer-default-export exceptions

* 🔥 Remove no-plusplus exceptions

* 🔥 Remove class-methods-use-this exceptions

* 🎨 👕 Lint and format cli package (#2124)

* 🎨 Format /cli package

* 👕 Exclude migrations from linting

* 👕 Lint /cli package

* 🎨 Re-format /cli package

* 👕 Re-lint /cli package

* 👕 Fix build

* 🔥 Remove prefer-default-export exceptions

*  Update exceptions in ActiveExecutions

* 🔥 Remove no-plusplus exceptions

* 🔥 Remove class-methods-use-this exceptions

* 👕 fix lint issues

* 🔧 use package specific linter, remove tslint command

* 🔨 resolve build issue, sync dependencies

* 🔧 change lint command

Co-authored-by: Ben Hesseldieck <b.hesseldieck@gmail.com>
This commit is contained in:
Iván Ovejero
2021-08-29 20:58:11 +02:00
committed by GitHub
parent 223cd75685
commit 56c4c6991f
108 changed files with 11832 additions and 8416 deletions

View File

@@ -17,8 +17,9 @@
"scripts": {
"dev": "npm run watch",
"build": "tsc",
"tslint": "tslint -p tsconfig.json -c tslint.json",
"tslintfix": "tslint --fix -p tsconfig.json -c tslint.json",
"format": "cd ../.. && node_modules/prettier/bin-prettier.js packages/workflow/**/**.ts --write",
"lint": "cd ../.. && node_modules/eslint/bin/eslint.js packages/workflow",
"lintfix": "cd ../.. && node_modules/eslint/bin/eslint.js packages/workflow --fix",
"watch": "tsc --watch",
"test": "jest"
},
@@ -31,10 +32,18 @@
"@types/lodash.get": "^4.4.6",
"@types/node": "^14.14.40",
"@types/xml2js": "^0.4.3",
"@typescript-eslint/eslint-plugin": "^4.29.0",
"@typescript-eslint/parser": "^4.29.0",
"eslint": "^7.32.0",
"eslint-config-airbnb-typescript": "^12.3.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-prettier": "^3.4.0",
"jest": "^26.4.2",
"prettier": "^2.3.2",
"ts-jest": "^26.3.0",
"tslint": "^6.1.2",
"typescript": "~3.9.7"
"typescript": "~4.3.5"
},
"dependencies": {
"lodash.get": "^4.4.2",

View File

@@ -1,4 +1,6 @@
// @ts-ignore
import * as tmpl from 'riot-tmpl';
// eslint-disable-next-line import/no-cycle
import {
INode,
INodeExecutionData,
@@ -9,28 +11,26 @@ import {
Workflow,
WorkflowDataProxy,
WorkflowExecuteMode,
} from './';
} from '.';
// @ts-ignore
import * as tmpl from 'riot-tmpl';
// Set it to use double curly brackets instead of single ones
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
tmpl.brackets.set('{{ }}');
// Make sure that it does not always print an error when it could not resolve
// a variable
tmpl.tmpl.errorHandler = () => { };
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
tmpl.tmpl.errorHandler = () => {};
export class Expression {
workflow: Workflow;
constructor(workflow: Workflow) {
this.workflow = workflow;
}
/**
* Converts an object to a string in a way to make it clear that
* the value comes from an object
@@ -44,8 +44,6 @@ export class Expression {
return `[${typeName}: ${JSON.stringify(value)}]`;
}
/**
* Resolves the paramter value. If it is an expression it will execute it and
* return the result. For everything simply the supplied value will be returned.
@@ -60,7 +58,19 @@ export class Expression {
* @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])}
* @memberof Workflow
*/
resolveSimpleParameterValue(parameterValue: NodeParameterValue, siblingParameters: INodeParameters, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], mode: WorkflowExecuteMode, additionalKeys: IWorkflowDataProxyAdditionalKeys, returnObjectAsString = false, selfData = {}): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] {
resolveSimpleParameterValue(
parameterValue: NodeParameterValue,
siblingParameters: INodeParameters,
runExecutionData: IRunExecutionData | null,
runIndex: number,
itemIndex: number,
activeNodeName: string,
connectionInputData: INodeExecutionData[],
mode: WorkflowExecuteMode,
additionalKeys: IWorkflowDataProxyAdditionalKeys,
returnObjectAsString = false,
selfData = {},
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] {
// Check if it is an expression
if (typeof parameterValue !== 'string' || parameterValue.charAt(0) !== '=') {
// Is no expression so return value
@@ -70,30 +80,44 @@ export class Expression {
// Is an expression
// Remove the equal sign
// eslint-disable-next-line no-param-reassign
parameterValue = parameterValue.substr(1);
// Generate a data proxy which allows to query workflow data
const dataProxy = new WorkflowDataProxy(this.workflow, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, siblingParameters, mode, additionalKeys, -1, selfData);
const dataProxy = new WorkflowDataProxy(
this.workflow,
runExecutionData,
runIndex,
itemIndex,
activeNodeName,
connectionInputData,
siblingParameters,
mode,
additionalKeys,
-1,
selfData,
);
const data = dataProxy.getDataProxy();
// Execute the expression
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
const returnValue = tmpl.tmpl(parameterValue, data);
if (typeof returnValue === 'function') {
throw new Error('Expression resolved to a function. Please add "()"');
} else if (returnValue !== null && typeof returnValue === 'object') {
if (returnObjectAsString === true) {
if (returnObjectAsString) {
return this.convertObjectValueToString(returnValue);
}
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return returnValue;
} catch (e) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-unsafe-member-access
throw new Error(`Expression is not valid: ${e.message}`);
}
}
/**
* Resolves value of parameter. But does not work for workflow-data.
*
@@ -103,7 +127,13 @@ export class Expression {
* @returns {(string | undefined)}
* @memberof Workflow
*/
getSimpleParameterValue(node: INode, parameterValue: string | boolean | undefined, mode: WorkflowExecuteMode, additionalKeys: IWorkflowDataProxyAdditionalKeys, defaultValue?: boolean | number | string): boolean | number | string | undefined {
getSimpleParameterValue(
node: INode,
parameterValue: string | boolean | undefined,
mode: WorkflowExecuteMode,
additionalKeys: IWorkflowDataProxyAdditionalKeys,
defaultValue?: boolean | number | string,
): boolean | number | string | undefined {
if (parameterValue === undefined) {
// Value is not set so return the default
return defaultValue;
@@ -119,11 +149,18 @@ export class Expression {
},
};
return this.getParameterValue(parameterValue, runData, runIndex, itemIndex, node.name, connectionInputData, mode, additionalKeys) as boolean | number | string | undefined;
return this.getParameterValue(
parameterValue,
runData,
runIndex,
itemIndex,
node.name,
connectionInputData,
mode,
additionalKeys,
) as boolean | number | string | undefined;
}
/**
* Resolves value of complex parameter. But does not work for workflow-data.
*
@@ -133,7 +170,19 @@ export class Expression {
* @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined)}
* @memberof Workflow
*/
getComplexParameterValue(node: INode, parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], mode: WorkflowExecuteMode, additionalKeys: IWorkflowDataProxyAdditionalKeys, defaultValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined = undefined, selfData = {}): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined {
getComplexParameterValue(
node: INode,
parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[],
mode: WorkflowExecuteMode,
additionalKeys: IWorkflowDataProxyAdditionalKeys,
defaultValue:
| NodeParameterValue
| INodeParameters
| NodeParameterValue[]
| INodeParameters[]
| undefined = undefined,
selfData = {},
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined {
if (parameterValue === undefined) {
// Value is not set so return the default
return defaultValue;
@@ -150,14 +199,34 @@ export class Expression {
};
// Resolve the "outer" main values
const returnData = this.getParameterValue(parameterValue, runData, runIndex, itemIndex, node.name, connectionInputData, mode, additionalKeys, false, selfData);
const returnData = this.getParameterValue(
parameterValue,
runData,
runIndex,
itemIndex,
node.name,
connectionInputData,
mode,
additionalKeys,
false,
selfData,
);
// Resolve the "inner" values
return this.getParameterValue(returnData, runData, runIndex, itemIndex, node.name, connectionInputData, mode, additionalKeys, false, selfData);
return this.getParameterValue(
returnData,
runData,
runIndex,
itemIndex,
node.name,
connectionInputData,
mode,
additionalKeys,
false,
selfData,
);
}
/**
* Returns the resolved node parameter value. If it is an expression it will execute it and
* return the result. If the value to resolve is an array or object it will do the same
@@ -173,24 +242,74 @@ export class Expression {
* @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])}
* @memberof Workflow
*/
getParameterValue(parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], mode: WorkflowExecuteMode, additionalKeys: IWorkflowDataProxyAdditionalKeys, returnObjectAsString = false, selfData = {}): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] {
getParameterValue(
parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[],
runExecutionData: IRunExecutionData | null,
runIndex: number,
itemIndex: number,
activeNodeName: string,
connectionInputData: INodeExecutionData[],
mode: WorkflowExecuteMode,
additionalKeys: IWorkflowDataProxyAdditionalKeys,
returnObjectAsString = false,
selfData = {},
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] {
// Helper function which returns true when the parameter is a complex one or array
const isComplexParameter = (value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[]) => {
const isComplexParameter = (
value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[],
) => {
return typeof value === 'object';
};
// Helper function which resolves a parameter value depending on if it is simply or not
const resolveParameterValue = (value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], siblingParameters: INodeParameters) => {
const resolveParameterValue = (
value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[],
siblingParameters: INodeParameters,
) => {
if (isComplexParameter(value)) {
return this.getParameterValue(value, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, mode, additionalKeys, returnObjectAsString, selfData);
} else {
return this.resolveSimpleParameterValue(value as NodeParameterValue, siblingParameters, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, mode, additionalKeys, returnObjectAsString, selfData);
return this.getParameterValue(
value,
runExecutionData,
runIndex,
itemIndex,
activeNodeName,
connectionInputData,
mode,
additionalKeys,
returnObjectAsString,
selfData,
);
}
return this.resolveSimpleParameterValue(
value as NodeParameterValue,
siblingParameters,
runExecutionData,
runIndex,
itemIndex,
activeNodeName,
connectionInputData,
mode,
additionalKeys,
returnObjectAsString,
selfData,
);
};
// Check if it value is a simple one that we can get it resolved directly
if (!isComplexParameter(parameterValue)) {
return this.resolveSimpleParameterValue(parameterValue as NodeParameterValue, {}, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, mode, additionalKeys, returnObjectAsString, selfData);
return this.resolveSimpleParameterValue(
parameterValue as NodeParameterValue,
{},
runExecutionData,
runIndex,
itemIndex,
activeNodeName,
connectionInputData,
mode,
additionalKeys,
returnObjectAsString,
selfData,
);
}
// The parameter value is complex so resolve depending on type
@@ -198,28 +317,33 @@ export class Expression {
if (Array.isArray(parameterValue)) {
// Data is an array
const returnData = [];
// eslint-disable-next-line no-restricted-syntax
for (const item of parameterValue) {
returnData.push(resolveParameterValue(item, {}));
}
if (returnObjectAsString === true && typeof returnData === 'object') {
if (returnObjectAsString && typeof returnData === 'object') {
return this.convertObjectValueToString(returnData);
}
return returnData as NodeParameterValue[] | INodeParameters[];
} else if (parameterValue === null || parameterValue === undefined) {
return parameterValue;
} else {
// Data is an object
const returnData: INodeParameters = {};
for (const key of Object.keys(parameterValue)) {
returnData[key] = resolveParameterValue((parameterValue as INodeParameters)[key], parameterValue as INodeParameters);
}
if (returnObjectAsString === true && typeof returnData === 'object') {
return this.convertObjectValueToString(returnData);
}
return returnData;
}
if (parameterValue === null || parameterValue === undefined) {
return parameterValue;
}
// Data is an object
const returnData: INodeParameters = {};
// eslint-disable-next-line no-restricted-syntax
for (const key of Object.keys(parameterValue)) {
returnData[key] = resolveParameterValue(
(parameterValue as INodeParameters)[key],
parameterValue as INodeParameters,
);
}
if (returnObjectAsString && typeof returnData === 'object') {
return this.convertObjectValueToString(returnData);
}
return returnData;
}
}

View File

@@ -1,10 +1,22 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable import/no-cycle */
// eslint-disable-next-line import/no-extraneous-dependencies
// eslint-disable-next-line max-classes-per-file
import * as express from 'express';
import { Workflow } from './Workflow';
import { WorkflowHooks } from './WorkflowHooks';
import { WorkflowOperationError } from './WorkflowErrors';
import { NodeApiError, NodeOperationError } from './NodeErrors';
import * as express from 'express';
export type IAllExecuteFunctions = IExecuteFunctions | IExecuteSingleFunctions | IHookFunctions | ILoadOptionsFunctions | IPollFunctions | ITriggerFunctions | IWebhookFunctions;
export type IAllExecuteFunctions =
| IExecuteFunctions
| IExecuteSingleFunctions
| IHookFunctions
| ILoadOptionsFunctions
| IPollFunctions
| ITriggerFunctions
| IWebhookFunctions;
export interface IBinaryData {
[key: string]: string | undefined;
@@ -43,8 +55,11 @@ export interface IGetCredentials {
export abstract class ICredentials {
name: string;
type: string;
data: string | undefined;
nodesAccess: ICredentialNodeAccess[];
constructor(name: string, type: string, nodesAccess: ICredentialNodeAccess[], data?: string) {
@@ -55,10 +70,15 @@ export abstract class ICredentials {
}
abstract getData(encryptionKey: string, nodeType?: string): ICredentialDataDecryptedObject;
abstract getDataKey(key: string, encryptionKey: string, nodeType?: string): CredentialInformation;
abstract getDataToSave(): ICredentialsEncrypted;
abstract hasNodeAccess(nodeType: string): boolean;
abstract setData(data: ICredentialDataDecryptedObject, encryptionKey: string): void;
abstract setDataKey(key: string, data: CredentialInformation, encryptionKey: string): void;
}
@@ -101,8 +121,20 @@ export abstract class ICredentialsHelper {
}
abstract getCredentials(name: string, type: string): Promise<ICredentials>;
abstract getDecrypted(name: string, type: string, mode: WorkflowExecuteMode, raw?: boolean, expressionResolveValues?: ICredentialsExpressionResolveValues): Promise<ICredentialDataDecryptedObject>;
abstract updateCredentials(name: string, type: string, data: ICredentialDataDecryptedObject): Promise<void>;
abstract getDecrypted(
name: string,
type: string,
mode: WorkflowExecuteMode,
raw?: boolean,
expressionResolveValues?: ICredentialsExpressionResolveValues,
): Promise<ICredentialDataDecryptedObject>;
abstract updateCredentials(
name: string,
type: string,
data: ICredentialDataDecryptedObject,
): Promise<void>;
}
export interface ICredentialType {
@@ -116,7 +148,7 @@ export interface ICredentialType {
export interface ICredentialTypes {
credentialTypes?: {
[key: string]: ICredentialType
[key: string]: ICredentialType;
};
init(credentialTypes?: { [key: string]: ICredentialType }): Promise<void>;
getAll(): ICredentialType[];
@@ -133,7 +165,6 @@ export interface ICredentialData {
// The encrypted credentials which the nodes can access
export type CredentialInformation = string | number | boolean | IDataObject;
// The encrypted credentials which the nodes can access
export interface ICredentialDataDecryptedObject {
[key: string]: CredentialInformation;
@@ -159,92 +190,150 @@ export interface IDataObject {
[key: string]: GenericValue | IDataObject | GenericValue[] | IDataObject[];
}
export interface IGetExecutePollFunctions {
(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): IPollFunctions;
(
workflow: Workflow,
node: INode,
additionalData: IWorkflowExecuteAdditionalData,
mode: WorkflowExecuteMode,
activation: WorkflowActivateMode,
): IPollFunctions;
}
export interface IGetExecuteTriggerFunctions {
(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): ITriggerFunctions;
(
workflow: Workflow,
node: INode,
additionalData: IWorkflowExecuteAdditionalData,
mode: WorkflowExecuteMode,
activation: WorkflowActivateMode,
): ITriggerFunctions;
}
export interface IGetExecuteFunctions {
(workflow: Workflow, runExecutionData: IRunExecutionData, runIndex: number, connectionInputData: INodeExecutionData[], inputData: ITaskDataConnections, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): IExecuteFunctions;
(
workflow: Workflow,
runExecutionData: IRunExecutionData,
runIndex: number,
connectionInputData: INodeExecutionData[],
inputData: ITaskDataConnections,
node: INode,
additionalData: IWorkflowExecuteAdditionalData,
mode: WorkflowExecuteMode,
): IExecuteFunctions;
}
export interface IGetExecuteSingleFunctions {
(workflow: Workflow, runExecutionData: IRunExecutionData, runIndex: number, connectionInputData: INodeExecutionData[], inputData: ITaskDataConnections, node: INode, itemIndex: number, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): IExecuteSingleFunctions;
(
workflow: Workflow,
runExecutionData: IRunExecutionData,
runIndex: number,
connectionInputData: INodeExecutionData[],
inputData: ITaskDataConnections,
node: INode,
itemIndex: number,
additionalData: IWorkflowExecuteAdditionalData,
mode: WorkflowExecuteMode,
): IExecuteSingleFunctions;
}
export interface IGetExecuteHookFunctions {
(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode, isTest?: boolean, webhookData?: IWebhookData): IHookFunctions;
(
workflow: Workflow,
node: INode,
additionalData: IWorkflowExecuteAdditionalData,
mode: WorkflowExecuteMode,
activation: WorkflowActivateMode,
isTest?: boolean,
webhookData?: IWebhookData,
): IHookFunctions;
}
export interface IGetExecuteWebhookFunctions {
(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, webhookData: IWebhookData): IWebhookFunctions;
(
workflow: Workflow,
node: INode,
additionalData: IWorkflowExecuteAdditionalData,
mode: WorkflowExecuteMode,
webhookData: IWebhookData,
): IWebhookFunctions;
}
export interface IExecuteData {
data: ITaskDataConnections;
node: INode;
}
export type IContextObject = {
[key: string]: any; // tslint:disable-line:no-any
[key: string]: any;
};
export interface IExecuteContextData {
// Keys are: "flow" | "node:<NODE_NAME>"
[key: string]: IContextObject;
}
export interface IExecuteFunctions {
continueOnFail(): boolean;
evaluateExpression(expression: string, itemIndex: number): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any>; // tslint:disable-line:no-any
evaluateExpression(
expression: string,
itemIndex: number,
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
executeWorkflow(
workflowInfo: IExecuteWorkflowInfo,
inputData?: INodeExecutionData[],
): Promise<any>;
getContext(type: string): IContextObject;
getCredentials(type: string, itemIndex?: number): Promise<ICredentialDataDecryptedObject | undefined>;
getCredentials(
type: string,
itemIndex?: number,
): Promise<ICredentialDataDecryptedObject | undefined>;
getInputData(inputIndex?: number, inputName?: string): INodeExecutionData[];
getMode(): WorkflowExecuteMode;
getNode(): INode;
getNodeParameter(parameterName: string, itemIndex: number, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
getNodeParameter(
parameterName: string,
itemIndex: number,
fallbackValue?: any,
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object;
getWorkflowDataProxy(itemIndex: number): IWorkflowDataProxyData;
getWorkflowStaticData(type: string): IDataObject;
getRestApiUrl(): string;
getTimezone(): string;
getWorkflow(): IWorkflowMetadata;
prepareOutputData(outputData: INodeExecutionData[], outputIndex?: number): Promise<INodeExecutionData[][]>;
prepareOutputData(
outputData: INodeExecutionData[],
outputIndex?: number,
): Promise<INodeExecutionData[][]>;
putExecutionToWait(waitTill: Date): Promise<void>;
sendMessageToUI(message: any): void; // tslint:disable-line:no-any
helpers: {
[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
[key: string]: (...args: any[]) => any;
};
}
export interface IExecuteSingleFunctions {
continueOnFail(): boolean;
evaluateExpression(expression: string, itemIndex: number | undefined): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
evaluateExpression(
expression: string,
itemIndex: number | undefined,
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
getContext(type: string): IContextObject;
getCredentials(type: string): Promise<ICredentialDataDecryptedObject | undefined>;
getInputData(inputIndex?: number, inputName?: string): INodeExecutionData;
getMode(): WorkflowExecuteMode;
getNode(): INode;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
getNodeParameter(
parameterName: string,
fallbackValue?: any,
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object;
getRestApiUrl(): string;
getTimezone(): string;
getWorkflow(): IWorkflowMetadata;
getWorkflowDataProxy(): IWorkflowDataProxyData;
getWorkflowStaticData(type: string): IDataObject;
helpers: {
[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
[key: string]: (...args: any[]) => any;
};
}
@@ -256,13 +345,24 @@ export interface IExecuteWorkflowInfo {
export interface ILoadOptionsFunctions {
getCredentials(type: string): Promise<ICredentialDataDecryptedObject | undefined>;
getNode(): INode;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
getCurrentNodeParameter(parameterName: string): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object | undefined;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object;
getCurrentNodeParameter(
parameterName: string,
):
| NodeParameterValue
| INodeParameters
| NodeParameterValue[]
| INodeParameters[]
| object
| undefined;
getCurrentNodeParameters(): INodeParameters | undefined;
getTimezone(): string;
getRestApiUrl(): string;
helpers: {
[key: string]: ((...args: any[]) => any) | undefined; //tslint:disable-line:no-any
[key: string]: ((...args: any[]) => any) | undefined;
};
}
@@ -272,14 +372,17 @@ export interface IHookFunctions {
getActivationMode(): WorkflowActivateMode;
getNode(): INode;
getNodeWebhookUrl: (name: string) => string | undefined;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
getNodeParameter(
parameterName: string,
fallbackValue?: any,
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object;
getTimezone(): string;
getWebhookDescription(name: string): IWebhookDescription | undefined;
getWebhookName(): string;
getWorkflow(): IWorkflowMetadata;
getWorkflowStaticData(type: string): IDataObject;
helpers: {
[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
[key: string]: (...args: any[]) => any;
};
}
@@ -289,13 +392,16 @@ export interface IPollFunctions {
getMode(): WorkflowExecuteMode;
getActivationMode(): WorkflowActivateMode;
getNode(): INode;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
getNodeParameter(
parameterName: string,
fallbackValue?: any,
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object;
getRestApiUrl(): string;
getTimezone(): string;
getWorkflow(): IWorkflowMetadata;
getWorkflowStaticData(type: string): IDataObject;
helpers: {
[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
[key: string]: (...args: any[]) => any;
};
}
@@ -305,13 +411,16 @@ export interface ITriggerFunctions {
getMode(): WorkflowExecuteMode;
getActivationMode(): WorkflowActivateMode;
getNode(): INode;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
getNodeParameter(
parameterName: string,
fallbackValue?: any,
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object;
getRestApiUrl(): string;
getTimezone(): string;
getWorkflow(): IWorkflowMetadata;
getWorkflowStaticData(type: string): IDataObject;
helpers: {
[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
[key: string]: (...args: any[]) => any;
};
}
@@ -321,7 +430,10 @@ export interface IWebhookFunctions {
getHeaderData(): object;
getMode(): WorkflowExecuteMode;
getNode(): INode;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
getNodeParameter(
parameterName: string,
fallbackValue?: any,
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object;
getNodeWebhookUrl: (name: string) => string | undefined;
getParamsData(): object;
getQueryData(): object;
@@ -331,9 +443,12 @@ export interface IWebhookFunctions {
getWebhookName(): string;
getWorkflowStaticData(type: string): IDataObject;
getWorkflow(): IWorkflowMetadata;
prepareOutputData(outputData: INodeExecutionData[], outputIndex?: number): Promise<INodeExecutionData[][]>;
prepareOutputData(
outputData: INodeExecutionData[],
outputIndex?: number,
): Promise<INodeExecutionData[][]>;
helpers: {
[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
[key: string]: (...args: any[]) => any;
};
}
@@ -360,18 +475,15 @@ export interface INode {
webhookId?: string;
}
export interface INodes {
[key: string]: INode;
}
export interface IObservableObject {
[key: string]: any; // tslint:disable-line:no-any
[key: string]: any;
__dataChanged: boolean;
}
export interface IBinaryKeyData {
[key: string]: IBinaryData;
}
@@ -385,7 +497,6 @@ export interface INodeExecutionData {
binary?: IBinaryKeyData;
}
export interface INodeExecuteFunctions {
getExecutePollFunctions: IGetExecutePollFunctions;
getExecuteTriggerFunctions: IGetExecuteTriggerFunctions;
@@ -395,7 +506,6 @@ export interface INodeExecuteFunctions {
getExecuteWebhookFunctions: IGetExecuteWebhookFunctions;
}
// The values a node property can have
export type NodeParameterValue = string | number | boolean | undefined | null;
@@ -404,25 +514,37 @@ export interface INodeParameters {
[key: string]: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
}
export type NodePropertyTypes = 'boolean' | 'collection' | 'color' | 'dateTime' | 'fixedCollection' | 'hidden' | 'json' | 'notice' | 'multiOptions' | 'number' | 'options' | 'string';
export type NodePropertyTypes =
| 'boolean'
| 'collection'
| 'color'
| 'dateTime'
| 'fixedCollection'
| 'hidden'
| 'json'
| 'notice'
| 'multiOptions'
| 'number'
| 'options'
| 'string';
export type EditorTypes = 'code';
export interface INodePropertyTypeOptions {
alwaysOpenEditWindow?: boolean; // Supported by: string
editor?: EditorTypes; // Supported by: string
loadOptionsDependsOn?: string[]; // Supported by: options
loadOptionsMethod?: string; // Supported by: options
maxValue?: number; // Supported by: number
minValue?: number; // Supported by: number
multipleValues?: boolean; // Supported by: <All>
multipleValueButtonText?: string; // Supported when "multipleValues" set to true
numberPrecision?: number; // Supported by: number
numberStepSize?: number; // Supported by: number
password?: boolean; // Supported by: string
rows?: number; // Supported by: string
showAlpha?: boolean; // Supported by: color
sortable?: boolean; // Supported when "multipleValues" set to true
editor?: EditorTypes; // Supported by: string
loadOptionsDependsOn?: string[]; // Supported by: options
loadOptionsMethod?: string; // Supported by: options
maxValue?: number; // Supported by: number
minValue?: number; // Supported by: number
multipleValues?: boolean; // Supported by: <All>
multipleValueButtonText?: string; // Supported when "multipleValues" set to true
numberPrecision?: number; // Supported by: number
numberStepSize?: number; // Supported by: number
password?: boolean; // Supported by: string
rows?: number; // Supported by: string
showAlpha?: boolean; // Supported by: color
sortable?: boolean; // Supported when "multipleValues" set to true
[key: string]: boolean | number | string | EditorTypes | undefined | string[];
}
@@ -435,7 +557,6 @@ export interface IDisplayOptions {
};
}
export interface INodeProperties {
displayName: string;
name: string;
@@ -492,7 +613,7 @@ export interface INodeType {
methods?: {
loadOptions?: {
[key: string]: (this: ILoadOptionsFunctions) => Promise<INodePropertyOptions[]>;
}
};
};
webhookMethods?: {
[key: string]: IWebhookSetupMethods;
@@ -501,7 +622,6 @@ export interface INodeType {
export type WebhookSetupMethodNames = 'checkExists' | 'create' | 'delete';
export interface IWebhookSetupMethods {
[key: string]: ((this: IHookFunctions) => Promise<boolean>) | undefined;
checkExists?: (this: IHookFunctions) => Promise<boolean>;
@@ -509,7 +629,6 @@ export interface IWebhookSetupMethods {
delete?: (this: IHookFunctions) => Promise<boolean>;
}
export interface INodeCredentialDescription {
name: string;
required?: boolean;
@@ -596,17 +715,17 @@ export interface IWebhookDescription {
}
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
$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
$position: any; // tslint:disable-line:no-any
$workflow: any; // tslint:disable-line:no-any
$binary: any;
$data: any;
$env: any;
$evaluateExpression: any;
$item: any;
$items: any;
$json: any;
$node: any;
$parameter: any;
$position: any;
$workflow: any;
}
export interface IWorkflowDataProxyAdditionalKeys {
@@ -623,7 +742,7 @@ export type WebhookHttpMethod = 'GET' | 'POST' | 'HEAD' | 'OPTIONS';
export interface IWebhookResponseData {
workflowData?: INodeExecutionData[][];
webhookResponse?: any; // tslint:disable-line:no-any
webhookResponse?: any;
noWebhookResponse?: boolean;
}
@@ -637,7 +756,6 @@ export interface INodeTypes {
getByName(nodeType: string): INodeType | undefined;
}
export interface INodeTypeData {
[key: string]: {
type: INodeType;
@@ -654,7 +772,6 @@ export interface IRun {
stoppedAt?: Date;
}
// Contains all the data which is needed to execute a workflow and so also to
// start restart it again after it did fail.
// The RunData, ExecuteData and WaitForExecution contain often the same data.
@@ -676,13 +793,11 @@ export interface IRunExecutionData {
waitTill?: Date;
}
export interface IRunData {
// node-name: result-data
[key: string]: ITaskData[];
}
// The data that gets returned when a node runs
export interface ITaskData {
startTime: number;
@@ -691,7 +806,6 @@ export interface ITaskData {
error?: ExecutionError;
}
// The data for al the different kind of connectons (like main) and all the indexes
export interface ITaskDataConnections {
// Key for each input type and because there can be multiple inputs of the same type it is an array
@@ -700,20 +814,17 @@ export interface ITaskDataConnections {
[key: string]: Array<INodeExecutionData[] | null>;
}
// Keeps data while workflow gets executed and allows when provided to restart execution
export interface IWaitingForExecution {
// Node name
[key: string]: {
// Run index
[key: number]: ITaskDataConnections
[key: number]: ITaskDataConnections;
};
}
export interface IWorkflowBase {
id?: number | string | any; // tslint:disable-line:no-any
id?: number | string | any;
name: string;
active: boolean;
createdAt: Date;
@@ -732,26 +843,34 @@ export interface IWorkflowCredentials {
};
}
export interface IWorkflowExecuteHooks {
[key: string]: Array<((...args: any[]) => Promise<void>)> | undefined; // tslint:disable-line:no-any
nodeExecuteAfter?: Array<((nodeName: string, data: ITaskData, executionData: IRunExecutionData) => Promise<void>)>;
nodeExecuteBefore?: Array<((nodeName: string) => Promise<void>)>;
workflowExecuteAfter?: Array<((data: IRun, newStaticData: IDataObject) => Promise<void>)>;
workflowExecuteBefore?: Array<((workflow: Workflow, data: IRunExecutionData) => Promise<void>)>;
[key: string]: Array<(...args: any[]) => Promise<void>> | undefined;
nodeExecuteAfter?: Array<
(nodeName: string, data: ITaskData, executionData: IRunExecutionData) => Promise<void>
>;
nodeExecuteBefore?: Array<(nodeName: string) => Promise<void>>;
workflowExecuteAfter?: Array<(data: IRun, newStaticData: IDataObject) => Promise<void>>;
workflowExecuteBefore?: Array<(workflow: Workflow, data: IRunExecutionData) => Promise<void>>;
}
export interface IWorkflowExecuteAdditionalData {
credentialsHelper: ICredentialsHelper;
encryptionKey: string;
executeWorkflow: (workflowInfo: IExecuteWorkflowInfo, additionalData: IWorkflowExecuteAdditionalData, inputData?: INodeExecutionData[], parentExecutionId?: string, loadedWorkflowData?: IWorkflowBase, loadedRunData?: any) => Promise<any>; // tslint:disable-line:no-any
executeWorkflow: (
workflowInfo: IExecuteWorkflowInfo,
additionalData: IWorkflowExecuteAdditionalData,
inputData?: INodeExecutionData[],
parentExecutionId?: string,
loadedWorkflowData?: IWorkflowBase,
loadedRunData?: any,
) => Promise<any>;
// hooks?: IWorkflowExecuteHooks;
executionId?: string;
hooks?: WorkflowHooks;
httpResponse?: express.Response;
httpRequest?: express.Request;
restApiUrl: string;
sendMessageToUI?: (source: string, message: any) => void; // tslint:disable-line:no-any
sendMessageToUI?: (source: string, message: any) => void;
timezone: string;
webhookBaseUrl: string;
webhookWaitingBaseUrl: string;
@@ -760,7 +879,15 @@ export interface IWorkflowExecuteAdditionalData {
executionTimeoutTimestamp?: number;
}
export type WorkflowExecuteMode = 'cli' | 'error' | 'integrated' | 'internal' | 'manual' | 'retry' | 'trigger' | 'webhook';
export type WorkflowExecuteMode =
| 'cli'
| 'error'
| 'integrated'
| 'internal'
| 'manual'
| 'retry'
| 'trigger'
| 'webhook';
export type WorkflowActivateMode = 'init' | 'create' | 'update' | 'activate' | 'manual';
export interface IWorkflowHooksOptionalParameters {
@@ -790,7 +917,7 @@ export interface IStatusCodeMessages {
export type CodexData = {
categories?: string[];
subcategories?: {[category: string]: string[]};
subcategories?: { [category: string]: string[] };
alias?: string[];
};

View File

@@ -1,8 +1,6 @@
import {
ILogger,
LogTypes,
} from './Interfaces';
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
// eslint-disable-next-line import/no-cycle
import { ILogger, LogTypes } from './Interfaces';
let logger: ILogger | undefined;

View File

@@ -1,5 +1,13 @@
import { INode, IStatusCodeMessages, JsonObject} from '.';
/* eslint-disable @typescript-eslint/no-shadow */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable no-param-reassign */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
// eslint-disable-next-line max-classes-per-file
import { parseString } from 'xml2js';
// eslint-disable-next-line import/no-cycle
import { INode, IStatusCodeMessages, JsonObject } from '.';
/**
* Top-level properties where an error message can be found in an API response.
@@ -33,7 +41,14 @@ const ERROR_MESSAGE_PROPERTIES = [
/**
* Top-level properties where an HTTP error code can be found in an API response.
*/
const ERROR_STATUS_PROPERTIES = ['statusCode', 'status', 'code', 'status_code', 'errorCode', 'error_code'];
const ERROR_STATUS_PROPERTIES = [
'statusCode',
'status',
'code',
'status_code',
'errorCode',
'error_code',
];
/**
* Properties where a nested object can be found in an API response.
@@ -46,8 +61,11 @@ const ERROR_NESTING_PROPERTIES = ['error', 'err', 'response', 'body', 'data'];
*/
abstract class NodeError extends Error {
description: string | null | undefined;
cause: Error | JsonObject;
node: INode;
timestamp: number;
constructor(node: INode, error: Error | JsonObject) {
@@ -95,13 +113,17 @@ abstract class NodeError extends Error {
potentialKeys: string[],
traversalKeys: string[] = [],
): string | null {
for(const key of potentialKeys) {
// eslint-disable-next-line no-restricted-syntax
for (const key of potentialKeys) {
if (error[key]) {
if (typeof error[key] === 'string') return error[key] as string;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (typeof error[key] === 'number') return error[key]!.toString();
if (Array.isArray(error[key])) {
// @ts-ignore
const resolvedErrors: string[] = error[key].map((error) => {
const resolvedErrors: string[] = error[key]
// @ts-ignore
.map((error) => {
if (typeof error === 'string') return error;
if (typeof error === 'number') return error.toString();
if (this.isTraversableObject(error)) {
@@ -125,6 +147,7 @@ abstract class NodeError extends Error {
}
}
// eslint-disable-next-line no-restricted-syntax
for (const key of traversalKeys) {
if (this.isTraversableObject(error[key])) {
const property = this.findProperty(error[key] as JsonObject, potentialKeys, traversalKeys);
@@ -140,18 +163,24 @@ abstract class NodeError extends Error {
/**
* Check if a value is an object with at least one key, i.e. it can be traversed.
*/
protected isTraversableObject(value: any): value is JsonObject { // tslint:disable-line:no-any
return value && typeof value === 'object' && !Array.isArray(value) && !!Object.keys(value).length;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected isTraversableObject(value: any): value is JsonObject {
return (
value && typeof value === 'object' && !Array.isArray(value) && !!Object.keys(value).length
);
}
/**
* Remove circular references from objects.
*/
protected removeCircularRefs(obj: JsonObject, seen = new Set()) {
protected removeCircularRefs(obj: JsonObject, seen = new Set()) {
seen.add(obj);
Object.entries(obj).forEach(([key, value]) => {
if (this.isTraversableObject(value)) {
seen.has(value) ? obj[key] = { circularReference: true } : this.removeCircularRefs(value, seen);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
seen.has(value)
? (obj[key] = { circularReference: true })
: this.removeCircularRefs(value, seen);
return;
}
if (Array.isArray(value)) {
@@ -173,7 +202,6 @@ abstract class NodeError extends Error {
* Class for instantiating an operational error, e.g. an invalid credentials error.
*/
export class NodeOperationError extends NodeError {
constructor(node: INode, error: Error | string) {
if (typeof error === 'string') {
error = new Error(error);
@@ -211,10 +239,16 @@ export class NodeApiError extends NodeError {
constructor(
node: INode,
error: JsonObject,
{ message, description, httpCode, parseXml }: { message?: string, description?: string, httpCode?: string, parseXml?: boolean } = {},
{
message,
description,
httpCode,
parseXml,
}: { message?: string; description?: string; httpCode?: string; parseXml?: boolean } = {},
) {
super(node, error);
if (error.error) { // only for request library error
if (error.error) {
// only for request library error
this.removeCircularRefs(error.error as JsonObject);
}
if (message) {
@@ -236,11 +270,17 @@ export class NodeApiError extends NodeError {
}
private setDescriptionFromXml(xml: string) {
// eslint-disable-next-line @typescript-eslint/naming-convention
parseString(xml, { explicitArray: false }, (_, result) => {
if (!result) return;
const topLevelKey = Object.keys(result)[0];
this.description = this.findProperty(result[topLevelKey], ERROR_MESSAGE_PROPERTIES, ['Error'].concat(ERROR_NESTING_PROPERTIES));
this.description = this.findProperty(
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
result[topLevelKey],
ERROR_MESSAGE_PROPERTIES,
['Error'].concat(ERROR_NESTING_PROPERTIES),
);
});
}

View File

@@ -1,3 +1,17 @@
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/no-use-before-define */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
/* eslint-disable no-param-reassign */
/* eslint-disable no-continue */
/* eslint-disable prefer-spread */
/* eslint-disable no-restricted-syntax */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable import/no-cycle */
// eslint-disable-next-line import/no-extraneous-dependencies
import { get, isEqual } from 'lodash';
import {
IContextObject,
INode,
@@ -17,13 +31,7 @@ import {
WebhookHttpMethod,
} from './Interfaces';
import {
Workflow
} from './Workflow';
import { get, isEqual } from 'lodash';
import { Workflow } from './Workflow';
/**
* Gets special parameters which should be added to nodeTypes depending
@@ -99,12 +107,7 @@ export function getSpecialNodeParameters(nodeType: INodeType) {
},
displayOptions: {
hide: {
mode: [
'custom',
'everyHour',
'everyMinute',
'everyX',
],
mode: ['custom', 'everyHour', 'everyMinute', 'everyX'],
},
},
default: 14,
@@ -120,11 +123,7 @@ export function getSpecialNodeParameters(nodeType: INodeType) {
},
displayOptions: {
hide: {
mode: [
'custom',
'everyMinute',
'everyX',
],
mode: ['custom', 'everyMinute', 'everyX'],
},
},
default: 0,
@@ -136,9 +135,7 @@ export function getSpecialNodeParameters(nodeType: INodeType) {
type: 'number',
displayOptions: {
show: {
mode: [
'everyMonth',
],
mode: ['everyMonth'],
},
},
typeOptions: {
@@ -154,9 +151,7 @@ export function getSpecialNodeParameters(nodeType: INodeType) {
type: 'options',
displayOptions: {
show: {
mode: [
'everyWeek',
],
mode: ['everyWeek'],
},
},
options: [
@@ -198,13 +193,12 @@ export function getSpecialNodeParameters(nodeType: INodeType) {
type: 'string',
displayOptions: {
show: {
mode: [
'custom',
],
mode: ['custom'],
},
},
default: '* * * * * *',
description: 'Use custom cron expression. Values and ranges as follows:<ul><li>Seconds: 0-59</li><li>Minutes: 0 - 59</li><li>Hours: 0 - 23</li><li>Day of Month: 1 - 31</li><li>Months: 0 - 11 (Jan - Dec)</li><li>Day of Week: 0 - 6 (Sun - Sat)</li></ul>',
description:
'Use custom cron expression. Values and ranges as follows:<ul><li>Seconds: 0-59</li><li>Minutes: 0 - 59</li><li>Hours: 0 - 23</li><li>Day of Month: 1 - 31</li><li>Months: 0 - 11 (Jan - Dec)</li><li>Day of Week: 0 - 6 (Sun - Sat)</li></ul>',
},
{
displayName: 'Value',
@@ -216,9 +210,7 @@ export function getSpecialNodeParameters(nodeType: INodeType) {
},
displayOptions: {
show: {
mode: [
'everyX',
],
mode: ['everyX'],
},
},
default: 2,
@@ -230,9 +222,7 @@ export function getSpecialNodeParameters(nodeType: INodeType) {
type: 'options',
displayOptions: {
show: {
mode: [
'everyX',
],
mode: ['everyX'],
},
},
options: [
@@ -258,7 +248,6 @@ export function getSpecialNodeParameters(nodeType: INodeType) {
return [];
}
/**
* Returns if the parameter should be displayed or not
*
@@ -269,7 +258,11 @@ export function getSpecialNodeParameters(nodeType: INodeType) {
* @param {INodeParameters} [nodeValuesRoot] The root node-parameter-data
* @returns
*/
export function displayParameter(nodeValues: INodeParameters, parameter: INodeProperties | INodeCredentialDescription, nodeValuesRoot?: INodeParameters) {
export function displayParameter(
nodeValues: INodeParameters,
parameter: INodeProperties | INodeCredentialDescription,
nodeValuesRoot?: INodeParameters,
) {
if (!parameter.displayOptions) {
return true;
}
@@ -277,7 +270,8 @@ export function displayParameter(nodeValues: INodeParameters, parameter: INodePr
nodeValuesRoot = nodeValuesRoot || nodeValues;
let value;
const values: any[] = []; // tslint:disable-line:no-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const values: any[] = [];
if (parameter.displayOptions.show) {
// All the defined rules have to match to display parameter
for (const propertyName of Object.keys(parameter.displayOptions.show)) {
@@ -296,11 +290,14 @@ export function displayParameter(nodeValues: INodeParameters, parameter: INodePr
values.push.apply(values, value);
}
if (values.some(v => (typeof v) === 'string' && (v as string).charAt(0) === '=')) {
if (values.some((v) => typeof v === 'string' && v.charAt(0) === '=')) {
return true;
}
if (values.length === 0 || !parameter.displayOptions.show[propertyName].some(v => values.includes(v))) {
if (
values.length === 0 ||
!parameter.displayOptions.show[propertyName].some((v) => values.includes(v))
) {
return false;
}
}
@@ -324,7 +321,10 @@ export function displayParameter(nodeValues: INodeParameters, parameter: INodePr
values.push.apply(values, value);
}
if (values.length !== 0 && parameter.displayOptions.hide[propertyName].some(v => values.includes(v))) {
if (
values.length !== 0 &&
parameter.displayOptions.hide[propertyName].some((v) => values.includes(v))
) {
return false;
}
}
@@ -333,7 +333,6 @@ export function displayParameter(nodeValues: INodeParameters, parameter: INodePr
return true;
}
/**
* Returns if the given parameter should be displayed or not considering the path
* to the properties
@@ -345,28 +344,25 @@ export function displayParameter(nodeValues: INodeParameters, parameter: INodePr
* @param {string} path The path to the property
* @returns
*/
export function displayParameterPath(nodeValues: INodeParameters, parameter: INodeProperties | INodeCredentialDescription, path: string) {
export function displayParameterPath(
nodeValues: INodeParameters,
parameter: INodeProperties | INodeCredentialDescription,
path: string,
) {
let resolvedNodeValues = nodeValues;
if (path !== '') {
resolvedNodeValues = get(
nodeValues,
path,
) as INodeParameters;
resolvedNodeValues = get(nodeValues, path) as INodeParameters;
}
// Get the root parameter data
let nodeValuesRoot = nodeValues;
if (path && path.split('.').indexOf('parameters') === 0) {
nodeValuesRoot = get(
nodeValues,
'parameters',
) as INodeParameters;
nodeValuesRoot = get(nodeValues, 'parameters') as INodeParameters;
}
return displayParameter(resolvedNodeValues, parameter, nodeValuesRoot);
}
/**
* Returns the context data
*
@@ -376,7 +372,11 @@ export function displayParameterPath(nodeValues: INodeParameters, parameter: INo
* @param {INode} [node] If type "node" is set the node to return the context of has to be supplied
* @returns {IContextObject}
*/
export function getContext(runExecutionData: IRunExecutionData, type: string, node?: INode): IContextObject {
export function getContext(
runExecutionData: IRunExecutionData,
type: string,
node?: INode,
): IContextObject {
if (runExecutionData.executionData === undefined) {
// TODO: Should not happen leave it for test now
throw new Error('The "executionData" is not initialized!');
@@ -395,13 +395,13 @@ export function getContext(runExecutionData: IRunExecutionData, type: string, no
}
if (runExecutionData.executionData.contextData[key] === undefined) {
// eslint-disable-next-line no-param-reassign
runExecutionData.executionData.contextData[key] = {};
}
return runExecutionData.executionData.contextData[key];
}
/**
* Returns which parameters are dependent on which
*
@@ -409,7 +409,9 @@ export function getContext(runExecutionData: IRunExecutionData, type: string, no
* @param {INodeProperties[]} nodePropertiesArray
* @returns {IParameterDependencies}
*/
export function getParamterDependencies(nodePropertiesArray: INodeProperties[]): IParameterDependencies {
export function getParamterDependencies(
nodePropertiesArray: INodeProperties[],
): IParameterDependencies {
const dependencies: IParameterDependencies = {};
let displayRule: string;
@@ -436,7 +438,6 @@ export function getParamterDependencies(nodePropertiesArray: INodeProperties[]):
return dependencies;
}
/**
* Returns in which order the parameters should be resolved
* to have the parameters available they depend on
@@ -446,7 +447,10 @@ export function getParamterDependencies(nodePropertiesArray: INodeProperties[]):
* @param {IParameterDependencies} parameterDependencies
* @returns {number[]}
*/
export function getParameterResolveOrder(nodePropertiesArray: INodeProperties[], parameterDependencies: IParameterDependencies): number[] {
export function getParamterResolveOrder(
nodePropertiesArray: INodeProperties[],
parameterDependencies: IParameterDependencies,
): number[] {
const executionOrder: number[] = [];
const indexToResolve = Array.from({ length: nodePropertiesArray.length }, (v, k) => k);
const resolvedParamters: string[] = [];
@@ -457,7 +461,7 @@ export function getParameterResolveOrder(nodePropertiesArray: INodeProperties[],
let lastIndexLength = indexToResolve.length;
let lastIndexReduction = -1;
let iterations = 0 ;
let iterations = 0;
while (indexToResolve.length !== 0) {
iterations += 1;
@@ -495,7 +499,9 @@ export function getParameterResolveOrder(nodePropertiesArray: INodeProperties[],
}
if (iterations > lastIndexReduction + nodePropertiesArray.length) {
throw new Error('Could not resolve parameter depenencies. Max iterations reached! Hint: If `displayOptions` are specified in any child parameter of a parent `collection` or `fixedCollection`, remove the `displayOptions` from the child parameter.');
throw new Error(
'Could not resolve parameter depenencies. Max iterations reached! Hint: If `displayOptions` are specified in any child parameter of a parent `collection` or `fixedCollection`, remove the `displayOptions` from the child parameter.',
);
}
lastIndexLength = indexToResolve.length;
}
@@ -503,7 +509,6 @@ export function getParameterResolveOrder(nodePropertiesArray: INodeProperties[],
return executionOrder;
}
/**
* Returns the node parameter values. Depending on the settings it either just returns the none
* default values or it applies all the default values.
@@ -518,7 +523,17 @@ export function getParameterResolveOrder(nodePropertiesArray: INodeProperties[],
* @param {INodeParameters} [nodeValuesRoot] The root node-parameter-data
* @returns {(INodeParameters | null)}
*/
export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeValues: INodeParameters, returnDefaults: boolean, returnNoneDisplayed: boolean, onlySimpleTypes = false, dataIsResolved = false, nodeValuesRoot?: INodeParameters, parentType?: string, parameterDependencies?: IParameterDependencies): INodeParameters | null {
export function getNodeParameters(
nodePropertiesArray: INodeProperties[],
nodeValues: INodeParameters,
returnDefaults: boolean,
returnNoneDisplayed: boolean,
onlySimpleTypes = false,
dataIsResolved = false,
nodeValuesRoot?: INodeParameters,
parentType?: string,
parameterDependencies?: IParameterDependencies,
): INodeParameters | null {
if (parameterDependencies === undefined) {
parameterDependencies = getParamterDependencies(nodePropertiesArray);
}
@@ -541,27 +556,43 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
const nodeParametersFull: INodeParameters = {};
let nodeValuesDisplayCheck = nodeParametersFull;
if (dataIsResolved !== true && returnNoneDisplayed === false) {
nodeValuesDisplayCheck = getNodeParameters(nodePropertiesArray, nodeValues, true, true, true, true, nodeValuesRoot, parentType, parameterDependencies) as INodeParameters;
if (!dataIsResolved && !returnNoneDisplayed) {
nodeValuesDisplayCheck = getNodeParameters(
nodePropertiesArray,
nodeValues,
true,
true,
true,
true,
nodeValuesRoot,
parentType,
parameterDependencies,
) as INodeParameters;
}
nodeValuesRoot = nodeValuesRoot || nodeValuesDisplayCheck;
// Go through the parameters in order of their dependencies
const parameterItterationOrderIndex = getParameterResolveOrder(nodePropertiesArray, parameterDependencies);
const parameterItterationOrderIndex = getParamterResolveOrder(
nodePropertiesArray,
parameterDependencies,
);
for (const parameterIndex of parameterItterationOrderIndex) {
const nodeProperties = nodePropertiesArray[parameterIndex];
if (nodeValues[nodeProperties.name] === undefined && (returnDefaults === false || parentType === 'collection')) {
if (
nodeValues[nodeProperties.name] === undefined &&
(!returnDefaults || parentType === 'collection')
) {
// The value is not defined so go to the next
continue;
}
if (returnNoneDisplayed === false && !displayParameter(nodeValuesDisplayCheck, nodeProperties, nodeValuesRoot)) {
if (returnNoneDisplayed === false) {
continue;
}
if (returnDefaults === false) {
if (
!returnNoneDisplayed &&
!displayParameter(nodeValuesDisplayCheck, nodeProperties, nodeValuesRoot)
) {
if (!returnNoneDisplayed || !returnDefaults) {
continue;
}
}
@@ -575,19 +606,27 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
}
}
if (returnDefaults === true) {
if (returnDefaults) {
// Set also when it has the default value
if (['boolean', 'number', 'options'].includes(nodeProperties.type)) {
// Boolean, numbers and options are special as false and 0 are valid values
// and should not be replaced with default value
nodeParameters[nodeProperties.name] = nodeValues[nodeProperties.name] !== undefined ? nodeValues[nodeProperties.name] : nodeProperties.default;
nodeParameters[nodeProperties.name] =
nodeValues[nodeProperties.name] !== undefined
? nodeValues[nodeProperties.name]
: nodeProperties.default;
} else {
nodeParameters[nodeProperties.name] = nodeValues[nodeProperties.name] || nodeProperties.default;
nodeParameters[nodeProperties.name] =
nodeValues[nodeProperties.name] || nodeProperties.default;
}
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
} else if ((nodeValues[nodeProperties.name] !== nodeProperties.default && typeof nodeValues[nodeProperties.name] !== 'object') ||
(typeof nodeValues[nodeProperties.name] === 'object' && !isEqual(nodeValues[nodeProperties.name], nodeProperties.default)) ||
(nodeValues[nodeProperties.name] !== undefined && parentType === 'collection')) {
} else if (
(nodeValues[nodeProperties.name] !== nodeProperties.default &&
typeof nodeValues[nodeProperties.name] !== 'object') ||
(typeof nodeValues[nodeProperties.name] === 'object' &&
!isEqual(nodeValues[nodeProperties.name], nodeProperties.default)) ||
(nodeValues[nodeProperties.name] !== undefined && parentType === 'collection')
) {
// Set only if it is different to the default value
nodeParameters[nodeProperties.name] = nodeValues[nodeProperties.name];
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
@@ -595,7 +634,7 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
}
}
if (onlySimpleTypes === true) {
if (onlySimpleTypes) {
// It is only supposed to resolve the simple types. So continue.
continue;
}
@@ -605,16 +644,21 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
if (nodeProperties.type === 'collection') {
// Is collection
if (nodeProperties.typeOptions !== undefined && nodeProperties.typeOptions.multipleValues === true) {
if (
nodeProperties.typeOptions !== undefined &&
nodeProperties.typeOptions.multipleValues === true
) {
// Multiple can be set so will be an array
// Return directly the values like they are
if (nodeValues[nodeProperties.name] !== undefined) {
nodeParameters[nodeProperties.name] = nodeValues[nodeProperties.name];
} else if (returnDefaults === true) {
} else if (returnDefaults) {
// Does not have values defined but defaults should be returned
if (Array.isArray(nodeProperties.default)) {
nodeParameters[nodeProperties.name] = JSON.parse(JSON.stringify(nodeProperties.default));
nodeParameters[nodeProperties.name] = JSON.parse(
JSON.stringify(nodeProperties.default),
);
} else {
// As it is probably wrong for many nodes, do we keep on returning an empty array if
// anything else than an array is set as default
@@ -622,20 +666,27 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
}
}
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
} else {
if (nodeValues[nodeProperties.name] !== undefined) {
// Has values defined so get them
const tempNodeParameters = getNodeParameters(nodeProperties.options as INodeProperties[], nodeValues[nodeProperties.name] as INodeParameters, returnDefaults, returnNoneDisplayed, false, false, nodeValuesRoot, nodeProperties.type);
} else if (nodeValues[nodeProperties.name] !== undefined) {
// Has values defined so get them
const tempNodeParameters = getNodeParameters(
nodeProperties.options as INodeProperties[],
nodeValues[nodeProperties.name] as INodeParameters,
returnDefaults,
returnNoneDisplayed,
false,
false,
nodeValuesRoot,
nodeProperties.type,
);
if (tempNodeParameters !== null) {
nodeParameters[nodeProperties.name] = tempNodeParameters;
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
}
} else if (returnDefaults === true) {
// Does not have values defined but defaults should be returned
nodeParameters[nodeProperties.name] = JSON.parse(JSON.stringify(nodeProperties.default));
if (tempNodeParameters !== null) {
nodeParameters[nodeProperties.name] = tempNodeParameters;
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
}
} else if (returnDefaults) {
// Does not have values defined but defaults should be returned
nodeParameters[nodeProperties.name] = JSON.parse(JSON.stringify(nodeProperties.default));
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
}
} else if (nodeProperties.type === 'fixedCollection') {
// Is fixedCollection
@@ -646,7 +697,7 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
let nodePropertyOptions: INodePropertyCollection | undefined;
let propertyValues = nodeValues[nodeProperties.name];
if (returnDefaults === true) {
if (returnDefaults) {
if (propertyValues === undefined) {
propertyValues = JSON.parse(JSON.stringify(nodeProperties.default));
}
@@ -654,20 +705,39 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
// Iterate over all collections
for (const itemName of Object.keys(propertyValues || {})) {
if (nodeProperties.typeOptions !== undefined && nodeProperties.typeOptions.multipleValues === true) {
if (
nodeProperties.typeOptions !== undefined &&
nodeProperties.typeOptions.multipleValues === true
) {
// Multiple can be set so will be an array
const tempArrayValue: INodeParameters[] = [];
// Iterate over all items as it contains multiple ones
for (const nodeValue of (propertyValues as INodeParameters)[itemName] as INodeParameters[]) {
nodePropertyOptions = nodeProperties!.options!.find((nodePropertyOptions) => nodePropertyOptions.name === itemName) as INodePropertyCollection;
for (const nodeValue of (propertyValues as INodeParameters)[
itemName
] as INodeParameters[]) {
nodePropertyOptions = nodeProperties.options!.find(
// eslint-disable-next-line @typescript-eslint/no-shadow
(nodePropertyOptions) => nodePropertyOptions.name === itemName,
) as INodePropertyCollection;
if (nodePropertyOptions === undefined) {
throw new Error(`Could not find property option "${itemName}" for "${nodeProperties.name}"`);
throw new Error(
`Could not find property option "${itemName}" for "${nodeProperties.name}"`,
);
}
tempNodePropertiesArray = (nodePropertyOptions as INodePropertyCollection).values!;
tempValue = getNodeParameters(tempNodePropertiesArray, nodeValue as INodeParameters, returnDefaults, returnNoneDisplayed, false, false, nodeValuesRoot, nodeProperties.type);
tempNodePropertiesArray = nodePropertyOptions.values!;
tempValue = getNodeParameters(
tempNodePropertiesArray,
nodeValue,
returnDefaults,
returnNoneDisplayed,
false,
false,
nodeValuesRoot,
nodeProperties.type,
);
if (tempValue !== null) {
tempArrayValue.push(tempValue);
}
@@ -678,11 +748,23 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
tempNodeParameters = {};
// Get the options of the current item
const nodePropertyOptions = nodeProperties!.options!.find((data) => data.name === itemName);
// eslint-disable-next-line @typescript-eslint/no-shadow
const nodePropertyOptions = nodeProperties.options!.find(
(data) => data.name === itemName,
);
if (nodePropertyOptions !== undefined) {
tempNodePropertiesArray = (nodePropertyOptions as INodePropertyCollection).values!;
tempValue = getNodeParameters(tempNodePropertiesArray, (nodeValues[nodeProperties.name] as INodeParameters)[itemName] as INodeParameters, returnDefaults, returnNoneDisplayed, false, false, nodeValuesRoot, nodeProperties.type);
tempValue = getNodeParameters(
tempNodePropertiesArray,
(nodeValues[nodeProperties.name] as INodeParameters)[itemName] as INodeParameters,
returnDefaults,
returnNoneDisplayed,
false,
false,
nodeValuesRoot,
nodeProperties.type,
);
if (tempValue !== null) {
Object.assign(tempNodeParameters, tempValue);
}
@@ -694,13 +776,15 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
}
}
if (Object.keys(collectionValues).length !== 0 || returnDefaults === true) {
if (Object.keys(collectionValues).length !== 0 || returnDefaults) {
// Set only if value got found
if (returnDefaults === true) {
if (returnDefaults) {
// Set also when it has the default value
if (collectionValues === undefined) {
nodeParameters[nodeProperties.name] = JSON.parse(JSON.stringify(nodeProperties.default));
nodeParameters[nodeProperties.name] = JSON.parse(
JSON.stringify(nodeProperties.default),
);
} else {
nodeParameters[nodeProperties.name] = collectionValues;
}
@@ -717,7 +801,6 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
return nodeParameters;
}
/**
* Brings the output data in a format that can be returned from a node
*
@@ -726,7 +809,10 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
* @param {number} [outputIndex=0]
* @returns {Promise<INodeExecutionData[][]>}
*/
export async function prepareOutputData(outputData: INodeExecutionData[], outputIndex = 0): Promise<INodeExecutionData[][]> {
export async function prepareOutputData(
outputData: INodeExecutionData[],
outputIndex = 0,
): Promise<INodeExecutionData[][]> {
// TODO: Check if node has output with that index
const returnData = [];
@@ -739,8 +825,6 @@ export async function prepareOutputData(outputData: INodeExecutionData[], output
return returnData;
}
/**
* Returns all the webhooks which should be created for the give node
*
@@ -749,7 +833,12 @@ export async function prepareOutputData(outputData: INodeExecutionData[], output
* @param {INode} node
* @returns {IWebhookData[]}
*/
export function getNodeWebhooks(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, ignoreRestartWehbooks = false): IWebhookData[] {
export function getNodeWebhooks(
workflow: Workflow,
node: INode,
additionalData: IWorkflowExecuteAdditionalData,
ignoreRestartWehbooks = false,
): IWebhookData[] {
if (node.disabled === true) {
// Node is disabled so webhooks will also not be enabled
return [];
@@ -767,15 +856,21 @@ export function getNodeWebhooks(workflow: Workflow, node: INode, additionalData:
const returnData: IWebhookData[] = [];
for (const webhookDescription of nodeType.description.webhooks) {
if (ignoreRestartWehbooks === true && webhookDescription.restartWebhook === true) {
if (ignoreRestartWehbooks && webhookDescription.restartWebhook === true) {
continue;
}
let nodeWebhookPath = workflow.expression.getSimpleParameterValue(node, webhookDescription['path'], mode, {});
let nodeWebhookPath = workflow.expression.getSimpleParameterValue(
node,
webhookDescription.path,
mode,
{},
);
if (nodeWebhookPath === undefined) {
// TODO: Use a proper logger
console.error(`No webhook path could be found for node "${node.name}" in workflow "${workflowId}".`);
console.error(
`No webhook path could be found for node "${node.name}" in workflow "${workflowId}".`,
);
continue;
}
@@ -788,15 +883,35 @@ export function getNodeWebhooks(workflow: Workflow, node: INode, additionalData:
nodeWebhookPath = nodeWebhookPath.slice(0, -1);
}
const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookDescription['isFullPath'], 'internal', {}, false) as boolean;
const restartWebhook: boolean = workflow.expression.getSimpleParameterValue(node, webhookDescription['restartWebhook'], 'internal', {}, false) as boolean;
const isFullPath: boolean = workflow.expression.getSimpleParameterValue(
node,
webhookDescription.isFullPath,
'internal',
{},
false,
) as boolean;
const restartWebhook: boolean = workflow.expression.getSimpleParameterValue(
node,
webhookDescription.restartWebhook,
'internal',
{},
false,
) as boolean;
const path = getNodeWebhookPath(workflowId, node, nodeWebhookPath, isFullPath, restartWebhook);
const httpMethod = workflow.expression.getSimpleParameterValue(node, webhookDescription['httpMethod'], mode, {}, 'GET');
const httpMethod = workflow.expression.getSimpleParameterValue(
node,
webhookDescription.httpMethod,
mode,
{},
'GET',
);
if (httpMethod === undefined) {
// TODO: Use a proper logger
console.error(`The webhook "${path}" for node "${node.name}" in workflow "${workflowId}" could not be added because the httpMethod is not defined.`);
console.error(
`The webhook "${path}" for node "${node.name}" in workflow "${workflowId}" could not be added because the httpMethod is not defined.`,
);
continue;
}
@@ -838,10 +953,17 @@ export function getNodeWebhooksBasic(workflow: Workflow, node: INode): IWebhookD
const returnData: IWebhookData[] = [];
for (const webhookDescription of nodeType.description.webhooks) {
let nodeWebhookPath = workflow.expression.getSimpleParameterValue(node, webhookDescription['path'], mode, {});
let nodeWebhookPath = workflow.expression.getSimpleParameterValue(
node,
webhookDescription.path,
mode,
{},
);
if (nodeWebhookPath === undefined) {
// TODO: Use a proper logger
console.error(`No webhook path could be found for node "${node.name}" in workflow "${workflowId}".`);
console.error(
`No webhook path could be found for node "${node.name}" in workflow "${workflowId}".`,
);
continue;
}
@@ -854,19 +976,32 @@ export function getNodeWebhooksBasic(workflow: Workflow, node: INode): IWebhookD
nodeWebhookPath = nodeWebhookPath.slice(0, -1);
}
const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookDescription['isFullPath'], mode, {}, false) as boolean;
const isFullPath: boolean = workflow.expression.getSimpleParameterValue(
node,
webhookDescription.isFullPath,
mode,
{},
false,
) as boolean;
const path = getNodeWebhookPath(workflowId, node, nodeWebhookPath, isFullPath);
const httpMethod = workflow.expression.getSimpleParameterValue(node, webhookDescription['httpMethod'], mode, {});
const httpMethod = workflow.expression.getSimpleParameterValue(
node,
webhookDescription.httpMethod,
mode,
{},
);
if (httpMethod === undefined) {
// TODO: Use a proper logger
console.error(`The webhook "${path}" for node "${node.name}" in workflow "${workflowId}" could not be added because the httpMethod is not defined.`);
console.error(
`The webhook "${path}" for node "${node.name}" in workflow "${workflowId}" could not be added because the httpMethod is not defined.`,
);
continue;
}
//@ts-ignore
// @ts-ignore
returnData.push({
httpMethod: httpMethod.toString() as WebhookHttpMethod,
node: node.name,
@@ -879,7 +1014,6 @@ export function getNodeWebhooksBasic(workflow: Workflow, node: INode): IWebhookD
return returnData;
}
/**
* Returns the webhook path
*
@@ -889,11 +1023,18 @@ export function getNodeWebhooksBasic(workflow: Workflow, node: INode): IWebhookD
* @param {string} path
* @returns {string}
*/
export function getNodeWebhookPath(workflowId: string, node: INode, path: string, isFullPath?: boolean, restartWebhook?: boolean): string {
export function getNodeWebhookPath(
workflowId: string,
node: INode,
path: string,
isFullPath?: boolean,
restartWebhook?: boolean,
): string {
let webhookPath = '';
if (restartWebhook === true) {
return path;
} else if (node.webhookId === undefined) {
}
if (node.webhookId === undefined) {
webhookPath = `${workflowId}/${encodeURIComponent(node.name.toLowerCase())}/${path}`;
} else {
if (isFullPath === true) {
@@ -904,7 +1045,6 @@ export function getNodeWebhookPath(workflowId: string, node: INode, path: string
return webhookPath;
}
/**
* Returns the webhook URL
*
@@ -916,7 +1056,13 @@ export function getNodeWebhookPath(workflowId: string, node: INode, path: string
* @param {boolean} isFullPath
* @returns {string}
*/
export function getNodeWebhookUrl(baseUrl: string, workflowId: string, node: INode, path: string, isFullPath?: boolean): string {
export function getNodeWebhookUrl(
baseUrl: string,
workflowId: string,
node: INode,
path: string,
isFullPath?: boolean,
): string {
if ((path.startsWith(':') || path.includes('/:')) && node.webhookId) {
// setting this to false to prefix the webhookId
isFullPath = false;
@@ -927,7 +1073,6 @@ export function getNodeWebhookUrl(baseUrl: string, workflowId: string, node: INo
return `${baseUrl}/${getNodeWebhookPath(workflowId, node, path, isFullPath)}`;
}
/**
* Returns all the parameter-issues of the node
*
@@ -936,7 +1081,10 @@ export function getNodeWebhookUrl(baseUrl: string, workflowId: string, node: INo
* @param {INode} node The data of the node
* @returns {(INodeIssues | null)}
*/
export function getNodeParametersIssues(nodePropertiesArray: INodeProperties[], node: INode): INodeIssues | null {
export function getNodeParametersIssues(
nodePropertiesArray: INodeProperties[],
node: INode,
): INodeIssues | null {
const foundIssues: INodeIssues = {};
let propertyIssues: INodeIssues;
@@ -957,7 +1105,6 @@ export function getNodeParametersIssues(nodePropertiesArray: INodeProperties[],
return foundIssues;
}
/**
* Returns the issues of the node as string
*
@@ -973,12 +1120,10 @@ export function nodeIssuesToString(issues: INodeIssues, node?: INode): string[]
nodeIssues.push(`Execution Error.`);
}
const objectProperties = [
'parameters',
'credentials',
];
const objectProperties = ['parameters', 'credentials'];
let issueText: string, parameterName: string;
let issueText: string;
let parameterName: string;
for (const propertyName of objectProperties) {
if (issues[propertyName] !== undefined) {
for (parameterName of Object.keys(issues[propertyName] as object)) {
@@ -1000,7 +1145,6 @@ export function nodeIssuesToString(issues: INodeIssues, node?: INode): string[]
return nodeIssues;
}
/**
* Adds an issue if the parameter is not defined
*
@@ -1009,11 +1153,17 @@ export function nodeIssuesToString(issues: INodeIssues, node?: INode): string[]
* @param {INodeProperties} nodeProperties The properties of the node
* @param {NodeParameterValue} value The value of the parameter
*/
export function addToIssuesIfMissing(foundIssues: INodeIssues, nodeProperties: INodeProperties, value: NodeParameterValue) {
export function addToIssuesIfMissing(
foundIssues: INodeIssues,
nodeProperties: INodeProperties,
value: NodeParameterValue,
) {
// TODO: Check what it really has when undefined
if ((nodeProperties.type === 'string' && (value === '' || value === undefined)) ||
if (
(nodeProperties.type === 'string' && (value === '' || value === undefined)) ||
(nodeProperties.type === 'multiOptions' && Array.isArray(value) && value.length === 0) ||
(nodeProperties.type === 'dateTime' && value === undefined)) {
(nodeProperties.type === 'dateTime' && value === undefined)
) {
// Parameter is requried but empty
if (foundIssues.parameters === undefined) {
foundIssues.parameters = {};
@@ -1022,11 +1172,12 @@ export function addToIssuesIfMissing(foundIssues: INodeIssues, nodeProperties: I
foundIssues.parameters[nodeProperties.name] = [];
}
foundIssues.parameters[nodeProperties.name].push(`Parameter "${nodeProperties.displayName}" is required.`);
foundIssues.parameters[nodeProperties.name].push(
`Parameter "${nodeProperties.displayName}" is required.`,
);
}
}
/**
* Returns the parameter value
*
@@ -1036,15 +1187,14 @@ export function addToIssuesIfMissing(foundIssues: INodeIssues, nodeProperties: I
* @param {string} path The path to the properties
* @returns
*/
export function getParameterValueByPath(nodeValues: INodeParameters, parameterName: string, path: string) {
return get(
nodeValues,
path ? path + '.' + parameterName : parameterName,
);
export function getParameterValueByPath(
nodeValues: INodeParameters,
parameterName: string,
path: string,
) {
return get(nodeValues, path ? `${path}.${parameterName}` : parameterName);
}
/**
* Returns all the issues with the given node-values
*
@@ -1054,7 +1204,11 @@ export function getParameterValueByPath(nodeValues: INodeParameters, parameterNa
* @param {string} path The path to the properties
* @returns {INodeIssues}
*/
export function getParameterIssues(nodeProperties: INodeProperties, nodeValues: INodeParameters, path: string): INodeIssues {
export function getParameterIssues(
nodeProperties: INodeProperties,
nodeValues: INodeParameters,
path: string,
): INodeIssues {
const foundIssues: INodeIssues = {};
let value;
@@ -1062,11 +1216,15 @@ export function getParameterIssues(nodeProperties: INodeProperties, nodeValues:
if (displayParameterPath(nodeValues, nodeProperties, path)) {
value = getParameterValueByPath(nodeValues, nodeProperties.name, path);
if (nodeProperties.typeOptions !== undefined && nodeProperties.typeOptions.multipleValues !== undefined) {
if (
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
nodeProperties.typeOptions !== undefined &&
nodeProperties.typeOptions.multipleValues !== undefined
) {
// Multiple can be set so will be an array
if (Array.isArray(value)) {
for (const singleValue of value as NodeParameterValue[]) {
addToIssuesIfMissing(foundIssues, nodeProperties, singleValue as NodeParameterValue);
addToIssuesIfMissing(foundIssues, nodeProperties, singleValue);
}
}
} else {
@@ -1106,7 +1264,7 @@ export function getParameterIssues(nodeProperties: INodeProperties, nodeValues:
});
}
} else if (nodeProperties.type === 'fixedCollection') {
basePath = basePath ? `${basePath}.` : '' + nodeProperties.name + '.';
basePath = basePath ? `${basePath}.` : `${nodeProperties.name}.`;
let propertyOptions: INodePropertyCollection;
for (propertyOptions of nodeProperties.options as INodePropertyCollection[]) {
@@ -1116,14 +1274,18 @@ export function getParameterIssues(nodeProperties: INodeProperties, nodeValues:
continue;
}
if (nodeProperties.typeOptions !== undefined && nodeProperties.typeOptions.multipleValues !== undefined) {
if (
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
nodeProperties.typeOptions !== undefined &&
nodeProperties.typeOptions.multipleValues !== undefined
) {
// Multiple can be set so will be an array of objects
if (Array.isArray(value)) {
for (let i = 0; i < (value as INodeParameters[]).length; i++) {
for (const option of propertyOptions.values) {
checkChildNodeProperties.push({
basePath: `${basePath}${propertyOptions.name}[${i}]`,
data: option as INodeProperties,
data: option,
});
}
}
@@ -1133,7 +1295,7 @@ export function getParameterIssues(nodeProperties: INodeProperties, nodeValues:
for (const option of propertyOptions.values) {
checkChildNodeProperties.push({
basePath: basePath + propertyOptions.name,
data: option as INodeProperties,
data: option,
});
}
}
@@ -1146,14 +1308,13 @@ export function getParameterIssues(nodeProperties: INodeProperties, nodeValues:
let propertyIssues;
for (const optionData of checkChildNodeProperties) {
propertyIssues = getParameterIssues(optionData.data as INodeProperties, nodeValues, optionData.basePath);
propertyIssues = getParameterIssues(optionData.data, nodeValues, optionData.basePath);
mergeIssues(foundIssues, propertyIssues);
}
return foundIssues;
}
/**
* Merges multiple NodeIssues together
*
@@ -1172,10 +1333,7 @@ export function mergeIssues(destination: INodeIssues, source: INodeIssues | null
destination.execution = true;
}
const objectProperties = [
'parameters',
'credentials',
];
const objectProperties = ['parameters', 'credentials'];
let destinationProperty: INodeIssueObjectProperty;
for (const propertyName of objectProperties) {
@@ -1190,7 +1348,10 @@ export function mergeIssues(destination: INodeIssues, source: INodeIssues | null
if (destinationProperty[parameterName] === undefined) {
destinationProperty[parameterName] = [];
}
destinationProperty[parameterName].push.apply(destinationProperty[parameterName], (source[propertyName] as INodeIssueObjectProperty)[parameterName]);
destinationProperty[parameterName].push.apply(
destinationProperty[parameterName],
(source[propertyName] as INodeIssueObjectProperty)[parameterName],
);
}
}
}
@@ -1200,8 +1361,6 @@ export function mergeIssues(destination: INodeIssues, source: INodeIssues | null
}
}
/**
* Merges the given node properties
*
@@ -1209,10 +1368,13 @@ export function mergeIssues(destination: INodeIssues, source: INodeIssues | null
* @param {INodeProperties[]} mainProperties
* @param {INodeProperties[]} addProperties
*/
export function mergeNodeProperties(mainProperties: INodeProperties[], addProperties: INodeProperties[]): void {
export function mergeNodeProperties(
mainProperties: INodeProperties[],
addProperties: INodeProperties[],
): void {
let existingIndex: number;
for (const property of addProperties) {
existingIndex = mainProperties.findIndex(element => element.name === property.name);
existingIndex = mainProperties.findIndex((element) => element.name === property.name);
if (existingIndex === -1) {
// Property does not exist yet, so add

View File

@@ -1,20 +1,35 @@
import {
IDataObject,
IObservableObject,
} from './';
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-shadow */
/* eslint-disable no-param-reassign */
/* eslint-disable no-underscore-dangle */
// eslint-disable-next-line import/no-cycle
import { IDataObject, IObservableObject } from '.';
export interface IObservableOptions {
ignoreEmptyOnFirstChild?: boolean;
}
export function create(target: IDataObject, parent?: IObservableObject, option?: IObservableOptions, depth?: number): IDataObject {
export function create(
target: IDataObject,
parent?: IObservableObject,
option?: IObservableOptions,
depth?: number,
): IDataObject {
// eslint-disable-next-line no-param-reassign, @typescript-eslint/prefer-nullish-coalescing
depth = depth || 0;
// Make all the children of target also observeable
// eslint-disable-next-line no-restricted-syntax
for (const key in target) {
if (typeof target[key] === 'object' && target[key] !== null) {
target[key] = create(target[key] as IDataObject, (parent || target) as IObservableObject, option, depth + 1);
// eslint-disable-next-line no-param-reassign
target[key] = create(
target[key] as IDataObject,
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
(parent || target) as IObservableObject,
option,
depth + 1,
);
}
}
@@ -23,6 +38,7 @@ export function create(target: IDataObject, parent?: IObservableObject, option?:
writable: true,
});
return new Proxy(target, {
// eslint-disable-next-line @typescript-eslint/no-shadow
deleteProperty(target, name) {
if (parent === undefined) {
// If no parent is given mark current data as changed
@@ -39,8 +55,15 @@ export function create(target: IDataObject, parent?: IObservableObject, option?:
set(target, name, value) {
if (parent === undefined) {
// If no parent is given mark current data as changed
if (option !== undefined && option.ignoreEmptyOnFirstChild === true && depth === 0
&& target[name.toString()] === undefined && typeof value === 'object' && Object.keys(value).length === 0) {
if (
option !== undefined &&
option.ignoreEmptyOnFirstChild === true &&
depth === 0 &&
target[name.toString()] === undefined &&
typeof value === 'object' &&
Object.keys(value).length === 0
// eslint-disable-next-line no-empty
) {
} else {
(target as IObservableObject).__dataChanged = true;
}

View File

@@ -1,4 +1,15 @@
/* eslint-disable no-param-reassign */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-for-in-array */
/* eslint-disable no-prototype-builtins */
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-continue */
/* eslint-disable no-restricted-syntax */
/* eslint-disable import/no-cycle */
// eslint-disable-next-line import/no-cycle
import {
Expression,
IConnections,
@@ -26,20 +37,27 @@ import {
WebhookSetupMethodNames,
WorkflowActivateMode,
WorkflowExecuteMode,
} from './';
} from '.';
import { IConnection, IDataObject, IObservableObject } from './Interfaces';
export class Workflow {
id: string | undefined;
name: string | undefined;
nodes: INodes = {};
connectionsBySourceNode: IConnections;
connectionsByDestinationNode: IConnections;
nodeTypes: INodeTypes;
expression: Expression;
active: boolean;
settings: IWorkflowSettings;
// To save workflow specific static data like for example
@@ -47,7 +65,16 @@ export class Workflow {
staticData: IDataObject;
// constructor(id: string | undefined, nodes: INode[], connections: IConnections, active: boolean, nodeTypes: INodeTypes, staticData?: IDataObject, settings?: IWorkflowSettings) {
constructor(parameters: {id?: string, name?: string, nodes: INode[], connections: IConnections, active: boolean, nodeTypes: INodeTypes, staticData?: IDataObject, settings?: IWorkflowSettings}) {
constructor(parameters: {
id?: string;
name?: string;
nodes: INode[];
connections: IConnections;
active: boolean;
nodeTypes: INodeTypes;
staticData?: IDataObject;
settings?: IWorkflowSettings;
}) {
this.id = parameters.id;
this.name = parameters.name;
this.nodeTypes = parameters.nodeTypes;
@@ -70,7 +97,12 @@ export class Workflow {
}
// Add default values
const nodeParameters = NodeHelpers.getNodeParameters(nodeType.description.properties, node.parameters, true, false);
const nodeParameters = NodeHelpers.getNodeParameters(
nodeType.description.properties,
node.parameters,
true,
false,
);
node.parameters = nodeParameters !== null ? nodeParameters : {};
}
this.connectionsBySourceNode = parameters.connections;
@@ -80,15 +112,15 @@ export class Workflow {
this.active = parameters.active || false;
this.staticData = ObservableObject.create(parameters.staticData || {}, undefined, { ignoreEmptyOnFirstChild: true });
this.staticData = ObservableObject.create(parameters.staticData || {}, undefined, {
ignoreEmptyOnFirstChild: true,
});
this.settings = parameters.settings || {};
this.expression = new Expression(this);
}
/**
* The default connections are by source node. This function rewrites them by destination nodes
* to easily find parent nodes.
@@ -140,8 +172,6 @@ export class Workflow {
return returnConnection;
}
/**
* A workflow can only be activated if it has a node which has either triggers
* or webhooks defined.
@@ -162,6 +192,7 @@ export class Workflow {
continue;
}
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
if (ignoreNodeTypes !== undefined && ignoreNodeTypes.includes(node.type)) {
continue;
}
@@ -173,7 +204,11 @@ export class Workflow {
continue;
}
if (nodeType.poll !== undefined || nodeType.trigger !== undefined || nodeType.webhook !== undefined) {
if (
nodeType.poll !== undefined ||
nodeType.trigger !== undefined ||
nodeType.webhook !== undefined
) {
// Is a trigger node. So workflow can be activated.
return true;
}
@@ -182,8 +217,6 @@ export class Workflow {
return false;
}
/**
* Checks if everything in the workflow is complete
* and ready to be executed. If it returns null everything
@@ -216,7 +249,7 @@ export class Workflow {
typeUnknown: true,
};
} else {
nodeIssues = NodeHelpers.getNodeParametersIssues(nodeType.description.properties!, node);
nodeIssues = NodeHelpers.getNodeParametersIssues(nodeType.description.properties, node);
}
if (nodeIssues !== null) {
@@ -231,8 +264,6 @@ export class Workflow {
return workflowIssues;
}
/**
* Returns the static data of the workflow.
* It gets saved with the workflow and will be the same for
@@ -249,11 +280,15 @@ export class Workflow {
key = 'global';
} else if (type === 'node') {
if (node === undefined) {
throw new Error(`The request data of context type "node" the node parameter has to be set!`);
throw new Error(
`The request data of context type "node" the node parameter has to be set!`,
);
}
key = `node:${node.name}`;
} else {
throw new Error(`The context type "${type}" is not know. Only "global" and node" are supported!`);
throw new Error(
`The context type "${type}" is not know. Only "global" and node" are supported!`,
);
}
if (this.staticData[key] === undefined) {
@@ -265,8 +300,6 @@ export class Workflow {
return this.staticData[key] as IDataObject;
}
/**
* Returns all the trigger nodes in the workflow.
*
@@ -274,10 +307,9 @@ export class Workflow {
* @memberof Workflow
*/
getTriggerNodes(): INode[] {
return this.queryNodes((nodeType: INodeType) => !!nodeType.trigger );
return this.queryNodes((nodeType: INodeType) => !!nodeType.trigger);
}
/**
* Returns all the poll nodes in the workflow
*
@@ -285,10 +317,9 @@ export class Workflow {
* @memberof Workflow
*/
getPollNodes(): INode[] {
return this.queryNodes((nodeType: INodeType) => !!nodeType.poll );
return this.queryNodes((nodeType: INodeType) => !!nodeType.poll);
}
/**
* Returns all the nodes in the workflow for which the given
* checkFunction return true
@@ -321,8 +352,6 @@ export class Workflow {
return returnNodes;
}
/**
* Returns the node with the given name if it exists else null
*
@@ -338,7 +367,6 @@ export class Workflow {
return null;
}
/**
* Renames nodes in expressions
*
@@ -348,7 +376,11 @@ export class Workflow {
* @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])}
* @memberof Workflow
*/
renameNodeInExpressions(parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], currentName: string, newName: string): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] {
renameNodeInExpressions(
parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[],
currentName: string,
newName: string,
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] {
if (typeof parameterValue !== 'object') {
// Reached the actual value
if (typeof parameterValue === 'string' && parameterValue.charAt(0) === '=') {
@@ -362,7 +394,11 @@ export class Workflow {
// In case some special characters are used in name escape them
const currentNameEscaped = currentName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
parameterValue = parameterValue.replace(new RegExp(`(\\$node(\.|\\["|\\[\'))${currentNameEscaped}((\.|"\\]|\'\\]))`, 'g'), `$1${newName}$3`);
parameterValue = parameterValue.replace(
// eslint-disable-next-line no-useless-escape
new RegExp(`(\\$node(\.|\\["|\\[\'))${currentNameEscaped}((\.|"\\]|\'\\]))`, 'g'),
`$1${newName}$3`,
);
}
}
@@ -370,7 +406,8 @@ export class Workflow {
}
if (Array.isArray(parameterValue)) {
const returnArray: any[] = []; // tslint:disable-line:no-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const returnArray: any[] = [];
for (const currentValue of parameterValue) {
returnArray.push(this.renameNodeInExpressions(currentValue, currentName, newName));
@@ -379,17 +416,21 @@ export class Workflow {
return returnArray;
}
const returnData: any = {}; // tslint:disable-line:no-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const returnData: any = {};
for (const parameterName of Object.keys(parameterValue || {})) {
returnData[parameterName] = this.renameNodeInExpressions(parameterValue![parameterName], currentName, newName);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
returnData[parameterName] = this.renameNodeInExpressions(
parameterValue![parameterName],
currentName,
newName,
);
}
return returnData;
}
/**
* Rename a node in the workflow
*
@@ -398,7 +439,6 @@ export class Workflow {
* @memberof Workflow
*/
renameNode(currentName: string, newName: string) {
// Rename the node itself
if (this.nodes[currentName] !== undefined) {
this.nodes[newName] = this.nodes[currentName];
@@ -409,7 +449,11 @@ export class Workflow {
// Update the expressions which reference the node
// with its old name
for (const node of Object.values(this.nodes)) {
node.parameters = this.renameNodeInExpressions(node.parameters, currentName, newName) as INodeParameters;
node.parameters = this.renameNodeInExpressions(
node.parameters,
currentName,
newName,
) as INodeParameters;
}
// Change all source connections
@@ -419,12 +463,21 @@ export class Workflow {
}
// Change all destination connections
let sourceNode: string, type: string, sourceIndex: string, connectionIndex: string, connectionData: IConnection;
let sourceNode: string;
let type: string;
let sourceIndex: string;
let connectionIndex: string;
let connectionData: IConnection;
for (sourceNode of Object.keys(this.connectionsBySourceNode)) {
for (type of Object.keys(this.connectionsBySourceNode[sourceNode])) {
for (sourceIndex of Object.keys(this.connectionsBySourceNode[sourceNode][type])) {
for (connectionIndex of Object.keys(this.connectionsBySourceNode[sourceNode][type][parseInt(sourceIndex, 10)])) {
connectionData = this.connectionsBySourceNode[sourceNode][type][parseInt(sourceIndex, 10)][parseInt(connectionIndex, 10)];
for (connectionIndex of Object.keys(
this.connectionsBySourceNode[sourceNode][type][parseInt(sourceIndex, 10)],
)) {
connectionData =
this.connectionsBySourceNode[sourceNode][type][parseInt(sourceIndex, 10)][
parseInt(connectionIndex, 10)
];
if (connectionData.node === currentName) {
connectionData.node = newName;
}
@@ -434,11 +487,11 @@ export class Workflow {
}
// Use the updated connections to create updated connections by destionation nodes
this.connectionsByDestinationNode = this.__getConnectionsByDestination(this.connectionsBySourceNode);
this.connectionsByDestinationNode = this.__getConnectionsByDestination(
this.connectionsBySourceNode,
);
}
/**
* Finds the highest parent nodes of the node with the given name
*
@@ -448,7 +501,12 @@ export class Workflow {
* @returns {string[]}
* @memberof Workflow
*/
getHighestNode(nodeName: string, type = 'main', nodeConnectionIndex?:number, checkedNodes?: string[]): string[] {
getHighestNode(
nodeName: string,
type = 'main',
nodeConnectionIndex?: number,
checkedNodes?: string[],
): string[] {
const currentHighest: string[] = [];
if (this.nodes[nodeName].disabled === false) {
// If the current node is not disabled itself is the highest
@@ -467,23 +525,28 @@ export class Workflow {
checkedNodes = checkedNodes || [];
if (checkedNodes!.includes(nodeName)) {
if (checkedNodes.includes(nodeName)) {
// Node got checked already before
return currentHighest;
}
checkedNodes!.push(nodeName);
checkedNodes.push(nodeName);
const returnNodes: string[] = [];
let addNodes: string[];
let connectionsByIndex: IConnection[];
for (let connectionIndex = 0; connectionIndex < this.connectionsByDestinationNode[nodeName][type].length; connectionIndex++) {
for (
let connectionIndex = 0;
connectionIndex < this.connectionsByDestinationNode[nodeName][type].length;
connectionIndex++
) {
if (nodeConnectionIndex !== undefined && nodeConnectionIndex !== connectionIndex) {
// If a connection-index is given ignore all other ones
continue;
}
connectionsByIndex = this.connectionsByDestinationNode[nodeName][type][connectionIndex];
// eslint-disable-next-line @typescript-eslint/no-loop-func
connectionsByIndex.forEach((connection) => {
if (checkedNodes!.includes(connection.node)) {
// Node got checked already before
@@ -512,8 +575,6 @@ export class Workflow {
return returnNodes;
}
/**
* Returns all the after the given one
*
@@ -527,8 +588,6 @@ export class Workflow {
return this.getConnectedNodes(this.connectionsBySourceNode, nodeName, type, depth);
}
/**
* Returns all the nodes before the given one
*
@@ -542,8 +601,6 @@ export class Workflow {
return this.getConnectedNodes(this.connectionsByDestinationNode, nodeName, type, depth);
}
/**
* Gets all the nodes which are connected nodes starting from
* the given one
@@ -556,7 +613,13 @@ export class Workflow {
* @returns {string[]}
* @memberof Workflow
*/
getConnectedNodes(connections: IConnections, nodeName: string, type = 'main', depth = -1, checkedNodes?: string[]): string[] {
getConnectedNodes(
connections: IConnections,
nodeName: string,
type = 'main',
depth = -1,
checkedNodes?: string[],
): string[] {
depth = depth === -1 ? -1 : depth;
const newDepth = depth === -1 ? depth : depth - 1;
if (depth === 0) {
@@ -576,12 +639,12 @@ export class Workflow {
checkedNodes = checkedNodes || [];
if (checkedNodes!.includes(nodeName)) {
if (checkedNodes.includes(nodeName)) {
// Node got checked already before
return [];
}
checkedNodes!.push(nodeName);
checkedNodes.push(nodeName);
const returnNodes: string[] = [];
let addNodes: string[];
@@ -597,7 +660,13 @@ export class Workflow {
returnNodes.unshift(connection.node);
addNodes = this.getConnectedNodes(connections, connection.node, type, newDepth, checkedNodes);
addNodes = this.getConnectedNodes(
connections,
connection.node,
type,
newDepth,
checkedNodes,
);
for (i = addNodes.length; i--; i > 0) {
// Because nodes can have multiple parents it is possible that
@@ -620,8 +689,6 @@ export class Workflow {
return returnNodes;
}
/**
* Returns via which output of the parent-node the node
* is connected to.
@@ -634,7 +701,13 @@ export class Workflow {
* @returns {(number | undefined)}
* @memberof Workflow
*/
getNodeConnectionOutputIndex(nodeName: string, parentNodeName: string, type = 'main', depth = -1, checkedNodes?: string[]): number | undefined {
getNodeConnectionOutputIndex(
nodeName: string,
parentNodeName: string,
type = 'main',
depth = -1,
checkedNodes?: string[],
): number | undefined {
const node = this.getNode(parentNodeName);
if (node === null) {
return undefined;
@@ -665,12 +738,12 @@ export class Workflow {
checkedNodes = checkedNodes || [];
if (checkedNodes!.includes(nodeName)) {
if (checkedNodes.includes(nodeName)) {
// Node got checked already before
return undefined;
}
checkedNodes!.push(nodeName);
checkedNodes.push(nodeName);
let outputIndex: number | undefined;
for (const connectionsByIndex of this.connectionsByDestinationNode[nodeName][type]) {
@@ -679,12 +752,18 @@ export class Workflow {
return connection.index;
}
if (checkedNodes!.includes(connection.node)) {
if (checkedNodes.includes(connection.node)) {
// Node got checked already before so continue with the next one
continue;
}
outputIndex = this.getNodeConnectionOutputIndex(connection.node, parentNodeName, type, newDepth, checkedNodes);
outputIndex = this.getNodeConnectionOutputIndex(
connection.node,
parentNodeName,
type,
newDepth,
checkedNodes,
);
if (outputIndex !== undefined) {
return outputIndex;
@@ -695,9 +774,6 @@ export class Workflow {
return undefined;
}
/**
* Returns from which of the given nodes the workflow should get started from
*
@@ -713,7 +789,6 @@ export class Workflow {
node = this.nodes[nodeName];
nodeType = this.nodeTypes.getByName(node.type) as INodeType;
if (nodeType.trigger !== undefined || nodeType.poll !== undefined) {
if (node.disabled === true) {
continue;
@@ -734,8 +809,6 @@ export class Workflow {
return undefined;
}
/**
* Returns the start node to start the worfklow from
*
@@ -744,7 +817,6 @@ export class Workflow {
* @memberof Workflow
*/
getStartNode(destinationNode?: string): INode | undefined {
if (destinationNode) {
// Find the highest parent nodes of the given one
const nodeNames = this.getHighestNode(destinationNode);
@@ -769,8 +841,6 @@ export class Workflow {
return this.__getStartNode(Object.keys(this.nodes));
}
/**
* Executes the Webhooks method of the node
*
@@ -781,11 +851,17 @@ export class Workflow {
* @returns {(Promise<boolean | undefined>)}
* @memberof Workflow
*/
async runWebhookMethod(method: WebhookSetupMethodNames, webhookData: IWebhookData, nodeExecuteFunctions: INodeExecuteFunctions, mode: WorkflowExecuteMode, activation: WorkflowActivateMode, isTest?: boolean): Promise<boolean | undefined> {
async runWebhookMethod(
method: WebhookSetupMethodNames,
webhookData: IWebhookData,
nodeExecuteFunctions: INodeExecuteFunctions,
mode: WorkflowExecuteMode,
activation: WorkflowActivateMode,
isTest?: boolean,
): Promise<boolean | undefined> {
const node = this.getNode(webhookData.node) as INode;
const nodeType = this.nodeTypes.getByName(node.type) as INodeType;
if (nodeType.webhookMethods === undefined) {
return;
}
@@ -798,11 +874,19 @@ export class Workflow {
return;
}
const thisArgs = nodeExecuteFunctions.getExecuteHookFunctions(this, node, webhookData.workflowExecuteAdditionalData, mode, activation, isTest, webhookData);
const thisArgs = nodeExecuteFunctions.getExecuteHookFunctions(
this,
node,
webhookData.workflowExecuteAdditionalData,
mode,
activation,
isTest,
webhookData,
);
// eslint-disable-next-line consistent-return
return nodeType.webhookMethods[webhookData.webhookDescription.name][method]!.call(thisArgs);
}
/**
* Runs the given trigger node so that it can trigger the workflow
* when the node has data.
@@ -814,7 +898,13 @@ export class Workflow {
* @returns {(Promise<ITriggerResponse | undefined>)}
* @memberof Workflow
*/
async runTrigger(node: INode, getTriggerFunctions: IGetExecuteTriggerFunctions, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): Promise<ITriggerResponse | undefined> {
async runTrigger(
node: INode,
getTriggerFunctions: IGetExecuteTriggerFunctions,
additionalData: IWorkflowExecuteAdditionalData,
mode: WorkflowExecuteMode,
activation: WorkflowActivateMode,
): Promise<ITriggerResponse | undefined> {
const triggerFunctions = getTriggerFunctions(this, node, additionalData, mode, activation);
const nodeType = this.nodeTypes.getByName(node.type);
@@ -824,29 +914,30 @@ export class Workflow {
}
if (!nodeType.trigger) {
throw new Error(`The node type "${node.type}" of node "${node.name}" does not have a trigger function defined.`);
throw new Error(
`The node type "${node.type}" of node "${node.name}" does not have a trigger function defined.`,
);
}
if (mode === 'manual') {
// In manual mode we do not just start the trigger function we also
// want to be able to get informed as soon as the first data got emitted
const triggerResponse = await nodeType.trigger!.call(triggerFunctions);
const triggerResponse = await nodeType.trigger.call(triggerFunctions);
// Add the manual trigger response which resolves when the first time data got emitted
triggerResponse!.manualTriggerResponse = new Promise((resolve) => {
// eslint-disable-next-line @typescript-eslint/no-shadow
triggerFunctions.emit = ((resolve) => (data: INodeExecutionData[][]) => {
resolve(data);
})(resolve);
});
return triggerResponse;
} else {
// In all other modes simply start the trigger
return nodeType.trigger!.call(triggerFunctions);
}
// In all other modes simply start the trigger
return nodeType.trigger.call(triggerFunctions);
}
/**
* Runs the given trigger node so that it can trigger the workflow
* when the node has data.
@@ -856,7 +947,10 @@ export class Workflow {
* @returns
* @memberof Workflow
*/
async runPoll(node: INode, pollFunctions: IPollFunctions): Promise<INodeExecutionData[][] | null> {
async runPoll(
node: INode,
pollFunctions: IPollFunctions,
): Promise<INodeExecutionData[][] | null> {
const nodeType = this.nodeTypes.getByName(node.type);
if (nodeType === undefined) {
@@ -864,13 +958,14 @@ export class Workflow {
}
if (!nodeType.poll) {
throw new Error(`The node type "${node.type}" of node "${node.name}" does not have a poll function defined.`);
throw new Error(
`The node type "${node.type}" of node "${node.name}" does not have a poll function defined.`,
);
}
return nodeType.poll!.call(pollFunctions);
return nodeType.poll.call(pollFunctions);
}
/**
* Executes the webhook data to see what it should return and if the
* workflow should be started or not
@@ -882,7 +977,13 @@ export class Workflow {
* @returns {Promise<IWebhookResponseData>}
* @memberof Workflow
*/
async runWebhook(webhookData: IWebhookData, node: INode, additionalData: IWorkflowExecuteAdditionalData, nodeExecuteFunctions: INodeExecuteFunctions, mode: WorkflowExecuteMode): Promise<IWebhookResponseData> {
async runWebhook(
webhookData: IWebhookData,
node: INode,
additionalData: IWorkflowExecuteAdditionalData,
nodeExecuteFunctions: INodeExecuteFunctions,
mode: WorkflowExecuteMode,
): Promise<IWebhookResponseData> {
const nodeType = this.nodeTypes.getByName(node.type);
if (nodeType === undefined) {
throw new Error(`The type of the webhook node "${node.name}" is not known.`);
@@ -890,11 +991,16 @@ export class Workflow {
throw new Error(`The node "${node.name}" does not have any webhooks defined.`);
}
const thisArgs = nodeExecuteFunctions.getExecuteWebhookFunctions(this, node, additionalData, mode, webhookData);
const thisArgs = nodeExecuteFunctions.getExecuteWebhookFunctions(
this,
node,
additionalData,
mode,
webhookData,
);
return nodeType.webhook.call(thisArgs);
}
/**
* Executes the given node.
*
@@ -908,7 +1014,15 @@ export class Workflow {
* @returns {(Promise<INodeExecutionData[][] | null>)}
* @memberof Workflow
*/
async runNode(node: INode, inputData: ITaskDataConnections, runExecutionData: IRunExecutionData, runIndex: number, additionalData: IWorkflowExecuteAdditionalData, nodeExecuteFunctions: INodeExecuteFunctions, mode: WorkflowExecuteMode): Promise<INodeExecutionData[][] | null | undefined> {
async runNode(
node: INode,
inputData: ITaskDataConnections,
runExecutionData: IRunExecutionData,
runIndex: number,
additionalData: IWorkflowExecuteAdditionalData,
nodeExecuteFunctions: INodeExecuteFunctions,
mode: WorkflowExecuteMode,
): Promise<INodeExecutionData[][] | null | undefined> {
if (node.disabled === true) {
// If node is disabled simply pass the data through
// return NodeRunHelpers.
@@ -917,7 +1031,7 @@ export class Workflow {
if (inputData.main[0] === null) {
return undefined;
}
return [(inputData.main[0] as INodeExecutionData[])];
return [inputData.main[0]];
}
return undefined;
}
@@ -935,7 +1049,7 @@ export class Workflow {
if (inputData.hasOwnProperty('main') && inputData.main.length > 0) {
// We always use the data of main input and the first input for executeSingle
connectionInputData = (inputData.main[0] as INodeExecutionData[]);
connectionInputData = inputData.main[0] as INodeExecutionData[];
}
if (connectionInputData.length === 0) {
@@ -944,7 +1058,10 @@ export class Workflow {
}
}
if (runExecutionData.resultData.lastNodeExecuted === node.name && runExecutionData.resultData.error !== undefined) {
if (
runExecutionData.resultData.lastNodeExecuted === node.name &&
runExecutionData.resultData.error !== undefined
) {
// The node did already fail. So throw an error here that it displays and logs it correctly.
// Does get used by webhook and trigger nodes in case they throw an error that it is possible
// to log the error and display in Editor-UI.
@@ -959,7 +1076,8 @@ export class Workflow {
connectionInputData = connectionInputData.slice(0, 1);
const newInputData: ITaskDataConnections = {};
for (const inputName of Object.keys(inputData)) {
newInputData[inputName] = inputData[inputName].map(input => {
newInputData[inputName] = inputData[inputName].map((input) => {
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
return input && input.slice(0, 1);
});
}
@@ -970,9 +1088,19 @@ export class Workflow {
const returnPromises: Array<Promise<INodeExecutionData>> = [];
for (let itemIndex = 0; itemIndex < connectionInputData.length; itemIndex++) {
const thisArgs = nodeExecuteFunctions.getExecuteSingleFunctions(this, runExecutionData, runIndex, connectionInputData, inputData, node, itemIndex, additionalData, mode);
const thisArgs = nodeExecuteFunctions.getExecuteSingleFunctions(
this,
runExecutionData,
runIndex,
connectionInputData,
inputData,
node,
itemIndex,
additionalData,
mode,
);
returnPromises.push(nodeType.executeSingle!.call(thisArgs));
returnPromises.push(nodeType.executeSingle.call(thisArgs));
}
if (returnPromises.length === 0) {
@@ -990,21 +1118,41 @@ export class Workflow {
return [promiseResults];
}
} else if (nodeType.execute) {
const thisArgs = nodeExecuteFunctions.getExecuteFunctions(this, runExecutionData, runIndex, connectionInputData, inputData, node, additionalData, mode);
const thisArgs = nodeExecuteFunctions.getExecuteFunctions(
this,
runExecutionData,
runIndex,
connectionInputData,
inputData,
node,
additionalData,
mode,
);
return nodeType.execute.call(thisArgs);
} else if (nodeType.poll) {
if (mode === 'manual') {
// In manual mode run the poll function
const thisArgs = nodeExecuteFunctions.getExecutePollFunctions(this, node, additionalData, mode, 'manual');
const thisArgs = nodeExecuteFunctions.getExecutePollFunctions(
this,
node,
additionalData,
mode,
'manual',
);
return nodeType.poll.call(thisArgs);
} else {
// In any other mode pass data through as it already contains the result of the poll
return inputData.main as INodeExecutionData[][];
}
// In any other mode pass data through as it already contains the result of the poll
return inputData.main as INodeExecutionData[][];
} else if (nodeType.trigger) {
if (mode === 'manual') {
// In manual mode start the trigger
const triggerResponse = await this.runTrigger(node, nodeExecuteFunctions.getExecuteTriggerFunctions, additionalData, mode, 'manual');
const triggerResponse = await this.runTrigger(
node,
nodeExecuteFunctions.getExecuteTriggerFunctions,
additionalData,
mode,
'manual',
);
if (triggerResponse === undefined) {
return null;
@@ -1027,11 +1175,9 @@ export class Workflow {
}
return response;
} else {
// For trigger nodes in any mode except "manual" do we simply pass the data through
return inputData.main as INodeExecutionData[][];
}
// For trigger nodes in any mode except "manual" do we simply pass the data through
return inputData.main as INodeExecutionData[][];
} else if (nodeType.webhook) {
// For webhook nodes always simply pass the data through
return inputData.main as INodeExecutionData[][];

View File

@@ -1,3 +1,12 @@
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-param-reassign */
/* eslint-disable @typescript-eslint/no-this-alias */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable no-prototype-builtins */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
// eslint-disable-next-line import/no-cycle
import {
IDataObject,
INodeExecutionData,
@@ -9,26 +18,44 @@ import {
NodeParameterValue,
Workflow,
WorkflowExecuteMode,
} from './';
} from '.';
export class WorkflowDataProxy {
private workflow: Workflow;
private runExecutionData: IRunExecutionData | null;
private defaultReturnRunIndex: number;
private runIndex: number;
private itemIndex: number;
private activeNodeName: string;
private connectionInputData: INodeExecutionData[];
private siblingParameters: INodeParameters;
private mode: WorkflowExecuteMode;
private selfData: IDataObject;
private additionalKeys: IWorkflowDataProxyAdditionalKeys;
constructor(workflow: Workflow, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], siblingParameters: INodeParameters, mode: WorkflowExecuteMode, additionalKeys: IWorkflowDataProxyAdditionalKeys, defaultReturnRunIndex = -1, selfData = {}) {
constructor(
workflow: Workflow,
runExecutionData: IRunExecutionData | null,
runIndex: number,
itemIndex: number,
activeNodeName: string,
connectionInputData: INodeExecutionData[],
siblingParameters: INodeParameters,
mode: WorkflowExecuteMode,
additionalKeys: IWorkflowDataProxyAdditionalKeys,
defaultReturnRunIndex = -1,
selfData = {},
) {
this.workflow = workflow;
this.runExecutionData = runExecutionData;
this.defaultReturnRunIndex = defaultReturnRunIndex;
@@ -42,8 +69,6 @@ export class WorkflowDataProxy {
this.additionalKeys = additionalKeys;
}
/**
* Returns a proxy which allows to query context data of a given node
*
@@ -56,46 +81,51 @@ export class WorkflowDataProxy {
const that = this;
const node = this.workflow.nodes[nodeName];
return new Proxy({}, {
ownKeys(target) {
if (Reflect.ownKeys(target).length === 0) {
// Target object did not get set yet
Object.assign(target, NodeHelpers.getContext(that.runExecutionData!, 'node', node));
}
return new Proxy(
{},
{
ownKeys(target) {
if (Reflect.ownKeys(target).length === 0) {
// Target object did not get set yet
Object.assign(target, NodeHelpers.getContext(that.runExecutionData!, 'node', node));
}
return Reflect.ownKeys(target);
return Reflect.ownKeys(target);
},
get(target, name, receiver) {
// eslint-disable-next-line no-param-reassign
name = name.toString();
const contextData = NodeHelpers.getContext(that.runExecutionData!, 'node', node);
if (!contextData.hasOwnProperty(name)) {
// Parameter does not exist on node
throw new Error(`Could not find parameter "${name}" on context of node "${nodeName}"`);
}
return contextData[name];
},
},
get(target, name, receiver) {
name = name.toString();
const contextData = NodeHelpers.getContext(that.runExecutionData!, 'node', node);
if (!contextData.hasOwnProperty(name)) {
// Parameter does not exist on node
throw new Error(`Could not find parameter "${name}" on context of node "${nodeName}"`);
}
return contextData[name];
},
});
);
}
private selfGetter() {
const that = this;
return new Proxy({}, {
ownKeys(target) {
return Reflect.ownKeys(target);
return new Proxy(
{},
{
ownKeys(target) {
return Reflect.ownKeys(target);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
get(target, name, receiver) {
name = name.toString();
return that.selfData[name];
},
},
get(target, name, receiver) {
name = name.toString();
return that.selfData[name];
},
});
);
}
/**
* Returns a proxy which allows to query parameter data of a given node
*
@@ -115,12 +145,15 @@ export class WorkflowDataProxy {
get(target, name, receiver) {
name = name.toString();
let returnValue: INodeParameters | NodeParameterValue | NodeParameterValue[] | INodeParameters[];
let returnValue:
| INodeParameters
| NodeParameterValue
| NodeParameterValue[]
| INodeParameters[];
if (name[0] === '&') {
const key = name.slice(1);
if (!that.siblingParameters.hasOwnProperty(key)) {
throw new Error(`Could not find sibling parameter "${key}" on node "${nodeName}"`);
}
returnValue = that.siblingParameters[key];
} else {
@@ -134,7 +167,16 @@ export class WorkflowDataProxy {
if (typeof returnValue === 'string' && returnValue.charAt(0) === '=') {
// The found value is an expression so resolve it
return that.workflow.expression.getParameterValue(returnValue, that.runExecutionData, that.runIndex, that.itemIndex, that.activeNodeName, that.connectionInputData, that.mode, that.additionalKeys);
return that.workflow.expression.getParameterValue(
returnValue,
that.runExecutionData,
that.runIndex,
that.itemIndex,
that.activeNodeName,
that.connectionInputData,
that.mode,
that.additionalKeys,
);
}
return returnValue;
@@ -142,7 +184,6 @@ export class WorkflowDataProxy {
});
}
/**
* Returns the node ExecutionData
*
@@ -154,11 +195,16 @@ export class WorkflowDataProxy {
* @returns {INodeExecutionData[]}
* @memberof WorkflowDataProxy
*/
private getNodeExecutionData(nodeName: string, shortSyntax = false, outputIndex?: number, runIndex?: number): INodeExecutionData[] {
private getNodeExecutionData(
nodeName: string,
shortSyntax = false,
outputIndex?: number,
runIndex?: number,
): INodeExecutionData[] {
const that = this;
let executionData: INodeExecutionData[];
if (shortSyntax === false) {
if (!shortSyntax) {
// Long syntax got used to return data from node in path
if (that.runExecutionData === null) {
@@ -170,7 +216,8 @@ export class WorkflowDataProxy {
}
runIndex = runIndex === undefined ? that.defaultReturnRunIndex : runIndex;
runIndex = runIndex === -1 ? (that.runExecutionData.resultData.runData[nodeName].length -1) : runIndex;
runIndex =
runIndex === -1 ? that.runExecutionData.resultData.runData[nodeName].length - 1 : runIndex;
if (that.runExecutionData.resultData.runData[nodeName].length < runIndex) {
throw new Error(`No execution data found for run "${runIndex}" of node "${nodeName}"`);
@@ -187,10 +234,17 @@ export class WorkflowDataProxy {
// 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');
// eslint-disable-next-line @typescript-eslint/no-shadow
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.`);
throw new Error(
`The node "${that.activeNodeName}" is not connected with node "${nodeName}" so no data can get returned from it.`,
);
}
}
@@ -199,7 +253,9 @@ export class WorkflowDataProxy {
}
if (taskData.main.length < outputIndex) {
throw new Error(`No data found from "main" input with index "${outputIndex}" via which node is connected with.`);
throw new Error(
`No data found from "main" input with index "${outputIndex}" via which node is connected with.`,
);
}
executionData = taskData.main[outputIndex] as INodeExecutionData[];
@@ -217,7 +273,6 @@ export class WorkflowDataProxy {
return executionData;
}
/**
* Returns a proxy which allows to query data of a given node
*
@@ -228,7 +283,6 @@ export class WorkflowDataProxy {
* @memberof WorkflowDataGetter
*/
private nodeDataGetter(nodeName: string, shortSyntax = false) {
const that = this;
const node = this.workflow.nodes[nodeName];
@@ -236,65 +290,69 @@ export class WorkflowDataProxy {
throw new Error(`The node "${nodeName}" does not exist!`);
}
return new Proxy({}, {
get(target, name, receiver) {
name = name.toString();
return new Proxy(
{},
{
get(target, name, receiver) {
name = name.toString();
if (['binary', 'data', 'json'].includes(name)) {
const executionData = that.getNodeExecutionData(nodeName, shortSyntax, undefined);
if (['binary', 'data', 'json'].includes(name)) {
const executionData = that.getNodeExecutionData(nodeName, shortSyntax, undefined);
if (executionData.length <= that.itemIndex) {
throw new Error(`No data found for item-index: "${that.itemIndex}"`);
}
if (executionData.length <= that.itemIndex) {
throw new Error(`No data found for item-index: "${that.itemIndex}"`);
}
if (['data', 'json'].includes(name as string)) {
// JSON-Data
return executionData[that.itemIndex].json;
} else if (name === 'binary') {
// Binary-Data
const returnData: IDataObject = {};
if (['data', 'json'].includes(name)) {
// JSON-Data
return executionData[that.itemIndex].json;
}
if (name === 'binary') {
// Binary-Data
const returnData: IDataObject = {};
if (!executionData[that.itemIndex].binary) {
return returnData;
}
const binaryKeyData = executionData[that.itemIndex].binary!;
for (const keyName of Object.keys(binaryKeyData)) {
returnData[keyName] = {};
const binaryData = binaryKeyData[keyName];
for (const propertyName in binaryData) {
if (propertyName === 'data') {
// Skip the data property
// eslint-disable-next-line no-continue
continue;
}
(returnData[keyName] as IDataObject)[propertyName] = binaryData[propertyName];
}
}
if (!executionData[that.itemIndex].binary) {
return returnData;
}
const binaryKeyData = executionData[that.itemIndex].binary!;
for (const keyName of Object.keys(binaryKeyData)) {
returnData[keyName] = {};
const binaryData = binaryKeyData[keyName];
for (const propertyName in binaryData) {
if (propertyName === 'data') {
// Skip the data property
continue;
}
(returnData[keyName] as IDataObject)[propertyName] = binaryData[propertyName];
}
} else if (name === 'context') {
return that.nodeContextGetter(nodeName);
} else if (name === 'parameter') {
// Get node parameter data
return that.nodeParameterGetter(nodeName);
} else if (name === 'runIndex') {
if (
that.runExecutionData === null ||
!that.runExecutionData.resultData.runData[nodeName]
) {
return -1;
}
return returnData;
return that.runExecutionData.resultData.runData[nodeName].length - 1;
}
} else if (name === 'context') {
return that.nodeContextGetter(nodeName);
} else if (name === 'parameter') {
// Get node parameter data
return that.nodeParameterGetter(nodeName);
} else if (name === 'runIndex') {
if (that.runExecutionData === null || !that.runExecutionData.resultData.runData[nodeName]) {
return -1;
}
return that.runExecutionData.resultData.runData[nodeName].length - 1;
}
return Reflect.get(target, name, receiver);
return Reflect.get(target, name, receiver);
},
},
});
);
}
/**
* Returns a proxy to query data from the environment
*
@@ -303,15 +361,16 @@ export class WorkflowDataProxy {
* @memberof WorkflowDataGetter
*/
private envGetter() {
return new Proxy({}, {
get(target, name, receiver) {
return process.env[name.toString()];
return new Proxy(
{},
{
get(target, name, receiver) {
return process.env[name.toString()];
},
},
});
);
}
/**
* Returns a proxt to query data from the workflow
*
@@ -320,27 +379,24 @@ export class WorkflowDataProxy {
* @memberof WorkflowDataProxy
*/
private workflowGetter() {
const allowedValues = [
'active',
'id',
'name',
];
const allowedValues = ['active', 'id', 'name'];
const that = this;
return new Proxy({}, {
get(target, name, receiver) {
if (!allowedValues.includes(name.toString())) {
throw new Error(`The key "${name.toString()}" is not supported!`);
}
return new Proxy(
{},
{
get(target, name, receiver) {
if (!allowedValues.includes(name.toString())) {
throw new Error(`The key "${name.toString()}" is not supported!`);
}
// @ts-ignore
return that.workflow[name.toString()];
// @ts-ignore
return that.workflow[name.toString()];
},
},
});
);
}
/**
* Returns a proxy to query data of all nodes
*
@@ -350,15 +406,16 @@ export class WorkflowDataProxy {
*/
private nodeGetter() {
const that = this;
return new Proxy({}, {
get(target, name, receiver) {
return that.nodeDataGetter(name.toString());
return new Proxy(
{},
{
get(target, name, receiver) {
return that.nodeDataGetter(name.toString());
},
},
});
);
}
/**
* Returns the data proxy object which allows to query data from current run
*
@@ -374,11 +431,31 @@ export class WorkflowDataProxy {
$env: this.envGetter(),
$evaluateExpression: (expression: string, itemIndex?: number) => {
itemIndex = itemIndex || that.itemIndex;
return that.workflow.expression.getParameterValue('=' + expression, that.runExecutionData, that.runIndex, itemIndex, that.activeNodeName, that.connectionInputData, that.mode, that.additionalKeys);
return that.workflow.expression.getParameterValue(
`=${expression}`,
that.runExecutionData,
that.runIndex,
itemIndex,
that.activeNodeName,
that.connectionInputData,
that.mode,
that.additionalKeys,
);
},
$item: (itemIndex: number, runIndex?: number) => {
const defaultReturnRunIndex = runIndex === undefined ? -1 : runIndex;
const dataProxy = new WorkflowDataProxy(this.workflow, this.runExecutionData, this.runIndex, itemIndex, this.activeNodeName, this.connectionInputData, that.siblingParameters, that.mode, that.additionalKeys, defaultReturnRunIndex);
const dataProxy = new WorkflowDataProxy(
this.workflow,
this.runExecutionData,
this.runIndex,
itemIndex,
this.activeNodeName,
this.connectionInputData,
that.siblingParameters,
that.mode,
that.additionalKeys,
defaultReturnRunIndex,
);
return dataProxy.getDataProxy();
},
$items: (nodeName?: string, outputIndex?: number, runIndex?: number) => {
@@ -410,7 +487,8 @@ export class WorkflowDataProxy {
if (['$data', '$json'].includes(name as string)) {
// @ts-ignore
return that.nodeDataGetter(that.activeNodeName, true).json;
} else if (name === '$binary') {
}
if (name === '$binary') {
// @ts-ignore
return that.nodeDataGetter(that.activeNodeName, true).binary;
}

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-cycle
import { INode } from '.';
/**
@@ -5,9 +6,10 @@ import { INode } from '.';
*/
export class WorkflowOperationError extends Error {
node: INode | undefined;
timestamp: number;
constructor(message: string, node?: INode, ) {
constructor(message: string, node?: INode) {
super(message);
this.name = this.constructor.name;
this.node = node;

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-cycle
import {
IWorkflowBase,
IWorkflowExecuteHooks,
@@ -5,16 +6,27 @@ import {
WorkflowExecuteMode,
} from './Interfaces';
export class WorkflowHooks {
mode: WorkflowExecuteMode;
workflowData: IWorkflowBase;
executionId: string;
sessionId?: string;
retryOf?: string;
hookFunctions: IWorkflowExecuteHooks;
constructor(hookFunctions: IWorkflowExecuteHooks, mode: WorkflowExecuteMode, executionId: string, workflowData: IWorkflowBase, optionalParameters?: IWorkflowHooksOptionalParameters) {
constructor(
hookFunctions: IWorkflowExecuteHooks,
mode: WorkflowExecuteMode,
executionId: string,
workflowData: IWorkflowBase,
optionalParameters?: IWorkflowHooksOptionalParameters,
) {
// eslint-disable-next-line no-param-reassign, @typescript-eslint/prefer-nullish-coalescing
optionalParameters = optionalParameters || {};
this.hookFunctions = hookFunctions;
@@ -25,12 +37,15 @@ export class WorkflowHooks {
this.retryOf = optionalParameters.retryOf;
}
async executeHookFunctions(hookName: string, parameters: any[]) { // tslint:disable-line:no-any
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
async executeHookFunctions(hookName: string, parameters: any[]) {
// tslint:disable-line:no-any
if (this.hookFunctions[hookName] !== undefined && Array.isArray(this.hookFunctions[hookName])) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, no-restricted-syntax
for (const hookFunction of this.hookFunctions[hookName]!) {
// eslint-disable-next-line no-await-in-loop
await hookFunction.apply(this, parameters);
}
}
}
}

View File

@@ -1,3 +1,8 @@
/* eslint-disable import/no-cycle */
import * as LoggerProxy from './LoggerProxy';
import * as NodeHelpers from './NodeHelpers';
import * as ObservableObject from './ObservableObject';
export * from './Interfaces';
export * from './Expression';
export * from './NodeErrors';
@@ -5,12 +10,4 @@ export * from './Workflow';
export * from './WorkflowDataProxy';
export * from './WorkflowErrors';
export * from './WorkflowHooks';
import * as LoggerProxy from './LoggerProxy';
import * as NodeHelpers from './NodeHelpers';
import * as ObservableObject from './ObservableObject';
export {
LoggerProxy,
NodeHelpers,
ObservableObject,
};
export { LoggerProxy, NodeHelpers, ObservableObject };

View File

@@ -1,15 +1,10 @@
import {
INodeType,
INodeTypeData,
INodeTypes,
} from '../src';
import { INodeType, INodeTypeData, INodeTypes } from '../src';
export interface INodeTypesObject {
[key: string]: INodeType;
}
class NodeTypesClass implements INodeTypes {
nodeTypes: INodeTypeData = {
'test.set': {
sourcePath: '',
@@ -96,7 +91,7 @@ class NodeTypesClass implements INodeTypes {
},
};
async init(nodeTypes: INodeTypeData): Promise<void> { }
async init(nodeTypes: INodeTypeData): Promise<void> {}
getAll(): INodeType[] {
return Object.values(this.nodeTypes).map((data) => data.type);

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,6 @@
import {
IDataObject,
ObservableObject,
} from '../src';
import { IDataObject, ObservableObject } from '../src';
describe('ObservableObject', () => {
test('should recognize that item on parent level got added (init empty)', () => {
const testObject = ObservableObject.create({});
expect(testObject.__dataChanged).toBeFalsy();
@@ -76,7 +71,9 @@ describe('ObservableObject', () => {
});
test('should recognize that item on first child level changed if it is now empty and option "ignoreEmptyOnFirstChild" === true (init data exists)', () => {
const testObject = ObservableObject.create({ a: { b: 1 } }, undefined, { ignoreEmptyOnFirstChild: true });
const testObject = ObservableObject.create({ a: { b: 1 } }, undefined, {
ignoreEmptyOnFirstChild: true,
});
expect(testObject.__dataChanged).toBeFalsy();
expect((testObject.a! as IDataObject).b).toEqual(1);
testObject.a = {};
@@ -85,7 +82,9 @@ describe('ObservableObject', () => {
});
test('should recognize that item on first child level changed if it is now empty and option "ignoreEmptyOnFirstChild" === false (init data exists)', () => {
const testObject = ObservableObject.create({ a: { b: 1 } }, undefined, { ignoreEmptyOnFirstChild: false });
const testObject = ObservableObject.create({ a: { b: 1 } }, undefined, {
ignoreEmptyOnFirstChild: false,
});
expect(testObject.__dataChanged).toBeFalsy();
expect((testObject.a! as IDataObject).b).toEqual(1);
testObject.a = {};
@@ -105,7 +104,7 @@ describe('ObservableObject', () => {
test('should recognize that item on second child level changed (init data exists)', () => {
const testObject = ObservableObject.create({ a: { b: { c: 1 } } });
expect(testObject.__dataChanged).toBeFalsy();
expect((testObject.a! as IDataObject).b).toEqual({c: 1});
expect((testObject.a! as IDataObject).b).toEqual({ c: 1 });
expect(((testObject.a! as IDataObject).b! as IDataObject).c).toEqual(1);
((testObject.a! as IDataObject).b! as IDataObject).c = 2;
expect(testObject.__dataChanged).toBeTruthy();
@@ -123,7 +122,9 @@ describe('ObservableObject', () => {
});
test('should recognize that item on parent level got deleted even with and option "ignoreEmptyOnFirstChild" === true (init data exists)', () => {
const testObject = ObservableObject.create({ a: 1 }, undefined, { ignoreEmptyOnFirstChild: true });
const testObject = ObservableObject.create({ a: 1 }, undefined, {
ignoreEmptyOnFirstChild: true,
});
expect(testObject.__dataChanged).toBeFalsy();
expect(testObject.a!).toEqual(1);
delete testObject.a;
@@ -152,11 +153,6 @@ describe('ObservableObject', () => {
expect((testObject.a! as IDataObject).b).toEqual({ c: 2 });
});
// test('xxxxxx', () => {
// const testObject = ObservableObject.create({ a: { } }, undefined, { ignoreEmptyOnFirstChild: true });
// expect(testObject.__dataChanged).toBeFalsy();
@@ -167,12 +163,9 @@ describe('ObservableObject', () => {
// // expect(testObject.a).toEqual({});
// // expect((testObject.a! as DataObject).b).toEqual({ c: 1 });
// // expect(((testObject.a! as DataObject).b! as DataObject).c).toEqual(1);
// // ((testObject.a! as DataObject).b! as DataObject).c = 2;
// // expect((testObject.a! as DataObject).b).toEqual({ c: 2 });
// });
});

View File

@@ -16,11 +16,8 @@ interface StubNode {
parameters: INodeParameters;
}
describe('Workflow', () => {
describe('renameNodeInExpressions', () => {
const tests = [
{
description: 'do nothing if there is no expression',
@@ -43,13 +40,13 @@ describe('Workflow', () => {
currentName: 'Node1',
newName: 'NewName',
parameters: {
value1: '={{$node.Node1.data.value1 + \'Node1\'}}',
value2: '={{$node.Node1.data.value2 + \' - \' + $node.Node1.data.value2}}',
value1: "={{$node.Node1.data.value1 + 'Node1'}}",
value2: "={{$node.Node1.data.value2 + ' - ' + $node.Node1.data.value2}}",
},
},
output: {
value1: '={{$node.NewName.data.value1 + \'Node1\'}}',
value2: '={{$node.NewName.data.value2 + \' - \' + $node.NewName.data.value2}}',
value1: "={{$node.NewName.data.value1 + 'Node1'}}",
value2: "={{$node.NewName.data.value2 + ' - ' + $node.NewName.data.value2}}",
},
},
{
@@ -59,27 +56,31 @@ describe('Workflow', () => {
newName: 'NewName',
parameters: {
value1: '={{$node["Node1"]["data"]["value1"] + \'Node1\'}}',
value2: '={{$node["Node1"]["data"]["value2"] + \' - \' + $node["Node1"]["data"]["value2"]}}',
value2:
'={{$node["Node1"]["data"]["value2"] + \' - \' + $node["Node1"]["data"]["value2"]}}',
},
},
output: {
value1: '={{$node["NewName"]["data"]["value1"] + \'Node1\'}}',
value2: '={{$node["NewName"]["data"]["value2"] + \' - \' + $node["NewName"]["data"]["value2"]}}',
value2:
'={{$node["NewName"]["data"]["value2"] + \' - \' + $node["NewName"]["data"]["value2"]}}',
},
},
{
description: 'should work with [\'nodeName\']',
description: "should work with ['nodeName']",
input: {
currentName: 'Node1',
newName: 'NewName',
parameters: {
value1: '={{$node[\'Node1\'][\'data\'][\'value1\'] + \'Node1\'}}',
value2: '={{$node[\'Node1\'][\'data\'][\'value2\'] + \' - \' + $node[\'Node1\'][\'data\'][\'value2\']}}',
value1: "={{$node['Node1']['data']['value1'] + 'Node1'}}",
value2:
"={{$node['Node1']['data']['value2'] + ' - ' + $node['Node1']['data']['value2']}}",
},
},
output: {
value1: '={{$node[\'NewName\'][\'data\'][\'value1\'] + \'Node1\'}}',
value2: '={{$node[\'NewName\'][\'data\'][\'value2\'] + \' - \' + $node[\'NewName\'][\'data\'][\'value2\']}}',
value1: "={{$node['NewName']['data']['value1'] + 'Node1'}}",
value2:
"={{$node['NewName']['data']['value2'] + ' - ' + $node['NewName']['data']['value2']}}",
},
},
{
@@ -88,22 +89,22 @@ describe('Workflow', () => {
currentName: 'Node1',
newName: 'NewName',
parameters: {
level1a: '={{$node.Node1.data.value1 + \'Node1\'}}',
level1a: "={{$node.Node1.data.value1 + 'Node1'}}",
level1b: [
{
value2a: '={{$node.Node1.data.value1 + \'Node1\'}}',
value2b: '={{$node.Node1.data.value1 + \'Node1\'}}',
value2a: "={{$node.Node1.data.value1 + 'Node1'}}",
value2b: "={{$node.Node1.data.value1 + 'Node1'}}",
},
],
level1c: {
value2a: {
value3a: '={{$node.Node1.data.value1 + \'Node1\'}}',
value3a: "={{$node.Node1.data.value1 + 'Node1'}}",
value3b: [
{
value4a: '={{$node.Node1.data.value1 + \'Node1\'}}',
value4a: "={{$node.Node1.data.value1 + 'Node1'}}",
value4b: {
value5a: '={{$node.Node1.data.value1 + \'Node1\'}}',
value5b: '={{$node.Node1.data.value1 + \'Node1\'}}',
value5a: "={{$node.Node1.data.value1 + 'Node1'}}",
value5b: "={{$node.Node1.data.value1 + 'Node1'}}",
},
},
],
@@ -112,22 +113,22 @@ describe('Workflow', () => {
} as INodeParameters,
},
output: {
level1a: '={{$node.NewName.data.value1 + \'Node1\'}}',
level1a: "={{$node.NewName.data.value1 + 'Node1'}}",
level1b: [
{
value2a: '={{$node.NewName.data.value1 + \'Node1\'}}',
value2b: '={{$node.NewName.data.value1 + \'Node1\'}}',
value2a: "={{$node.NewName.data.value1 + 'Node1'}}",
value2b: "={{$node.NewName.data.value1 + 'Node1'}}",
},
],
level1c: {
value2a: {
value3a: '={{$node.NewName.data.value1 + \'Node1\'}}',
value3a: "={{$node.NewName.data.value1 + 'Node1'}}",
value3b: [
{
value4a: '={{$node.NewName.data.value1 + \'Node1\'}}',
value4a: "={{$node.NewName.data.value1 + 'Node1'}}",
value4b: {
value5a: '={{$node.NewName.data.value1 + \'Node1\'}}',
value5b: '={{$node.NewName.data.value1 + \'Node1\'}}',
value5a: "={{$node.NewName.data.value1 + 'Node1'}}",
value5b: "={{$node.NewName.data.value1 + 'Node1'}}",
},
},
],
@@ -142,15 +143,17 @@ describe('Workflow', () => {
for (const testData of tests) {
test(testData.description, () => {
const result = workflow.renameNodeInExpressions(testData.input.parameters, testData.input.currentName, testData.input.newName);
const result = workflow.renameNodeInExpressions(
testData.input.parameters,
testData.input.currentName,
testData.input.newName,
);
expect(result).toEqual(testData.output);
});
}
});
describe('renameNode', () => {
const tests = [
{
description: 'rename node without connections',
@@ -507,8 +510,8 @@ describe('Workflow', () => {
{
name: 'Node2',
parameters: {
value1: '={{$node.Node1.data.value1 + \'Node1\'}}',
value2: '={{$node.Node1.data.value2 + \' - \' + $node.Node1.data.value2}}',
value1: "={{$node.Node1.data.value1 + 'Node1'}}",
value2: "={{$node.Node1.data.value2 + ' - ' + $node.Node1.data.value2}}",
},
},
],
@@ -526,8 +529,8 @@ describe('Workflow', () => {
{
name: 'Node2',
parameters: {
value1: '={{$node.Node1New.data.value1 + \'Node1\'}}',
value2: '={{$node.Node1New.data.value2 + \' - \' + $node.Node1New.data.value2}}',
value1: "={{$node.Node1New.data.value1 + 'Node1'}}",
value2: "={{$node.Node1New.data.value2 + ' - ' + $node.Node1New.data.value2}}",
},
},
],
@@ -545,10 +548,7 @@ describe('Workflow', () => {
parameters: stubData.parameters,
type: 'test.set',
typeVersion: 1,
position: [
100,
100,
],
position: [100, 100],
};
}
@@ -559,13 +559,17 @@ describe('Workflow', () => {
for (const testData of tests) {
test(testData.description, () => {
executeNodes = [];
for (const node of testData.input.nodes) {
executeNodes.push(createNodeData(node));
}
workflow = new Workflow({ nodes: executeNodes, connections: testData.input.connections as IConnections, active: false, nodeTypes });
workflow = new Workflow({
nodes: executeNodes,
connections: testData.input.connections as IConnections,
active: false,
nodeTypes,
});
workflow.renameNode(testData.input.currentName, testData.input.newName);
resultNodes = {};
@@ -577,12 +581,9 @@ describe('Workflow', () => {
expect(workflow.connectionsBySourceNode).toEqual(testData.output.connections);
});
}
});
describe('getParameterValue', () => {
const tests = [
{
description: 'read simple not expression value',
@@ -639,7 +640,8 @@ describe('Workflow', () => {
},
},
{
description: 'read data from node-output-data with with long "$node.{NODE}.data" syntax add value and append text',
description:
'read data from node-output-data with with long "$node.{NODE}.data" syntax add value and append text',
input: {
Node1: {
parameters: {
@@ -657,7 +659,8 @@ describe('Workflow', () => {
},
},
{
description: 'read deep-data from node-output-data with with long "$node.{NODE}.data" syntax with JavaScript Code',
description:
'read deep-data from node-output-data with with long "$node.{NODE}.data" syntax with JavaScript Code',
input: {
Node1: {
parameters: {
@@ -727,7 +730,8 @@ describe('Workflow', () => {
},
},
{
description: 'read deep-data from node-output-data with with long "$node.{NODE}.data" syntax',
description:
'read deep-data from node-output-data with with long "$node.{NODE}.data" syntax',
input: {
Node1: {
parameters: {
@@ -753,7 +757,8 @@ describe('Workflow', () => {
},
},
{
description: 'read binary-string-data from incoming-node-data with with short "$binary" syntax',
description:
'read binary-string-data from incoming-node-data with with short "$binary" syntax',
input: {
Node1: {
parameters: {
@@ -779,7 +784,8 @@ describe('Workflow', () => {
},
},
{
description: 'read binary-string-data from node-output-data with with long "$node.{NODE}.binary" syntax',
description:
'read binary-string-data from node-output-data with with long "$node.{NODE}.binary" syntax',
input: {
Node1: {
parameters: {
@@ -879,7 +885,8 @@ describe('Workflow', () => {
},
},
{
description: 'return resolved value when referencing another property with expression (long "$node.{NODE}.data" syntax)',
description:
'return resolved value when referencing another property with expression (long "$node.{NODE}.data" syntax)',
input: {
Node1: {
parameters: {
@@ -899,7 +906,8 @@ describe('Workflow', () => {
},
},
{
description: 'return resolved value when referencing another property with expression (short "data" syntax)',
description:
'return resolved value when referencing another property with expression (short "data" syntax)',
input: {
Node1: {
parameters: {
@@ -919,7 +927,8 @@ describe('Workflow', () => {
},
},
{
description: 'return resolved value when referencing another property with expression when a node has spaces (long "$node["{NODE}"].parameter" syntax)',
description:
'return resolved value when referencing another property with expression when a node has spaces (long "$node["{NODE}"].parameter" syntax)',
input: {
Node1: {
parameters: {
@@ -937,7 +946,8 @@ describe('Workflow', () => {
},
},
{
description: 'return resolved value when referencing another property with expression on another node (long "$node["{NODE}"].parameter" syntax)',
description:
'return resolved value when referencing another property with expression on another node (long "$node["{NODE}"].parameter" syntax)',
input: {
Node1: {
parameters: {
@@ -987,75 +997,67 @@ describe('Workflow', () => {
// },
];
const nodeTypes = Helpers.NodeTypes();
for (const testData of tests) {
test(testData.description, () => {
const nodes: INode[] = [
{
"name": "Node1",
"parameters": testData.input.Node1.parameters,
"type": "test.set",
"typeVersion": 1,
"position": [
100,
100,
],
name: 'Node1',
parameters: testData.input.Node1.parameters,
type: 'test.set',
typeVersion: 1,
position: [100, 100],
},
{
"name": "Node2",
"parameters": testData.input.Node2.parameters,
"type": "test.set",
"typeVersion": 1,
"position": [
100,
200,
],
name: 'Node2',
parameters: testData.input.Node2.parameters,
type: 'test.set',
typeVersion: 1,
position: [100, 200],
},
{
"name": "Node3",
name: 'Node3',
// @ts-ignore
"parameters": testData.input.hasOwnProperty('Node3') ? testData.input.Node3.parameters : {},
"type": "test.set",
"typeVersion": 1,
"position": [
100,
300,
],
parameters: testData.input.hasOwnProperty('Node3')
? // @ts-ignore
testData.input.Node3.parameters
: {},
type: 'test.set',
typeVersion: 1,
position: [100, 300],
},
{
"name": "Node 4 with spaces",
name: 'Node 4 with spaces',
// @ts-ignore
"parameters": testData.input.hasOwnProperty('Node4') ? testData.input.Node4.parameters : {},
"type": "test.set",
"typeVersion": 1,
"position": [
100,
400,
],
parameters: testData.input.hasOwnProperty('Node4')
? // @ts-ignore
testData.input.Node4.parameters
: {},
type: 'test.set',
typeVersion: 1,
position: [100, 400],
},
];
const connections: IConnections = {
"Node1": {
"main": [
Node1: {
main: [
[
{
"node": "Node2",
"type": "main",
"index": 0,
node: 'Node2',
type: 'main',
index: 0,
},
],
],
},
"Node2": {
"main": [
Node2: {
main: [
[
{
"node": "Node3",
"type": "main",
"index": 0,
node: 'Node3',
type: 'main',
index: 0,
},
],
],
@@ -1093,19 +1095,29 @@ describe('Workflow', () => {
const itemIndex = 0;
const runIndex = 0;
const connectionInputData: INodeExecutionData[] = runExecutionData.resultData.runData!['Node1']![0]!.data!.main[0]!;
const connectionInputData: INodeExecutionData[] =
runExecutionData.resultData.runData!['Node1']![0]!.data!.main[0]!;
for (const parameterName of Object.keys(testData.output)) {
const parameterValue = nodes.find((node) => node.name === activeNodeName)!.parameters[parameterName];
const result = workflow.expression.getParameterValue(parameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, 'manual', {});
const parameterValue = nodes.find((node) => node.name === activeNodeName)!.parameters[
parameterName
];
const result = workflow.expression.getParameterValue(
parameterValue,
runExecutionData,
runIndex,
itemIndex,
activeNodeName,
connectionInputData,
'manual',
{},
);
// @ts-ignore
expect(result).toEqual(testData.output[parameterName]);
}
});
}
// test('should be able to set and read key data without initial data set', () => {
// const nodes: Node[] = [
@@ -1175,7 +1187,6 @@ describe('Workflow', () => {
// ]
// };
// const itemIndex = 0;
// const connectionInputData: NodeExecutionData[] = runData!['Node1']![0]!.data!.main[0]!;
@@ -1184,34 +1195,29 @@ describe('Workflow', () => {
// expect(result).toEqual('outputSet1');
// });
test('should also resolve all child parameters when the parent get requested', () => {
const nodeTypes = Helpers.NodeTypes();
const nodes: INode[] = [
{
"name": "Node1",
"parameters": {
"values": {
"string": [
name: 'Node1',
parameters: {
values: {
string: [
{
"name": "name1",
"value": "value1",
name: 'name1',
value: 'value1',
},
{
"name": "name2",
"value": "={{$parameter.values.string[0].value}}A",
name: 'name2',
value: '={{$parameter.values.string[0].value}}A',
},
],
},
},
"type": "test.setMulti",
"typeVersion": 1,
"position": [
100,
100,
],
type: 'test.setMulti',
typeVersion: 1,
position: [100, 100],
},
];
const connections: IConnections = {};
@@ -1243,11 +1249,23 @@ describe('Workflow', () => {
const itemIndex = 0;
const runIndex = 0;
const connectionInputData: INodeExecutionData[] = runExecutionData.resultData.runData!['Node1']![0]!.data!.main[0]!;
const connectionInputData: INodeExecutionData[] =
runExecutionData.resultData.runData!['Node1']![0]!.data!.main[0]!;
const parameterName = 'values';
const parameterValue = nodes.find((node) => node.name === activeNodeName)!.parameters[parameterName];
const result = workflow.expression.getParameterValue(parameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, 'manual', {});
const parameterValue = nodes.find((node) => node.name === activeNodeName)!.parameters[
parameterName
];
const result = workflow.expression.getParameterValue(
parameterValue,
runExecutionData,
runIndex,
itemIndex,
activeNodeName,
connectionInputData,
'manual',
{},
);
expect(result).toEqual({
string: [
@@ -1261,9 +1279,6 @@ describe('Workflow', () => {
},
],
});
});
});
});