2019-06-23 03:35:23 -07:00
|
|
|
import {
|
2020-10-22 06:46:03 -07:00
|
|
|
BINARY_ENCODING,
|
2019-06-23 03:35:23 -07:00
|
|
|
IHookFunctions,
|
|
|
|
ILoadOptionsFunctions,
|
2020-01-13 18:46:58 -08:00
|
|
|
IResponseError,
|
2019-06-23 03:35:23 -07:00
|
|
|
IWorkflowSettings,
|
|
|
|
} from './';
|
|
|
|
|
|
|
|
import {
|
2020-01-13 18:46:58 -08:00
|
|
|
IAllExecuteFunctions,
|
2019-06-23 03:35:23 -07:00
|
|
|
IBinaryData,
|
|
|
|
IContextObject,
|
|
|
|
ICredentialDataDecryptedObject,
|
2021-01-24 04:33:57 -08:00
|
|
|
ICredentialsExpressionResolveValues,
|
2019-06-23 03:35:23 -07:00
|
|
|
IDataObject,
|
|
|
|
IExecuteFunctions,
|
|
|
|
IExecuteSingleFunctions,
|
2020-01-02 15:13:53 -08:00
|
|
|
IExecuteWorkflowInfo,
|
2019-06-23 03:35:23 -07:00
|
|
|
INode,
|
|
|
|
INodeExecutionData,
|
|
|
|
INodeParameters,
|
|
|
|
INodeType,
|
2020-10-22 06:46:03 -07:00
|
|
|
IOAuth2Options,
|
2019-12-31 12:19:37 -08:00
|
|
|
IPollFunctions,
|
2019-06-23 03:35:23 -07:00
|
|
|
IRunExecutionData,
|
|
|
|
ITaskDataConnections,
|
|
|
|
ITriggerFunctions,
|
2019-07-12 02:33:18 -07:00
|
|
|
IWebhookData,
|
2019-06-23 03:35:23 -07:00
|
|
|
IWebhookDescription,
|
|
|
|
IWebhookFunctions,
|
2019-09-04 05:53:39 -07:00
|
|
|
IWorkflowDataProxyData,
|
2019-06-23 03:35:23 -07:00
|
|
|
IWorkflowExecuteAdditionalData,
|
2020-02-15 17:07:01 -08:00
|
|
|
IWorkflowMetadata,
|
2019-06-23 03:35:23 -07:00
|
|
|
NodeHelpers,
|
2021-04-16 09:33:36 -07:00
|
|
|
NodeOperationError,
|
2019-06-23 03:35:23 -07:00
|
|
|
NodeParameterValue,
|
|
|
|
Workflow,
|
2021-03-23 11:08:47 -07:00
|
|
|
WorkflowActivateMode,
|
2019-09-04 05:53:39 -07:00
|
|
|
WorkflowDataProxy,
|
2019-06-23 03:35:23 -07:00
|
|
|
WorkflowExecuteMode,
|
|
|
|
} from 'n8n-workflow';
|
|
|
|
|
2020-06-01 17:42:38 -07:00
|
|
|
import * as clientOAuth1 from 'oauth-1.0a';
|
2020-11-03 14:01:38 -08:00
|
|
|
import { Token } from 'oauth-1.0a';
|
2020-01-13 18:46:58 -08:00
|
|
|
import * as clientOAuth2 from 'client-oauth2';
|
2020-02-08 21:25:46 -08:00
|
|
|
import { get } from 'lodash';
|
|
|
|
import * as express from 'express';
|
2019-06-23 03:35:23 -07:00
|
|
|
import * as path from 'path';
|
2020-10-22 06:46:03 -07:00
|
|
|
import { OptionsWithUri, OptionsWithUrl } from 'request';
|
2019-06-23 03:35:23 -07:00
|
|
|
import * as requestPromise from 'request-promise-native';
|
2020-06-01 17:42:38 -07:00
|
|
|
import { createHmac } from 'crypto';
|
2020-07-08 00:40:47 -07:00
|
|
|
import { fromBuffer } from 'file-type';
|
|
|
|
import { lookup } from 'mime-types';
|
2021-05-01 20:43:01 -07:00
|
|
|
import {
|
|
|
|
LoggerProxy as Logger,
|
|
|
|
} from 'n8n-workflow';
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2021-04-17 06:15:33 -07:00
|
|
|
const requestPromiseWithDefaults = requestPromise.defaults({
|
|
|
|
timeout: 300000, // 5 minutes
|
|
|
|
});
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Takes a buffer and converts it into the format n8n uses. It encodes the binary data as
|
|
|
|
* base64 and adds metadata.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Buffer} binaryData
|
|
|
|
* @param {string} [filePath]
|
|
|
|
* @param {string} [mimeType]
|
|
|
|
* @returns {Promise<IBinaryData>}
|
|
|
|
*/
|
|
|
|
export async function prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData> {
|
|
|
|
if (!mimeType) {
|
2020-07-08 00:40:47 -07:00
|
|
|
// If no mime type is given figure it out
|
|
|
|
|
|
|
|
if (filePath) {
|
|
|
|
// Use file path to guess mime type
|
|
|
|
const mimeTypeLookup = lookup(filePath);
|
|
|
|
if (mimeTypeLookup) {
|
|
|
|
mimeType = mimeTypeLookup;
|
|
|
|
}
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2020-07-08 00:40:47 -07:00
|
|
|
if (!mimeType) {
|
|
|
|
// Use buffer to guess mime type
|
|
|
|
const fileTypeData = await fromBuffer(binaryData);
|
|
|
|
if (fileTypeData) {
|
|
|
|
mimeType = fileTypeData.mime;
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
2020-07-08 00:40:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!mimeType) {
|
|
|
|
// Fall back to text
|
|
|
|
mimeType = 'text/plain';
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const returnData: IBinaryData = {
|
|
|
|
mimeType,
|
|
|
|
// TODO: Should program it in a way that it does not have to converted to base64
|
|
|
|
// It should only convert to and from base64 when saved in database because
|
|
|
|
// of for example an error or when there is a wait node.
|
2020-10-22 06:46:03 -07:00
|
|
|
data: binaryData.toString(BINARY_ENCODING),
|
2019-06-23 03:35:23 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
if (filePath) {
|
|
|
|
if (filePath.includes('?')) {
|
|
|
|
// Remove maybe present query parameters
|
|
|
|
filePath = filePath.split('?').shift();
|
|
|
|
}
|
|
|
|
|
|
|
|
const filePathParts = path.parse(filePath as string);
|
|
|
|
|
2021-03-18 10:13:24 -07:00
|
|
|
if (filePathParts.dir !== '') {
|
|
|
|
returnData.directory = filePathParts.dir;
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
returnData.fileName = filePathParts.base;
|
|
|
|
|
|
|
|
// Remove the dot
|
|
|
|
const fileExtension = filePathParts.ext.slice(1);
|
|
|
|
if (fileExtension) {
|
|
|
|
returnData.fileExtension = fileExtension;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return returnData;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-01-13 18:46:58 -08:00
|
|
|
/**
|
|
|
|
* Makes a request using OAuth data for authentication
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {IAllExecuteFunctions} this
|
|
|
|
* @param {string} credentialsType
|
|
|
|
* @param {(OptionsWithUri | requestPromise.RequestPromiseOptions)} requestOptions
|
|
|
|
* @param {INode} node
|
|
|
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
2020-07-25 10:58:38 -07:00
|
|
|
*
|
2020-01-13 18:46:58 -08:00
|
|
|
* @returns
|
|
|
|
*/
|
2020-07-25 10:58:38 -07:00
|
|
|
export function requestOAuth2(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, node: INode, additionalData: IWorkflowExecuteAdditionalData, oAuth2Options?: IOAuth2Options) {
|
2020-01-13 18:46:58 -08:00
|
|
|
const credentials = this.getCredentials(credentialsType) as ICredentialDataDecryptedObject;
|
|
|
|
|
|
|
|
if (credentials === undefined) {
|
|
|
|
throw new Error('No credentials got returned!');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (credentials.oauthTokenData === undefined) {
|
|
|
|
throw new Error('OAuth credentials not connected!');
|
|
|
|
}
|
|
|
|
|
2020-02-08 21:25:46 -08:00
|
|
|
const oAuthClient = new clientOAuth2({
|
|
|
|
clientId: credentials.clientId as string,
|
|
|
|
clientSecret: credentials.clientSecret as string,
|
|
|
|
accessTokenUri: credentials.accessTokenUrl as string,
|
|
|
|
});
|
|
|
|
|
|
|
|
const oauthTokenData = credentials.oauthTokenData as clientOAuth2.Data;
|
2020-01-13 18:46:58 -08:00
|
|
|
|
2020-07-25 10:58:38 -07:00
|
|
|
const token = oAuthClient.createToken(get(oauthTokenData, oAuth2Options?.property as string) || oauthTokenData.accessToken, oauthTokenData.refreshToken, oAuth2Options?.tokenType || oauthTokenData.tokenType, oauthTokenData);
|
2020-01-13 18:46:58 -08:00
|
|
|
// Signs the request by adding authorization headers or query parameters depending
|
|
|
|
// on the token-type used.
|
|
|
|
const newRequestOptions = token.sign(requestOptions as clientOAuth2.RequestObject);
|
|
|
|
|
2020-09-16 00:16:06 -07:00
|
|
|
// If keep bearer is false remove the it from the authorization header
|
|
|
|
if (oAuth2Options?.keepBearer === false) {
|
|
|
|
//@ts-ignore
|
|
|
|
newRequestOptions?.headers?.Authorization = newRequestOptions?.headers?.Authorization.split(' ')[1];
|
|
|
|
}
|
|
|
|
|
2020-01-13 18:46:58 -08:00
|
|
|
return this.helpers.request!(newRequestOptions)
|
|
|
|
.catch(async (error: IResponseError) => {
|
2021-02-21 23:49:00 -08:00
|
|
|
const statusCodeReturned = oAuth2Options?.tokenExpiredStatusCode === undefined ? 401 : oAuth2Options?.tokenExpiredStatusCode;
|
|
|
|
|
|
|
|
if (error.statusCode === statusCodeReturned) {
|
2020-01-13 18:46:58 -08:00
|
|
|
// Token is probably not valid anymore. So try refresh it.
|
2020-07-25 10:58:38 -07:00
|
|
|
|
|
|
|
const tokenRefreshOptions: IDataObject = {};
|
|
|
|
|
|
|
|
if (oAuth2Options?.includeCredentialsOnRefreshOnBody) {
|
|
|
|
const body: IDataObject = {
|
|
|
|
client_id: credentials.clientId as string,
|
|
|
|
client_secret: credentials.clientSecret as string,
|
|
|
|
};
|
|
|
|
tokenRefreshOptions.body = body;
|
2020-12-12 08:00:57 -08:00
|
|
|
// Override authorization property so the credentails are not included in it
|
|
|
|
tokenRefreshOptions.headers = {
|
|
|
|
Authorization: '',
|
|
|
|
};
|
2020-07-25 10:58:38 -07:00
|
|
|
}
|
|
|
|
|
2021-05-01 20:43:01 -07:00
|
|
|
Logger.debug(`OAuth2 token for "${credentialsType}" used by node "${node.name}" expired. Should revalidate.`);
|
|
|
|
|
2020-07-25 10:58:38 -07:00
|
|
|
const newToken = await token.refresh(tokenRefreshOptions);
|
2020-01-13 18:46:58 -08:00
|
|
|
|
2021-05-01 20:43:01 -07:00
|
|
|
Logger.debug(`OAuth2 token for "${credentialsType}" used by node "${node.name}" has been renewed.`);
|
|
|
|
|
2020-02-08 21:25:46 -08:00
|
|
|
credentials.oauthTokenData = newToken.data;
|
2020-01-13 18:46:58 -08:00
|
|
|
|
|
|
|
// Find the name of the credentials
|
|
|
|
if (!node.credentials || !node.credentials[credentialsType]) {
|
|
|
|
throw new Error(`The node "${node.name}" does not have credentials of type "${credentialsType}"!`);
|
|
|
|
}
|
|
|
|
const name = node.credentials[credentialsType];
|
|
|
|
|
|
|
|
// Save the refreshed token
|
2020-02-08 21:25:46 -08:00
|
|
|
await additionalData.credentialsHelper.updateCredentials(name, credentialsType, credentials);
|
2020-01-13 18:46:58 -08:00
|
|
|
|
2021-05-01 20:43:01 -07:00
|
|
|
Logger.debug(`OAuth2 token for "${credentialsType}" used by node "${node.name}" has been saved to database successfully.`);
|
|
|
|
|
2020-01-13 18:46:58 -08:00
|
|
|
// Make the request again with the new token
|
|
|
|
const newRequestOptions = newToken.sign(requestOptions as clientOAuth2.RequestObject);
|
|
|
|
|
|
|
|
return this.helpers.request!(newRequestOptions);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unknown error so simply throw it
|
|
|
|
throw error;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-06-01 17:42:38 -07:00
|
|
|
/* Makes a request using OAuth1 data for authentication
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {IAllExecuteFunctions} this
|
|
|
|
* @param {string} credentialsType
|
|
|
|
* @param {(OptionsWithUrl | requestPromise.RequestPromiseOptions)} requestOptionså
|
|
|
|
* @returns
|
|
|
|
*/
|
2020-07-25 01:09:52 -07:00
|
|
|
export function requestOAuth1(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUrl | OptionsWithUri | requestPromise.RequestPromiseOptions) {
|
2020-06-01 17:42:38 -07:00
|
|
|
const credentials = this.getCredentials(credentialsType) as ICredentialDataDecryptedObject;
|
|
|
|
|
|
|
|
if (credentials === undefined) {
|
|
|
|
throw new Error('No credentials got returned!');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (credentials.oauthTokenData === undefined) {
|
|
|
|
throw new Error('OAuth credentials not connected!');
|
|
|
|
}
|
|
|
|
|
|
|
|
const oauth = new clientOAuth1({
|
|
|
|
consumer: {
|
|
|
|
key: credentials.consumerKey as string,
|
|
|
|
secret: credentials.consumerSecret as string,
|
|
|
|
},
|
|
|
|
signature_method: credentials.signatureMethod as string,
|
|
|
|
hash_function(base, key) {
|
|
|
|
const algorithm = (credentials.signatureMethod === 'HMAC-SHA1') ? 'sha1' : 'sha256';
|
|
|
|
return createHmac(algorithm, key)
|
|
|
|
.update(base)
|
|
|
|
.digest('base64');
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const oauthTokenData = credentials.oauthTokenData as IDataObject;
|
|
|
|
|
|
|
|
const token: Token = {
|
|
|
|
key: oauthTokenData.oauth_token as string,
|
|
|
|
secret: oauthTokenData.oauth_token_secret as string,
|
|
|
|
};
|
|
|
|
|
2020-11-03 14:01:38 -08:00
|
|
|
//@ts-ignore
|
|
|
|
requestOptions.data = { ...requestOptions.qs, ...requestOptions.form };
|
2020-06-01 17:42:38 -07:00
|
|
|
|
2020-12-10 01:39:11 -08:00
|
|
|
// Fixes issue that OAuth1 library only works with "url" property and not with "uri"
|
|
|
|
// @ts-ignore
|
|
|
|
if (requestOptions.uri && !requestOptions.url) {
|
|
|
|
// @ts-ignore
|
|
|
|
requestOptions.url = requestOptions.uri;
|
|
|
|
// @ts-ignore
|
|
|
|
delete requestOptions.uri;
|
|
|
|
}
|
|
|
|
|
2020-11-03 14:01:38 -08:00
|
|
|
//@ts-ignore
|
|
|
|
requestOptions.headers = oauth.toHeader(oauth.authorize(requestOptions, token));
|
2020-07-25 01:09:52 -07:00
|
|
|
|
2020-11-03 14:01:38 -08:00
|
|
|
return this.helpers.request!(requestOptions)
|
2020-06-01 17:42:38 -07:00
|
|
|
.catch(async (error: IResponseError) => {
|
|
|
|
// Unknown error so simply throw it
|
|
|
|
throw error;
|
|
|
|
});
|
|
|
|
}
|
2020-01-13 18:46:58 -08:00
|
|
|
|
|
|
|
|
2019-06-23 03:35:23 -07:00
|
|
|
/**
|
|
|
|
* Takes generic input data and brings it into the json format n8n uses.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {(IDataObject | IDataObject[])} jsonData
|
|
|
|
* @returns {INodeExecutionData[]}
|
|
|
|
*/
|
|
|
|
export function returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[] {
|
|
|
|
const returnData: INodeExecutionData[] = [];
|
|
|
|
|
|
|
|
if (!Array.isArray(jsonData)) {
|
|
|
|
jsonData = [jsonData];
|
|
|
|
}
|
|
|
|
|
|
|
|
jsonData.forEach((data) => {
|
|
|
|
returnData.push({ json: data });
|
|
|
|
});
|
|
|
|
|
|
|
|
return returnData;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the requested decrypted credentials if the node has access to them.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Workflow} workflow Workflow which requests the data
|
|
|
|
* @param {INode} node Node which request the data
|
|
|
|
* @param {string} type The credential type to return
|
|
|
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
|
|
|
* @returns {(ICredentialDataDecryptedObject | undefined)}
|
|
|
|
*/
|
2021-01-29 03:16:46 -08:00
|
|
|
export function getCredentials(workflow: Workflow, node: INode, type: string, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, runExecutionData?: IRunExecutionData | null, runIndex?: number, connectionInputData?: INodeExecutionData[], itemIndex?: number): ICredentialDataDecryptedObject | undefined {
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
|
|
// Get the NodeType as it has the information if the credentials are required
|
|
|
|
const nodeType = workflow.nodeTypes.getByName(node.type);
|
|
|
|
if (nodeType === undefined) {
|
2021-04-16 09:33:36 -07:00
|
|
|
throw new NodeOperationError(node, `Node type "${node.type}" is not known so can not get credentials!`);
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (nodeType.description.credentials === undefined) {
|
2021-04-16 09:33:36 -07:00
|
|
|
throw new NodeOperationError(node, `Node type "${node.type}" does not have any credentials defined!`);
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const nodeCredentialDescription = nodeType.description.credentials.find((credentialTypeDescription) => credentialTypeDescription.name === type);
|
|
|
|
if (nodeCredentialDescription === undefined) {
|
2021-04-16 09:33:36 -07:00
|
|
|
throw new NodeOperationError(node, `Node type "${node.type}" does not have any credentials of type "${type}" defined!`);
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
2020-05-04 21:07:19 -07:00
|
|
|
if (NodeHelpers.displayParameter(additionalData.currentNodeParameters || node.parameters, nodeCredentialDescription, node.parameters) === false) {
|
2019-06-23 03:35:23 -07:00
|
|
|
// Credentials should not be displayed so return undefined even if they would be defined
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if node has any credentials defined
|
|
|
|
if (!node.credentials || !node.credentials[type]) {
|
|
|
|
// If none are defined check if the credentials are required or not
|
|
|
|
|
|
|
|
if (nodeCredentialDescription.required === true) {
|
|
|
|
// Credentials are required so error
|
|
|
|
if (!node.credentials) {
|
2021-04-16 09:33:36 -07:00
|
|
|
throw new NodeOperationError(node,'Node does not have any credentials set!');
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
if (!node.credentials[type]) {
|
2021-04-16 09:33:36 -07:00
|
|
|
throw new NodeOperationError(node,`Node does not have any credentials set for "${type}"!`);
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Credentials are not required so resolve with undefined
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-24 04:33:57 -08:00
|
|
|
let expressionResolveValues: ICredentialsExpressionResolveValues | undefined;
|
|
|
|
if (connectionInputData && runExecutionData && runIndex !== undefined) {
|
|
|
|
expressionResolveValues = {
|
|
|
|
connectionInputData,
|
|
|
|
itemIndex: itemIndex || 0,
|
|
|
|
node,
|
|
|
|
runExecutionData,
|
|
|
|
runIndex,
|
|
|
|
workflow,
|
|
|
|
} as ICredentialsExpressionResolveValues;
|
|
|
|
}
|
|
|
|
|
2019-06-23 03:35:23 -07:00
|
|
|
const name = node.credentials[type];
|
|
|
|
|
2021-01-29 03:16:46 -08:00
|
|
|
const decryptedDataObject = additionalData.credentialsHelper.getDecrypted(name, type, mode, false, expressionResolveValues);
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
|
|
return decryptedDataObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-02-15 17:07:01 -08:00
|
|
|
/**
|
|
|
|
* Returns a copy of the node
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {INode} node
|
|
|
|
* @returns {INode}
|
|
|
|
*/
|
|
|
|
export function getNode(node: INode): INode {
|
|
|
|
return JSON.parse(JSON.stringify(node));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-06-23 03:35:23 -07:00
|
|
|
/**
|
|
|
|
* Returns the requested resolved (all expressions replaced) node parameters.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @param {(IRunExecutionData | null)} runExecutionData
|
|
|
|
* @param {number} runIndex
|
|
|
|
* @param {INodeExecutionData[]} connectionInputData
|
|
|
|
* @param {INode} node
|
|
|
|
* @param {string} parameterName
|
|
|
|
* @param {number} itemIndex
|
|
|
|
* @param {*} [fallbackValue]
|
|
|
|
* @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object)}
|
|
|
|
*/
|
2021-01-29 00:31:40 -08:00
|
|
|
export function getNodeParameter(workflow: Workflow, runExecutionData: IRunExecutionData | null, runIndex: number, connectionInputData: INodeExecutionData[], node: INode, parameterName: string, itemIndex: number, mode: WorkflowExecuteMode, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object { //tslint:disable-line:no-any
|
2019-06-23 03:35:23 -07:00
|
|
|
const nodeType = workflow.nodeTypes.getByName(node.type);
|
|
|
|
if (nodeType === undefined) {
|
|
|
|
throw new Error(`Node type "${node.type}" is not known so can not return paramter value!`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const value = get(node.parameters, parameterName, fallbackValue);
|
|
|
|
|
|
|
|
if (value === undefined) {
|
|
|
|
throw new Error(`Could not get parameter "${parameterName}"!`);
|
|
|
|
}
|
|
|
|
|
2019-11-07 22:18:01 -08:00
|
|
|
let returnData;
|
|
|
|
try {
|
2021-01-29 00:31:40 -08:00
|
|
|
returnData = workflow.expression.getParameterValue(value, runExecutionData, runIndex, itemIndex, node.name, connectionInputData, mode);
|
2019-11-07 22:18:01 -08:00
|
|
|
} catch (e) {
|
|
|
|
e.message += ` [Error in parameter: "${parameterName}"]`;
|
|
|
|
throw e;
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
|
|
return returnData;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-03-17 05:18:04 -07:00
|
|
|
/**
|
|
|
|
* Returns if execution should be continued even if there was an error.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {INode} node
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
|
|
|
export function continueOnFail(node: INode): boolean {
|
|
|
|
return get(node, 'continueOnFail', false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-07-12 02:33:18 -07:00
|
|
|
/**
|
|
|
|
* Returns the webhook URL of the webhook with the given name
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {string} name
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @param {INode} node
|
|
|
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
|
|
|
* @param {boolean} [isTest]
|
|
|
|
* @returns {(string | undefined)}
|
|
|
|
*/
|
2021-01-29 00:31:40 -08:00
|
|
|
export function getNodeWebhookUrl(name: string, workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, isTest?: boolean): string | undefined {
|
2019-07-12 02:33:18 -07:00
|
|
|
let baseUrl = additionalData.webhookBaseUrl;
|
|
|
|
if (isTest === true) {
|
|
|
|
baseUrl = additionalData.webhookTestBaseUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
const webhookDescription = getWebhookDescription(name, workflow, node);
|
|
|
|
if (webhookDescription === undefined) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
2021-01-29 00:31:40 -08:00
|
|
|
const path = workflow.expression.getSimpleParameterValue(node, webhookDescription['path'], mode);
|
2019-07-12 02:33:18 -07:00
|
|
|
if (path === undefined) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
2021-01-29 00:31:40 -08:00
|
|
|
const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookDescription['isFullPath'], mode, false) as boolean;
|
2020-06-10 06:39:15 -07:00
|
|
|
return NodeHelpers.getNodeWebhookUrl(baseUrl, workflow.id!, node, path.toString(), isFullPath);
|
2019-07-12 02:33:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-06-23 03:35:23 -07:00
|
|
|
/**
|
|
|
|
* Returns the timezone for the workflow
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
export function getTimezone(workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData):string {
|
|
|
|
if (workflow.settings !== undefined && workflow.settings.timezone !== undefined) {
|
|
|
|
return (workflow.settings as IWorkflowSettings).timezone as string;
|
|
|
|
}
|
|
|
|
return additionalData.timezone;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-07-12 02:33:18 -07:00
|
|
|
/**
|
|
|
|
* Returns the full webhook description of the webhook with the given name
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {string} name
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @param {INode} node
|
|
|
|
* @returns {(IWebhookDescription | undefined)}
|
|
|
|
*/
|
|
|
|
export function getWebhookDescription(name: string, workflow: Workflow, node: INode): IWebhookDescription | undefined {
|
|
|
|
const nodeType = workflow.nodeTypes.getByName(node.type) as INodeType;
|
|
|
|
|
|
|
|
if (nodeType.description.webhooks === undefined) {
|
|
|
|
// Node does not have any webhooks so return
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const webhookDescription of nodeType.description.webhooks) {
|
|
|
|
if (webhookDescription.name === name) {
|
|
|
|
return webhookDescription;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-02-15 17:07:01 -08:00
|
|
|
/**
|
|
|
|
* Returns the workflow metadata
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @returns {IWorkflowMetadata}
|
|
|
|
*/
|
|
|
|
export function getWorkflowMetadata(workflow: Workflow): IWorkflowMetadata {
|
|
|
|
return {
|
|
|
|
id: workflow.id,
|
|
|
|
name: workflow.name,
|
|
|
|
active: workflow.active,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-12-31 12:19:37 -08:00
|
|
|
/**
|
|
|
|
* Returns the execute functions the poll nodes have access to.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @param {INode} node
|
|
|
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
|
|
|
* @param {WorkflowExecuteMode} mode
|
|
|
|
* @returns {ITriggerFunctions}
|
|
|
|
*/
|
|
|
|
// TODO: Check if I can get rid of: additionalData, and so then maybe also at ActiveWorkflowRunner.add
|
2021-03-23 11:08:47 -07:00
|
|
|
export function getExecutePollFunctions(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): IPollFunctions {
|
2019-12-31 12:19:37 -08:00
|
|
|
return ((workflow: Workflow, node: INode) => {
|
|
|
|
return {
|
|
|
|
__emit: (data: INodeExecutionData[][]): void => {
|
|
|
|
throw new Error('Overwrite NodeExecuteFunctions.getExecutePullFunctions.__emit function!');
|
|
|
|
},
|
|
|
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
2021-01-29 03:16:46 -08:00
|
|
|
return getCredentials(workflow, node, type, additionalData, mode);
|
2019-12-31 12:19:37 -08:00
|
|
|
},
|
|
|
|
getMode: (): WorkflowExecuteMode => {
|
|
|
|
return mode;
|
|
|
|
},
|
2021-03-23 11:08:47 -07:00
|
|
|
getActivationMode: (): WorkflowActivateMode => {
|
|
|
|
return activation;
|
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getNode: () => {
|
|
|
|
return getNode(node);
|
|
|
|
},
|
2019-12-31 12:19:37 -08:00
|
|
|
getNodeParameter: (parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any
|
|
|
|
const runExecutionData: IRunExecutionData | null = null;
|
|
|
|
const itemIndex = 0;
|
|
|
|
const runIndex = 0;
|
|
|
|
const connectionInputData: INodeExecutionData[] = [];
|
|
|
|
|
2021-01-29 00:31:40 -08:00
|
|
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
2019-12-31 12:19:37 -08:00
|
|
|
},
|
|
|
|
getRestApiUrl: (): string => {
|
|
|
|
return additionalData.restApiUrl;
|
|
|
|
},
|
|
|
|
getTimezone: (): string => {
|
|
|
|
return getTimezone(workflow, additionalData);
|
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getWorkflow: () => {
|
|
|
|
return getWorkflowMetadata(workflow);
|
|
|
|
},
|
2019-12-31 12:19:37 -08:00
|
|
|
getWorkflowStaticData(type: string): IDataObject {
|
|
|
|
return workflow.getStaticData(type, node);
|
|
|
|
},
|
|
|
|
helpers: {
|
|
|
|
prepareBinaryData,
|
2021-04-17 06:15:33 -07:00
|
|
|
request: requestPromiseWithDefaults,
|
2020-07-25 10:58:38 -07:00
|
|
|
requestOAuth2(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, oAuth2Options?: IOAuth2Options): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth2.call(this, credentialsType, requestOptions, node, additionalData, oAuth2Options);
|
2020-06-01 17:42:38 -07:00
|
|
|
},
|
|
|
|
requestOAuth1(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUrl | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth1.call(this, credentialsType, requestOptions);
|
2020-01-13 18:46:58 -08:00
|
|
|
},
|
2019-12-31 12:19:37 -08:00
|
|
|
returnJsonArray,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
})(workflow, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-06-23 03:35:23 -07:00
|
|
|
/**
|
|
|
|
* Returns the execute functions the trigger nodes have access to.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @param {INode} node
|
|
|
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
|
|
|
* @param {WorkflowExecuteMode} mode
|
|
|
|
* @returns {ITriggerFunctions}
|
|
|
|
*/
|
2019-08-08 11:38:25 -07:00
|
|
|
// TODO: Check if I can get rid of: additionalData, and so then maybe also at ActiveWorkflowRunner.add
|
2021-03-23 11:08:47 -07:00
|
|
|
export function getExecuteTriggerFunctions(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): ITriggerFunctions {
|
2019-06-23 03:35:23 -07:00
|
|
|
return ((workflow: Workflow, node: INode) => {
|
|
|
|
return {
|
|
|
|
emit: (data: INodeExecutionData[][]): void => {
|
2019-08-08 11:38:25 -07:00
|
|
|
throw new Error('Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!');
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
2021-01-29 03:16:46 -08:00
|
|
|
return getCredentials(workflow, node, type, additionalData, mode);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getNode: () => {
|
|
|
|
return getNode(node);
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getMode: (): WorkflowExecuteMode => {
|
|
|
|
return mode;
|
|
|
|
},
|
2021-03-23 11:08:47 -07:00
|
|
|
getActivationMode: (): WorkflowActivateMode => {
|
|
|
|
return activation;
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getNodeParameter: (parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any
|
|
|
|
const runExecutionData: IRunExecutionData | null = null;
|
|
|
|
const itemIndex = 0;
|
|
|
|
const runIndex = 0;
|
|
|
|
const connectionInputData: INodeExecutionData[] = [];
|
|
|
|
|
2021-01-29 00:31:40 -08:00
|
|
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2019-12-19 14:07:55 -08:00
|
|
|
getRestApiUrl: (): string => {
|
|
|
|
return additionalData.restApiUrl;
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getTimezone: (): string => {
|
|
|
|
return getTimezone(workflow, additionalData);
|
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getWorkflow: () => {
|
|
|
|
return getWorkflowMetadata(workflow);
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getWorkflowStaticData(type: string): IDataObject {
|
|
|
|
return workflow.getStaticData(type, node);
|
|
|
|
},
|
|
|
|
helpers: {
|
|
|
|
prepareBinaryData,
|
2021-04-17 06:15:33 -07:00
|
|
|
request: requestPromiseWithDefaults,
|
2020-07-25 10:58:38 -07:00
|
|
|
requestOAuth2(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, oAuth2Options?: IOAuth2Options): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth2.call(this, credentialsType, requestOptions, node, additionalData, oAuth2Options);
|
2020-06-01 17:42:38 -07:00
|
|
|
},
|
|
|
|
requestOAuth1(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUrl | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth1.call(this, credentialsType, requestOptions);
|
2020-01-13 18:46:58 -08:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
returnJsonArray,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}) (workflow, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the execute functions regular nodes have access to.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @param {IRunExecutionData} runExecutionData
|
|
|
|
* @param {number} runIndex
|
|
|
|
* @param {INodeExecutionData[]} connectionInputData
|
|
|
|
* @param {ITaskDataConnections} inputData
|
|
|
|
* @param {INode} node
|
|
|
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
|
|
|
* @param {WorkflowExecuteMode} mode
|
|
|
|
* @returns {IExecuteFunctions}
|
|
|
|
*/
|
|
|
|
export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunExecutionData, runIndex: number, connectionInputData: INodeExecutionData[], inputData: ITaskDataConnections, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): IExecuteFunctions {
|
|
|
|
return ((workflow, runExecutionData, connectionInputData, inputData, node) => {
|
|
|
|
return {
|
2020-03-17 05:18:04 -07:00
|
|
|
continueOnFail: () => {
|
|
|
|
return continueOnFail(node);
|
|
|
|
},
|
2020-03-21 09:25:29 -07:00
|
|
|
evaluateExpression: (expression: string, itemIndex: number) => {
|
2021-05-14 16:16:48 -07:00
|
|
|
return workflow.expression.resolveSimpleParameterValue('=' + expression, {}, runExecutionData, runIndex, itemIndex, node.name, connectionInputData, mode);
|
2020-03-21 09:25:29 -07:00
|
|
|
},
|
2020-01-02 15:13:53 -08:00
|
|
|
async executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return additionalData.executeWorkflow(workflowInfo, additionalData, inputData);
|
2019-12-19 14:07:55 -08:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getContext(type: string): IContextObject {
|
|
|
|
return NodeHelpers.getContext(runExecutionData, type, node);
|
|
|
|
},
|
2021-01-24 04:33:57 -08:00
|
|
|
getCredentials(type: string, itemIndex?: number): ICredentialDataDecryptedObject | undefined {
|
2021-01-29 03:16:46 -08:00
|
|
|
return getCredentials(workflow, node, type, additionalData, mode, runExecutionData, runIndex, connectionInputData, itemIndex);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
getInputData: (inputIndex = 0, inputName = 'main') => {
|
|
|
|
|
|
|
|
if (!inputData.hasOwnProperty(inputName)) {
|
|
|
|
// Return empty array because else it would throw error when nothing is connected to input
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Check if nodeType has input with that index defined
|
|
|
|
if (inputData[inputName].length < inputIndex) {
|
|
|
|
throw new Error(`Could not get input index "${inputIndex}" of input "${inputName}"!`);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (inputData[inputName][inputIndex] === null) {
|
|
|
|
// return [];
|
|
|
|
throw new Error(`Value "${inputIndex}" of input "${inputName}" did not get set!`);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Maybe do clone of data only here so it only clones the data that is really needed
|
|
|
|
return inputData[inputName][inputIndex] as INodeExecutionData[];
|
|
|
|
},
|
|
|
|
getNodeParameter: (parameterName: string, itemIndex: number, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any
|
2021-01-29 00:31:40 -08:00
|
|
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
getMode: (): WorkflowExecuteMode => {
|
|
|
|
return mode;
|
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getNode: () => {
|
|
|
|
return getNode(node);
|
|
|
|
},
|
2019-12-19 14:07:55 -08:00
|
|
|
getRestApiUrl: (): string => {
|
|
|
|
return additionalData.restApiUrl;
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getTimezone: (): string => {
|
|
|
|
return getTimezone(workflow, additionalData);
|
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getWorkflow: () => {
|
|
|
|
return getWorkflowMetadata(workflow);
|
|
|
|
},
|
2019-09-04 05:53:39 -07:00
|
|
|
getWorkflowDataProxy: (itemIndex: number): IWorkflowDataProxyData => {
|
2021-05-14 16:16:48 -07:00
|
|
|
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, node.name, connectionInputData, {}, mode);
|
2019-09-04 05:53:39 -07:00
|
|
|
return dataProxy.getDataProxy();
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getWorkflowStaticData(type: string): IDataObject {
|
|
|
|
return workflow.getStaticData(type, node);
|
|
|
|
},
|
|
|
|
prepareOutputData: NodeHelpers.prepareOutputData,
|
2021-06-12 11:22:55 -07:00
|
|
|
sendMessageToUI(message: any): void { // tslint:disable-line:no-any
|
2021-05-29 11:41:25 -07:00
|
|
|
if (mode !== 'manual') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
if (additionalData.sendMessageToUI) {
|
|
|
|
additionalData.sendMessageToUI(node.name, message);
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
Logger.warn(`There was a problem sending messsage to UI: ${error.message}`);
|
|
|
|
}
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
helpers: {
|
|
|
|
prepareBinaryData,
|
2021-04-17 06:15:33 -07:00
|
|
|
request: requestPromiseWithDefaults,
|
2020-07-25 10:58:38 -07:00
|
|
|
requestOAuth2(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, oAuth2Options?: IOAuth2Options): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth2.call(this, credentialsType, requestOptions, node, additionalData, oAuth2Options);
|
2020-06-01 17:42:38 -07:00
|
|
|
},
|
|
|
|
requestOAuth1(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUrl | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth1.call(this, credentialsType, requestOptions);
|
2020-01-13 18:46:58 -08:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
returnJsonArray,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
})(workflow, runExecutionData, connectionInputData, inputData, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the execute functions regular nodes have access to when single-function is defined.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @param {IRunExecutionData} runExecutionData
|
|
|
|
* @param {number} runIndex
|
|
|
|
* @param {INodeExecutionData[]} connectionInputData
|
|
|
|
* @param {ITaskDataConnections} inputData
|
|
|
|
* @param {INode} node
|
|
|
|
* @param {number} itemIndex
|
|
|
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
|
|
|
* @param {WorkflowExecuteMode} mode
|
|
|
|
* @returns {IExecuteSingleFunctions}
|
|
|
|
*/
|
|
|
|
export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData: IRunExecutionData, runIndex: number, connectionInputData: INodeExecutionData[], inputData: ITaskDataConnections, node: INode, itemIndex: number, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): IExecuteSingleFunctions {
|
|
|
|
return ((workflow, runExecutionData, connectionInputData, inputData, node, itemIndex) => {
|
|
|
|
return {
|
2020-03-17 05:18:04 -07:00
|
|
|
continueOnFail: () => {
|
|
|
|
return continueOnFail(node);
|
|
|
|
},
|
2020-03-21 09:25:29 -07:00
|
|
|
evaluateExpression: (expression: string, evaluateItemIndex: number | undefined) => {
|
|
|
|
evaluateItemIndex = evaluateItemIndex === undefined ? itemIndex : evaluateItemIndex;
|
2021-05-14 16:16:48 -07:00
|
|
|
return workflow.expression.resolveSimpleParameterValue('=' + expression, {}, runExecutionData, runIndex, evaluateItemIndex, node.name, connectionInputData, mode);
|
2020-03-21 09:25:29 -07:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getContext(type: string): IContextObject {
|
|
|
|
return NodeHelpers.getContext(runExecutionData, type, node);
|
|
|
|
},
|
|
|
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
2021-01-29 03:16:46 -08:00
|
|
|
return getCredentials(workflow, node, type, additionalData, mode, runExecutionData, runIndex, connectionInputData, itemIndex);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
getInputData: (inputIndex = 0, inputName = 'main') => {
|
|
|
|
if (!inputData.hasOwnProperty(inputName)) {
|
|
|
|
// Return empty array because else it would throw error when nothing is connected to input
|
|
|
|
return {json: {}};
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Check if nodeType has input with that index defined
|
|
|
|
if (inputData[inputName].length < inputIndex) {
|
|
|
|
throw new Error(`Could not get input index "${inputIndex}" of input "${inputName}"!`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const allItems = inputData[inputName][inputIndex];
|
|
|
|
|
|
|
|
if (allItems === null) {
|
|
|
|
// return [];
|
|
|
|
throw new Error(`Value "${inputIndex}" of input "${inputName}" did not get set!`);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (allItems[itemIndex] === null) {
|
|
|
|
// return [];
|
|
|
|
throw new Error(`Value "${inputIndex}" of input "${inputName}" with itemIndex "${itemIndex}" did not get set!`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return allItems[itemIndex] as INodeExecutionData;
|
|
|
|
},
|
|
|
|
getMode: (): WorkflowExecuteMode => {
|
|
|
|
return mode;
|
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getNode: () => {
|
|
|
|
return getNode(node);
|
|
|
|
},
|
2019-12-19 14:07:55 -08:00
|
|
|
getRestApiUrl: (): string => {
|
|
|
|
return additionalData.restApiUrl;
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getTimezone: (): string => {
|
|
|
|
return getTimezone(workflow, additionalData);
|
|
|
|
},
|
|
|
|
getNodeParameter: (parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any
|
2021-01-29 00:31:40 -08:00
|
|
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getWorkflow: () => {
|
|
|
|
return getWorkflowMetadata(workflow);
|
|
|
|
},
|
2019-09-04 05:53:39 -07:00
|
|
|
getWorkflowDataProxy: (): IWorkflowDataProxyData => {
|
2021-05-14 16:16:48 -07:00
|
|
|
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, node.name, connectionInputData, {}, mode);
|
2019-09-04 05:53:39 -07:00
|
|
|
return dataProxy.getDataProxy();
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getWorkflowStaticData(type: string): IDataObject {
|
|
|
|
return workflow.getStaticData(type, node);
|
|
|
|
},
|
|
|
|
helpers: {
|
|
|
|
prepareBinaryData,
|
2021-04-17 06:15:33 -07:00
|
|
|
request: requestPromiseWithDefaults,
|
2020-07-25 10:58:38 -07:00
|
|
|
requestOAuth2(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, oAuth2Options?: IOAuth2Options): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth2.call(this, credentialsType, requestOptions, node, additionalData, oAuth2Options);
|
2020-06-01 17:42:38 -07:00
|
|
|
},
|
|
|
|
requestOAuth1(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUrl | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth1.call(this, credentialsType, requestOptions);
|
2020-01-13 18:46:58 -08:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
})(workflow, runExecutionData, connectionInputData, inputData, node, itemIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the execute functions regular nodes have access to in load-options-function.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @param {INode} node
|
|
|
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
|
|
|
* @returns {ILoadOptionsFunctions}
|
|
|
|
*/
|
2021-05-16 16:16:24 -07:00
|
|
|
export function getLoadOptionsFunctions(workflow: Workflow, node: INode, path: string, additionalData: IWorkflowExecuteAdditionalData): ILoadOptionsFunctions {
|
|
|
|
return ((workflow: Workflow, node: INode, path: string) => {
|
2019-06-23 03:35:23 -07:00
|
|
|
const that = {
|
|
|
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
2021-01-29 03:16:46 -08:00
|
|
|
return getCredentials(workflow, node, type, additionalData, 'internal');
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2021-05-16 16:16:24 -07:00
|
|
|
getCurrentNodeParameter: (parameterPath: string): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object | undefined => {
|
2020-05-04 21:07:19 -07:00
|
|
|
const nodeParameters = additionalData.currentNodeParameters;
|
2021-05-16 16:16:24 -07:00
|
|
|
|
|
|
|
if (parameterPath.charAt(0) === '&') {
|
|
|
|
parameterPath = `${path.split('.').slice(1, -1).join('.')}.${parameterPath.slice(1)}`;
|
2019-10-20 11:55:49 -07:00
|
|
|
}
|
2021-05-16 16:16:24 -07:00
|
|
|
|
|
|
|
return get(nodeParameters, parameterPath);
|
2019-10-20 12:42:34 -07:00
|
|
|
},
|
|
|
|
getCurrentNodeParameters: (): INodeParameters | undefined => {
|
2020-05-04 21:07:19 -07:00
|
|
|
return additionalData.currentNodeParameters;
|
2019-10-20 11:55:49 -07:00
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getNode: () => {
|
|
|
|
return getNode(node);
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getNodeParameter: (parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any
|
|
|
|
const runExecutionData: IRunExecutionData | null = null;
|
|
|
|
const itemIndex = 0;
|
|
|
|
const runIndex = 0;
|
|
|
|
const connectionInputData: INodeExecutionData[] = [];
|
|
|
|
|
2021-01-29 00:31:40 -08:00
|
|
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, 'internal' as WorkflowExecuteMode, fallbackValue);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
getTimezone: (): string => {
|
|
|
|
return getTimezone(workflow, additionalData);
|
|
|
|
},
|
2019-12-19 14:07:55 -08:00
|
|
|
getRestApiUrl: (): string => {
|
|
|
|
return additionalData.restApiUrl;
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
helpers: {
|
2021-04-17 06:15:33 -07:00
|
|
|
request: requestPromiseWithDefaults,
|
2020-07-25 10:58:38 -07:00
|
|
|
requestOAuth2(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, oAuth2Options?: IOAuth2Options): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth2.call(this, credentialsType, requestOptions, node, additionalData, oAuth2Options);
|
2020-06-01 17:42:38 -07:00
|
|
|
},
|
|
|
|
requestOAuth1(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUrl | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth1.call(this, credentialsType, requestOptions);
|
2020-01-13 18:46:58 -08:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
return that;
|
2021-05-16 16:16:24 -07:00
|
|
|
})(workflow, node, path);
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the execute functions regular nodes have access to in hook-function.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @param {INode} node
|
|
|
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
|
|
|
* @param {WorkflowExecuteMode} mode
|
|
|
|
* @returns {IHookFunctions}
|
|
|
|
*/
|
2021-03-23 11:08:47 -07:00
|
|
|
export function getExecuteHookFunctions(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode, isTest?: boolean, webhookData?: IWebhookData): IHookFunctions {
|
2019-06-23 03:35:23 -07:00
|
|
|
return ((workflow: Workflow, node: INode) => {
|
|
|
|
const that = {
|
|
|
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
2021-01-29 03:16:46 -08:00
|
|
|
return getCredentials(workflow, node, type, additionalData, mode);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
getMode: (): WorkflowExecuteMode => {
|
|
|
|
return mode;
|
|
|
|
},
|
2021-03-23 11:08:47 -07:00
|
|
|
getActivationMode: (): WorkflowActivateMode => {
|
|
|
|
return activation;
|
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getNode: () => {
|
|
|
|
return getNode(node);
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getNodeParameter: (parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any
|
|
|
|
const runExecutionData: IRunExecutionData | null = null;
|
|
|
|
const itemIndex = 0;
|
|
|
|
const runIndex = 0;
|
|
|
|
const connectionInputData: INodeExecutionData[] = [];
|
|
|
|
|
2021-01-29 00:31:40 -08:00
|
|
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
getNodeWebhookUrl: (name: string): string | undefined => {
|
2021-01-29 00:31:40 -08:00
|
|
|
return getNodeWebhookUrl(name, workflow, node, additionalData, mode, isTest);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
getTimezone: (): string => {
|
|
|
|
return getTimezone(workflow, additionalData);
|
|
|
|
},
|
2019-07-12 02:33:18 -07:00
|
|
|
getWebhookName(): string {
|
|
|
|
if (webhookData === undefined) {
|
|
|
|
throw new Error('Is only supported in webhook functions!');
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
2019-07-12 02:33:18 -07:00
|
|
|
return webhookData.webhookDescription.name;
|
|
|
|
},
|
|
|
|
getWebhookDescription(name: string): IWebhookDescription | undefined {
|
|
|
|
return getWebhookDescription(name, workflow, node);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getWorkflow: () => {
|
|
|
|
return getWorkflowMetadata(workflow);
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getWorkflowStaticData(type: string): IDataObject {
|
|
|
|
return workflow.getStaticData(type, node);
|
|
|
|
},
|
|
|
|
helpers: {
|
2021-04-17 06:15:33 -07:00
|
|
|
request: requestPromiseWithDefaults,
|
2020-07-25 10:58:38 -07:00
|
|
|
requestOAuth2(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, oAuth2Options?: IOAuth2Options): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth2.call(this, credentialsType, requestOptions, node, additionalData, oAuth2Options);
|
2020-06-01 17:42:38 -07:00
|
|
|
},
|
|
|
|
requestOAuth1(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUrl | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth1.call(this, credentialsType, requestOptions);
|
2020-01-13 18:46:58 -08:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
return that;
|
|
|
|
})(workflow, node);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the execute functions regular nodes have access to when webhook-function is defined.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {Workflow} workflow
|
|
|
|
* @param {IRunExecutionData} runExecutionData
|
|
|
|
* @param {INode} node
|
|
|
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
|
|
|
* @param {WorkflowExecuteMode} mode
|
|
|
|
* @returns {IWebhookFunctions}
|
|
|
|
*/
|
2019-07-12 02:33:18 -07:00
|
|
|
export function getExecuteWebhookFunctions(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, webhookData: IWebhookData): IWebhookFunctions {
|
2019-06-23 03:35:23 -07:00
|
|
|
return ((workflow: Workflow, node: INode) => {
|
|
|
|
return {
|
|
|
|
getBodyData(): IDataObject {
|
|
|
|
if (additionalData.httpRequest === undefined) {
|
|
|
|
throw new Error('Request is missing!');
|
|
|
|
}
|
|
|
|
return additionalData.httpRequest.body;
|
|
|
|
},
|
|
|
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
2021-01-29 03:16:46 -08:00
|
|
|
return getCredentials(workflow, node, type, additionalData, mode);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
getHeaderData(): object {
|
|
|
|
if (additionalData.httpRequest === undefined) {
|
|
|
|
throw new Error('Request is missing!');
|
|
|
|
}
|
|
|
|
return additionalData.httpRequest.headers;
|
|
|
|
},
|
|
|
|
getMode: (): WorkflowExecuteMode => {
|
|
|
|
return mode;
|
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getNode: () => {
|
|
|
|
return getNode(node);
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getNodeParameter: (parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any
|
|
|
|
const runExecutionData: IRunExecutionData | null = null;
|
|
|
|
const itemIndex = 0;
|
|
|
|
const runIndex = 0;
|
|
|
|
const connectionInputData: INodeExecutionData[] = [];
|
|
|
|
|
2021-01-29 00:31:40 -08:00
|
|
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2021-01-23 11:00:32 -08:00
|
|
|
getParamsData(): object {
|
|
|
|
if (additionalData.httpRequest === undefined) {
|
|
|
|
throw new Error('Request is missing!');
|
|
|
|
}
|
|
|
|
return additionalData.httpRequest.params;
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getQueryData(): object {
|
|
|
|
if (additionalData.httpRequest === undefined) {
|
|
|
|
throw new Error('Request is missing!');
|
|
|
|
}
|
|
|
|
return additionalData.httpRequest.query;
|
|
|
|
},
|
|
|
|
getRequestObject(): express.Request {
|
|
|
|
if (additionalData.httpRequest === undefined) {
|
|
|
|
throw new Error('Request is missing!');
|
|
|
|
}
|
|
|
|
return additionalData.httpRequest;
|
|
|
|
},
|
|
|
|
getResponseObject(): express.Response {
|
|
|
|
if (additionalData.httpResponse === undefined) {
|
|
|
|
throw new Error('Response is missing!');
|
|
|
|
}
|
|
|
|
return additionalData.httpResponse;
|
|
|
|
},
|
2019-07-12 02:33:18 -07:00
|
|
|
getNodeWebhookUrl: (name: string): string | undefined => {
|
2021-01-29 00:31:40 -08:00
|
|
|
return getNodeWebhookUrl(name, workflow, node, additionalData, mode);
|
2019-07-12 02:33:18 -07:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getTimezone: (): string => {
|
|
|
|
return getTimezone(workflow, additionalData);
|
|
|
|
},
|
2020-02-15 17:07:01 -08:00
|
|
|
getWorkflow: () => {
|
|
|
|
return getWorkflowMetadata(workflow);
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
getWorkflowStaticData(type: string): IDataObject {
|
|
|
|
return workflow.getStaticData(type, node);
|
|
|
|
},
|
2019-07-12 02:33:18 -07:00
|
|
|
getWebhookName(): string {
|
|
|
|
return webhookData.webhookDescription.name;
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
prepareOutputData: NodeHelpers.prepareOutputData,
|
|
|
|
helpers: {
|
|
|
|
prepareBinaryData,
|
2021-04-17 06:15:33 -07:00
|
|
|
request: requestPromiseWithDefaults,
|
2020-07-25 10:58:38 -07:00
|
|
|
requestOAuth2(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, oAuth2Options?: IOAuth2Options): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth2.call(this, credentialsType, requestOptions, node, additionalData, oAuth2Options);
|
2020-06-01 17:42:38 -07:00
|
|
|
},
|
|
|
|
requestOAuth1(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUrl | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
|
|
|
|
return requestOAuth1.call(this, credentialsType, requestOptions);
|
2020-01-13 18:46:58 -08:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
returnJsonArray,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
})(workflow, node);
|
|
|
|
|
|
|
|
}
|