diff --git a/packages/cli/src/services/dynamic-node-parameters.service.ts b/packages/cli/src/services/dynamic-node-parameters.service.ts index 7a641e84a6..dd60c85444 100644 --- a/packages/cli/src/services/dynamic-node-parameters.service.ts +++ b/packages/cli/src/services/dynamic-node-parameters.service.ts @@ -1,4 +1,4 @@ -import { NodeExecuteFunctions } from 'n8n-core'; +import { LoadOptionsContext, NodeExecuteFunctions } from 'n8n-core'; import type { ILoadOptions, ILoadOptionsFunctions, @@ -253,6 +253,6 @@ export class DynamicNodeParametersService { workflow: Workflow, ) { const node = workflow.nodes['Temp-Node']; - return NodeExecuteFunctions.getLoadOptionsFunctions(workflow, node, path, additionalData); + return new LoadOptionsContext(workflow, node, additionalData, path); } } diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 46dfcdaa1c..4bd3237610 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -23,12 +23,11 @@ import type { } from 'axios'; import axios from 'axios'; import crypto, { createHmac } from 'crypto'; -import type { Request, Response } from 'express'; import FileType from 'file-type'; import FormData from 'form-data'; import { createReadStream } from 'fs'; import { access as fsAccess, writeFile as fsWriteFile } from 'fs/promises'; -import { IncomingMessage, type IncomingHttpHeaders } from 'http'; +import { IncomingMessage } from 'http'; import { Agent, type AgentOptions } from 'https'; import get from 'lodash/get'; import isEmpty from 'lodash/isEmpty'; @@ -60,7 +59,6 @@ import type { IGetNodeParameterOptions, IHookFunctions, IHttpRequestOptions, - ILoadOptionsFunctions, IN8nHttpFullResponse, IN8nHttpResponse, INode, @@ -100,7 +98,6 @@ import type { INodeParameters, EnsureTypeOptions, SSHTunnelFunctions, - SchedulingFunctions, DeduplicationHelperFunctions, IDeduplicationOutput, IDeduplicationOutputItems, @@ -163,7 +160,6 @@ import { import { extractValue } from './ExtractValue'; import { InstanceSettings } from './InstanceSettings'; import type { ExtendedValidationResult, IResponseError } from './Interfaces'; -import { ScheduledTaskManager } from './ScheduledTaskManager'; import { getSecretsProxy } from './Secrets'; import { SSHClientsManager } from './SSHClientsManager'; import { HookContext, PollContext } from './node-execution-context'; @@ -3591,85 +3587,6 @@ export function getCredentialTestFunctions(): ICredentialTestFunctions { }; } -/** - * Returns the execute functions regular nodes have access to in load-options-function. - */ -export function getLoadOptionsFunctions( - workflow: Workflow, - node: INode, - path: string, - additionalData: IWorkflowExecuteAdditionalData, -): ILoadOptionsFunctions { - return ((workflow: Workflow, node: INode, path: string) => { - return { - ...getCommonWorkflowFunctions(workflow, node, additionalData), - getCredentials: async (type) => - await getCredentials(workflow, node, type, additionalData, 'internal'), - getCurrentNodeParameter: ( - parameterPath: string, - options?: IGetNodeParameterOptions, - ): NodeParameterValueType | object | undefined => { - const nodeParameters = additionalData.currentNodeParameters; - - if (parameterPath.charAt(0) === '&') { - parameterPath = `${path.split('.').slice(1, -1).join('.')}.${parameterPath.slice(1)}`; - } - - let returnData = get(nodeParameters, parameterPath); - - // This is outside the try/catch because it throws errors with proper messages - if (options?.extractValue) { - const nodeType = workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion); - if (nodeType === undefined) { - throw new ApplicationError('Node type is not known so cannot return parameter value', { - tags: { nodeType: node.type }, - }); - } - returnData = extractValue( - returnData, - parameterPath, - node, - nodeType, - ) as NodeParameterValueType; - } - - return returnData; - }, - getCurrentNodeParameters: () => additionalData.currentNodeParameters, - getNodeParameter: ( - parameterName: string, - fallbackValue?: any, - options?: IGetNodeParameterOptions, - ): NodeParameterValueType | object => { - const runExecutionData: IRunExecutionData | null = null; - const itemIndex = 0; - const runIndex = 0; - const mode = 'internal' as WorkflowExecuteMode; - const connectionInputData: INodeExecutionData[] = []; - - return getNodeParameter( - workflow, - runExecutionData, - runIndex, - connectionInputData, - node, - parameterName, - itemIndex, - mode, - getAdditionalKeys(additionalData, mode, runExecutionData), - undefined, - fallbackValue, - options, - ); - }, - helpers: { - ...getSSHTunnelFunctions(), - ...getRequestHelperFunctions(workflow, node, additionalData), - }, - }; - })(workflow, node, path); -} - /** @deprecated */ export function getExecuteHookFunctions( workflow: Workflow, diff --git a/packages/core/src/node-execution-context/index.ts b/packages/core/src/node-execution-context/index.ts index 7b59642121..98892fd9e6 100644 --- a/packages/core/src/node-execution-context/index.ts +++ b/packages/core/src/node-execution-context/index.ts @@ -1,5 +1,6 @@ export { ExecuteSingleContext } from './execute-single-context'; export { HookContext } from './hook-context'; +export { LoadOptionsContext } from './load-options-context'; export { PollContext } from './poll-context'; export { TriggerContext } from './trigger-context'; export { WebhookContext } from './webhook-context'; diff --git a/packages/core/src/node-execution-context/load-options-context.ts b/packages/core/src/node-execution-context/load-options-context.ts index e69de29bb2..faf19193b0 100644 --- a/packages/core/src/node-execution-context/load-options-context.ts +++ b/packages/core/src/node-execution-context/load-options-context.ts @@ -0,0 +1,119 @@ +import type { + ICredentialDataDecryptedObject, + IGetNodeParameterOptions, + INode, + INodeExecutionData, + ILoadOptionsFunctions, + IRunExecutionData, + IWorkflowExecuteAdditionalData, + NodeParameterValueType, + Workflow, +} from 'n8n-workflow'; +import { ApplicationError } from 'n8n-workflow'; + +import { getAdditionalKeys, getCredentials, getNodeParameter } from '@/NodeExecuteFunctions'; +import { BaseContext } from './base-contexts'; +import { RequestHelpers } from './helpers/request-helpers'; +import { get } from 'lodash'; +import { extractValue } from '@/ExtractValue'; +import { SSHTunnelHelpers } from './helpers/ssh-tunnel-helpers'; + +export class LoadOptionsContext extends BaseContext implements ILoadOptionsFunctions { + readonly helpers: ILoadOptionsFunctions['helpers']; + + constructor( + workflow: Workflow, + node: INode, + additionalData: IWorkflowExecuteAdditionalData, + private readonly path: string, + ) { + super(workflow, node, additionalData); + + const requestHelpers = new RequestHelpers(this, workflow, node, additionalData); + const sshTunnelHelpers = new SSHTunnelHelpers(); + + this.helpers = { + httpRequest: requestHelpers.httpRequest.bind(requestHelpers), + httpRequestWithAuthentication: + requestHelpers.httpRequestWithAuthentication.bind(requestHelpers), + requestWithAuthenticationPaginated: + requestHelpers.requestWithAuthenticationPaginated.bind(requestHelpers), + request: requestHelpers.request.bind(requestHelpers), + requestWithAuthentication: requestHelpers.requestWithAuthentication.bind(requestHelpers), + requestOAuth1: requestHelpers.requestOAuth1.bind(requestHelpers), + requestOAuth2: requestHelpers.requestOAuth2.bind(requestHelpers), + getSSHClient: sshTunnelHelpers.getSSHClient.bind(sshTunnelHelpers), + }; + } + + // TODO: This is mostly identical to PollContext + async getCredentials(type: string) { + // TODO: move `this.mode` to the base class, instead of repeating `internal` everywhere in this class + return await getCredentials(this.workflow, this.node, type, this.additionalData, 'internal'); + } + + getCurrentNodeParameter( + parameterPath: string, + options?: IGetNodeParameterOptions, + ): NodeParameterValueType | object | undefined { + const nodeParameters = this.additionalData.currentNodeParameters; + + if (parameterPath.charAt(0) === '&') { + parameterPath = `${this.path.split('.').slice(1, -1).join('.')}.${parameterPath.slice(1)}`; + } + + let returnData = get(nodeParameters, parameterPath); + + // This is outside the try/catch because it throws errors with proper messages + if (options?.extractValue) { + const nodeType = this.workflow.nodeTypes.getByNameAndVersion( + this.node.type, + this.node.typeVersion, + ); + if (nodeType === undefined) { + throw new ApplicationError('Node type is not known so cannot return parameter value', { + tags: { nodeType: this.node.type }, + }); + } + returnData = extractValue( + returnData, + parameterPath, + this.node, + nodeType, + ) as NodeParameterValueType; + } + + return returnData; + } + + getCurrentNodeParameters() { + return this.additionalData.currentNodeParameters; + } + + // TODO: This is identical to PollContext + getNodeParameter( + parameterName: string, + fallbackValue?: any, + options?: IGetNodeParameterOptions, + ): NodeParameterValueType | object { + const runExecutionData: IRunExecutionData | null = null; + const itemIndex = 0; + const runIndex = 0; + const connectionInputData: INodeExecutionData[] = []; + + return getNodeParameter( + this.workflow, + runExecutionData, + runIndex, + connectionInputData, + this.node, + parameterName, + itemIndex, + 'internal', + getAdditionalKeys(this.additionalData, 'internal', runExecutionData), + undefined, + fallbackValue, + options, + ); + } +}