refactor(core): Convert dynamic node-parameter routes to a decorated controller (no-changelog) (#7284)
1. Reduce a lot of code duplication 2. Move more endpoints out of `Server.ts` 3. Move all query-param parsing and validation into a middleware to make the route handlers simpler.
This commit is contained in:
committed by
GitHub
parent
05ed86c64b
commit
fc60e9a809
@@ -19,23 +19,12 @@ import type { ServeStaticOptions } from 'serve-static';
|
||||
import type { FindManyOptions, FindOptionsWhere } from 'typeorm';
|
||||
import { Not, In } from 'typeorm';
|
||||
|
||||
import {
|
||||
LoadMappingOptions,
|
||||
LoadNodeParameterOptions,
|
||||
LoadNodeListSearch,
|
||||
InstanceSettings,
|
||||
} from 'n8n-core';
|
||||
import { InstanceSettings } from 'n8n-core';
|
||||
|
||||
import type {
|
||||
INodeCredentials,
|
||||
INodeListSearchResult,
|
||||
INodeParameters,
|
||||
INodePropertyOptions,
|
||||
INodeTypeNameVersion,
|
||||
ICredentialTypes,
|
||||
ExecutionStatus,
|
||||
IExecutionsSummary,
|
||||
ResourceMapperFields,
|
||||
IN8nUISettings,
|
||||
} from 'n8n-workflow';
|
||||
import { jsonParse } from 'n8n-workflow';
|
||||
@@ -57,17 +46,11 @@ import {
|
||||
TEMPLATES_DIR,
|
||||
} from '@/constants';
|
||||
import { credentialsController } from '@/credentials/credentials.controller';
|
||||
import type {
|
||||
CurlHelper,
|
||||
ExecutionRequest,
|
||||
NodeListSearchRequest,
|
||||
NodeParameterOptionsRequest,
|
||||
ResourceMapperRequest,
|
||||
WorkflowRequest,
|
||||
} from '@/requests';
|
||||
import type { CurlHelper, ExecutionRequest, WorkflowRequest } from '@/requests';
|
||||
import { registerController } from '@/decorators';
|
||||
import { AuthController } from '@/controllers/auth.controller';
|
||||
import { BinaryDataController } from '@/controllers/binaryData.controller';
|
||||
import { DynamicNodeParametersController } from '@/controllers/dynamicNodeParameters.controller';
|
||||
import { LdapController } from '@/controllers/ldap.controller';
|
||||
import { MeController } from '@/controllers/me.controller';
|
||||
import { MFAController } from '@/controllers/mfa.controller';
|
||||
@@ -93,7 +76,6 @@ import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
|
||||
import { NodeTypes } from '@/NodeTypes';
|
||||
import * as ResponseHelper from '@/ResponseHelper';
|
||||
import { WaitTracker } from '@/WaitTracker';
|
||||
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
|
||||
import { toHttpNodeParameters } from '@/CurlConverterHelper';
|
||||
import { EventBusController } from '@/eventbus/eventBus.controller';
|
||||
import { EventBusControllerEE } from '@/eventbus/eventBus.controller.ee';
|
||||
@@ -277,6 +259,7 @@ export class Server extends AbstractServer {
|
||||
postHog,
|
||||
),
|
||||
Container.get(MeController),
|
||||
Container.get(DynamicNodeParametersController),
|
||||
new NodeTypesController(config, nodeTypes),
|
||||
Container.get(PasswordResetController),
|
||||
Container.get(TagsController),
|
||||
@@ -450,170 +433,6 @@ export class Server extends AbstractServer {
|
||||
this.logger.warn(`Source Control initialization failed: ${error.message}`);
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
// Returns parameter values which normally get loaded from an external API or
|
||||
// get generated dynamically
|
||||
this.app.get(
|
||||
`/${this.restEndpoint}/node-parameter-options`,
|
||||
ResponseHelper.send(
|
||||
async (req: NodeParameterOptionsRequest): Promise<INodePropertyOptions[]> => {
|
||||
const nodeTypeAndVersion = jsonParse(
|
||||
req.query.nodeTypeAndVersion,
|
||||
) as INodeTypeNameVersion;
|
||||
|
||||
const { path, methodName } = req.query;
|
||||
|
||||
const currentNodeParameters = jsonParse(
|
||||
req.query.currentNodeParameters,
|
||||
) as INodeParameters;
|
||||
|
||||
let credentials: INodeCredentials | undefined;
|
||||
|
||||
if (req.query.credentials) {
|
||||
credentials = jsonParse(req.query.credentials);
|
||||
}
|
||||
|
||||
const loadDataInstance = new LoadNodeParameterOptions(
|
||||
nodeTypeAndVersion,
|
||||
this.nodeTypes,
|
||||
path,
|
||||
currentNodeParameters,
|
||||
credentials,
|
||||
);
|
||||
|
||||
const additionalData = await WorkflowExecuteAdditionalData.getBase(
|
||||
req.user.id,
|
||||
currentNodeParameters,
|
||||
);
|
||||
|
||||
if (methodName) {
|
||||
return loadDataInstance.getOptionsViaMethodName(methodName, additionalData);
|
||||
}
|
||||
// @ts-ignore
|
||||
if (req.query.loadOptions) {
|
||||
return loadDataInstance.getOptionsViaRequestProperty(
|
||||
// @ts-ignore
|
||||
jsonParse(req.query.loadOptions as string),
|
||||
additionalData,
|
||||
);
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Returns parameter values which normally get loaded from an external API or
|
||||
// get generated dynamically
|
||||
this.app.get(
|
||||
`/${this.restEndpoint}/nodes-list-search`,
|
||||
ResponseHelper.send(
|
||||
async (
|
||||
req: NodeListSearchRequest,
|
||||
res: express.Response,
|
||||
): Promise<INodeListSearchResult | undefined> => {
|
||||
const nodeTypeAndVersion = jsonParse(
|
||||
req.query.nodeTypeAndVersion,
|
||||
) as INodeTypeNameVersion;
|
||||
|
||||
const { path, methodName } = req.query;
|
||||
|
||||
if (!req.query.currentNodeParameters) {
|
||||
throw new ResponseHelper.BadRequestError(
|
||||
'Parameter currentNodeParameters is required.',
|
||||
);
|
||||
}
|
||||
|
||||
const currentNodeParameters = jsonParse(
|
||||
req.query.currentNodeParameters,
|
||||
) as INodeParameters;
|
||||
|
||||
let credentials: INodeCredentials | undefined;
|
||||
|
||||
if (req.query.credentials) {
|
||||
credentials = jsonParse(req.query.credentials);
|
||||
}
|
||||
|
||||
const listSearchInstance = new LoadNodeListSearch(
|
||||
nodeTypeAndVersion,
|
||||
this.nodeTypes,
|
||||
path,
|
||||
currentNodeParameters,
|
||||
credentials,
|
||||
);
|
||||
|
||||
const additionalData = await WorkflowExecuteAdditionalData.getBase(
|
||||
req.user.id,
|
||||
currentNodeParameters,
|
||||
);
|
||||
|
||||
if (methodName) {
|
||||
return listSearchInstance.getOptionsViaMethodName(
|
||||
methodName,
|
||||
additionalData,
|
||||
req.query.filter,
|
||||
req.query.paginationToken,
|
||||
);
|
||||
}
|
||||
|
||||
throw new ResponseHelper.BadRequestError('Parameter methodName is required.');
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.app.get(
|
||||
`/${this.restEndpoint}/get-mapping-fields`,
|
||||
ResponseHelper.send(
|
||||
async (
|
||||
req: ResourceMapperRequest,
|
||||
res: express.Response,
|
||||
): Promise<ResourceMapperFields | undefined> => {
|
||||
const nodeTypeAndVersion = jsonParse(
|
||||
req.query.nodeTypeAndVersion,
|
||||
) as INodeTypeNameVersion;
|
||||
|
||||
const { path, methodName } = req.query;
|
||||
|
||||
if (!req.query.currentNodeParameters) {
|
||||
throw new ResponseHelper.BadRequestError(
|
||||
'Parameter currentNodeParameters is required.',
|
||||
);
|
||||
}
|
||||
|
||||
const currentNodeParameters = jsonParse(
|
||||
req.query.currentNodeParameters,
|
||||
) as INodeParameters;
|
||||
|
||||
let credentials: INodeCredentials | undefined;
|
||||
|
||||
if (req.query.credentials) {
|
||||
credentials = jsonParse(req.query.credentials);
|
||||
}
|
||||
|
||||
const loadMappingOptionsInstance = new LoadMappingOptions(
|
||||
nodeTypeAndVersion,
|
||||
this.nodeTypes,
|
||||
path,
|
||||
currentNodeParameters,
|
||||
credentials,
|
||||
);
|
||||
|
||||
const additionalData = await WorkflowExecuteAdditionalData.getBase(
|
||||
req.user.id,
|
||||
currentNodeParameters,
|
||||
);
|
||||
|
||||
const fields = await loadMappingOptionsInstance.getOptionsViaMethodName(
|
||||
methodName,
|
||||
additionalData,
|
||||
);
|
||||
|
||||
return fields;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// ----------------------------------------
|
||||
// Active Workflows
|
||||
// ----------------------------------------
|
||||
|
||||
120
packages/cli/src/controllers/dynamicNodeParameters.controller.ts
Normal file
120
packages/cli/src/controllers/dynamicNodeParameters.controller.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { Service } from 'typedi';
|
||||
import type { RequestHandler } from 'express';
|
||||
import { NextFunction, Response } from 'express';
|
||||
import type {
|
||||
INodeListSearchResult,
|
||||
INodePropertyOptions,
|
||||
ResourceMapperFields,
|
||||
} from 'n8n-workflow';
|
||||
import { jsonParse } from 'n8n-workflow';
|
||||
|
||||
import { Authorized, Get, Middleware, RestController } from '@/decorators';
|
||||
import { getBase } from '@/WorkflowExecuteAdditionalData';
|
||||
import { DynamicNodeParametersService } from '@/services/dynamicNodeParameters.service';
|
||||
import { DynamicNodeParametersRequest } from '@/requests';
|
||||
import { BadRequestError } from '@/ResponseHelper';
|
||||
|
||||
const assertMethodName: RequestHandler = (req, res, next) => {
|
||||
const { methodName } = req.query as DynamicNodeParametersRequest.BaseRequest['query'];
|
||||
if (!methodName) {
|
||||
throw new BadRequestError('Parameter methodName is required.');
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
@Service()
|
||||
@Authorized()
|
||||
@RestController('/dynamic-node-parameters')
|
||||
export class DynamicNodeParametersController {
|
||||
constructor(private readonly service: DynamicNodeParametersService) {}
|
||||
|
||||
@Middleware()
|
||||
parseQueryParams(
|
||||
req: DynamicNodeParametersRequest.BaseRequest,
|
||||
res: Response,
|
||||
next: NextFunction,
|
||||
) {
|
||||
const { credentials, currentNodeParameters, nodeTypeAndVersion } = req.query;
|
||||
if (!nodeTypeAndVersion) {
|
||||
throw new BadRequestError('Parameter nodeTypeAndVersion is required.');
|
||||
}
|
||||
if (!currentNodeParameters) {
|
||||
throw new BadRequestError('Parameter currentNodeParameters is required.');
|
||||
}
|
||||
|
||||
req.params = {
|
||||
nodeTypeAndVersion: jsonParse(nodeTypeAndVersion),
|
||||
currentNodeParameters: jsonParse(currentNodeParameters),
|
||||
credentials: credentials ? jsonParse(credentials) : undefined,
|
||||
};
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
/** Returns parameter values which normally get loaded from an external API or get generated dynamically */
|
||||
@Get('/options')
|
||||
async getOptions(req: DynamicNodeParametersRequest.Options): Promise<INodePropertyOptions[]> {
|
||||
const { path, methodName, loadOptions } = req.query;
|
||||
const { credentials, currentNodeParameters, nodeTypeAndVersion } = req.params;
|
||||
const additionalData = await getBase(req.user.id, currentNodeParameters);
|
||||
|
||||
if (methodName) {
|
||||
return this.service.getOptionsViaMethodName(
|
||||
methodName,
|
||||
path,
|
||||
additionalData,
|
||||
nodeTypeAndVersion,
|
||||
currentNodeParameters,
|
||||
credentials,
|
||||
);
|
||||
}
|
||||
|
||||
if (loadOptions) {
|
||||
return this.service.getOptionsViaLoadOptions(
|
||||
jsonParse(loadOptions),
|
||||
additionalData,
|
||||
nodeTypeAndVersion,
|
||||
currentNodeParameters,
|
||||
credentials,
|
||||
);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
@Get('/resource-locator-results', { middlewares: [assertMethodName] })
|
||||
async getResourceLocatorResults(
|
||||
req: DynamicNodeParametersRequest.ResourceLocatorResults,
|
||||
): Promise<INodeListSearchResult | undefined> {
|
||||
const { path, methodName, filter, paginationToken } = req.query;
|
||||
const { credentials, currentNodeParameters, nodeTypeAndVersion } = req.params;
|
||||
const additionalData = await getBase(req.user.id, currentNodeParameters);
|
||||
return this.service.getResourceLocatorResults(
|
||||
methodName,
|
||||
path,
|
||||
additionalData,
|
||||
nodeTypeAndVersion,
|
||||
currentNodeParameters,
|
||||
credentials,
|
||||
filter,
|
||||
paginationToken,
|
||||
);
|
||||
}
|
||||
|
||||
@Get('/resource-mapper-fields', { middlewares: [assertMethodName] })
|
||||
async getResourceMappingFields(
|
||||
req: DynamicNodeParametersRequest.ResourceMapperFields,
|
||||
): Promise<ResourceMapperFields | undefined> {
|
||||
const { path, methodName } = req.query;
|
||||
const { credentials, currentNodeParameters, nodeTypeAndVersion } = req.params;
|
||||
const additionalData = await getBase(req.user.id, currentNodeParameters);
|
||||
return this.service.getResourceMappingFields(
|
||||
methodName,
|
||||
path,
|
||||
additionalData,
|
||||
nodeTypeAndVersion,
|
||||
currentNodeParameters,
|
||||
credentials,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,9 @@ import type {
|
||||
IDataObject,
|
||||
INode,
|
||||
INodeCredentialTestRequest,
|
||||
INodeCredentials,
|
||||
INodeParameters,
|
||||
INodeTypeNameVersion,
|
||||
IPinData,
|
||||
IRunData,
|
||||
IUser,
|
||||
@@ -403,57 +406,43 @@ export declare namespace OAuthRequest {
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// /node-parameter-options
|
||||
// /dynamic-node-parameters
|
||||
// ----------------------------------
|
||||
export declare namespace DynamicNodeParametersRequest {
|
||||
type BaseRequest<QueryParams = {}> = AuthenticatedRequest<
|
||||
{
|
||||
nodeTypeAndVersion: INodeTypeNameVersion;
|
||||
currentNodeParameters: INodeParameters;
|
||||
credentials?: INodeCredentials;
|
||||
},
|
||||
{},
|
||||
{},
|
||||
{
|
||||
path: string;
|
||||
nodeTypeAndVersion: string;
|
||||
currentNodeParameters: string;
|
||||
methodName?: string;
|
||||
credentials?: string;
|
||||
} & QueryParams
|
||||
>;
|
||||
|
||||
export type NodeParameterOptionsRequest = AuthenticatedRequest<
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{
|
||||
nodeTypeAndVersion: string;
|
||||
/** GET /dynamic-node-parameters/options */
|
||||
type Options = BaseRequest<{
|
||||
loadOptions?: string;
|
||||
}>;
|
||||
|
||||
/** GET /dynamic-node-parameters/resource-locator-results */
|
||||
type ResourceLocatorResults = BaseRequest<{
|
||||
methodName: string;
|
||||
path: string;
|
||||
currentNodeParameters: string;
|
||||
credentials: string;
|
||||
}
|
||||
>;
|
||||
|
||||
// ----------------------------------
|
||||
// /node-list-search
|
||||
// ----------------------------------
|
||||
|
||||
export type NodeListSearchRequest = AuthenticatedRequest<
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{
|
||||
nodeTypeAndVersion: string;
|
||||
methodName: string;
|
||||
path: string;
|
||||
currentNodeParameters: string;
|
||||
credentials: string;
|
||||
filter?: string;
|
||||
paginationToken?: string;
|
||||
}
|
||||
>;
|
||||
}>;
|
||||
|
||||
// ----------------------------------
|
||||
// /get-mapping-fields
|
||||
// ----------------------------------
|
||||
|
||||
export type ResourceMapperRequest = AuthenticatedRequest<
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{
|
||||
nodeTypeAndVersion: string;
|
||||
/** GET dynamic-node-parameters/resource-mapper-fields */
|
||||
type ResourceMapperFields = BaseRequest<{
|
||||
methodName: string;
|
||||
path: string;
|
||||
currentNodeParameters: string;
|
||||
credentials: string;
|
||||
}
|
||||
>;
|
||||
}>;
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// /tags
|
||||
|
||||
230
packages/cli/src/services/dynamicNodeParameters.service.ts
Normal file
230
packages/cli/src/services/dynamicNodeParameters.service.ts
Normal file
@@ -0,0 +1,230 @@
|
||||
import { Service } from 'typedi';
|
||||
import type {
|
||||
ILoadOptions,
|
||||
ILoadOptionsFunctions,
|
||||
INode,
|
||||
INodeExecutionData,
|
||||
INodeListSearchResult,
|
||||
INodeProperties,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
IRunExecutionData,
|
||||
ITaskDataConnections,
|
||||
IWorkflowExecuteAdditionalData,
|
||||
ResourceMapperFields,
|
||||
INodeCredentials,
|
||||
INodeParameters,
|
||||
INodeTypeNameVersion,
|
||||
} from 'n8n-workflow';
|
||||
import { Workflow, RoutingNode } from 'n8n-workflow';
|
||||
import { NodeExecuteFunctions } from 'n8n-core';
|
||||
import { NodeTypes } from '@/NodeTypes';
|
||||
|
||||
@Service()
|
||||
export class DynamicNodeParametersService {
|
||||
constructor(private nodeTypes: NodeTypes) {}
|
||||
|
||||
/** Returns the available options via a predefined method */
|
||||
async getOptionsViaMethodName(
|
||||
methodName: string,
|
||||
path: string,
|
||||
additionalData: IWorkflowExecuteAdditionalData,
|
||||
nodeTypeAndVersion: INodeTypeNameVersion,
|
||||
currentNodeParameters: INodeParameters,
|
||||
credentials?: INodeCredentials,
|
||||
): Promise<INodePropertyOptions[]> {
|
||||
const nodeType = this.getNodeType(nodeTypeAndVersion);
|
||||
const method = this.getMethod('loadOptions', methodName, nodeType);
|
||||
const workflow = this.getWorkflow(nodeTypeAndVersion, currentNodeParameters, credentials);
|
||||
const thisArgs = this.getThisArg(path, additionalData, workflow);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return method.call(thisArgs);
|
||||
}
|
||||
|
||||
/** Returns the available options via a loadOptions param */
|
||||
async getOptionsViaLoadOptions(
|
||||
loadOptions: ILoadOptions,
|
||||
additionalData: IWorkflowExecuteAdditionalData,
|
||||
nodeTypeAndVersion: INodeTypeNameVersion,
|
||||
currentNodeParameters: INodeParameters,
|
||||
credentials?: INodeCredentials,
|
||||
): Promise<INodePropertyOptions[]> {
|
||||
const nodeType = this.getNodeType(nodeTypeAndVersion);
|
||||
if (!nodeType.description.requestDefaults?.baseURL) {
|
||||
// This in in here for now for security reasons.
|
||||
// Background: As the full data for the request to make does get send, and the auth data
|
||||
// will then be applied, would it be possible to retrieve that data like that. By at least
|
||||
// requiring a baseURL to be defined can at least not a random server be called.
|
||||
// In the future this code has to get improved that it does not use the request information from
|
||||
// the request rather resolves it via the parameter-path and nodeType data.
|
||||
throw new Error(
|
||||
`The node-type "${nodeType.description.name}" does not exist or does not have "requestDefaults.baseURL" defined!`,
|
||||
);
|
||||
}
|
||||
|
||||
const mode = 'internal';
|
||||
const runIndex = 0;
|
||||
const connectionInputData: INodeExecutionData[] = [];
|
||||
const runExecutionData: IRunExecutionData = { resultData: { runData: {} } };
|
||||
const workflow = this.getWorkflow(nodeTypeAndVersion, currentNodeParameters, credentials);
|
||||
const node = workflow.nodes[0];
|
||||
|
||||
const routingNode = new RoutingNode(
|
||||
workflow,
|
||||
node,
|
||||
connectionInputData,
|
||||
runExecutionData ?? null,
|
||||
additionalData,
|
||||
mode,
|
||||
);
|
||||
|
||||
// Create copy of node-type with the single property we want to get the data off
|
||||
const tempNode: INodeType = {
|
||||
...nodeType,
|
||||
...{
|
||||
description: {
|
||||
...nodeType.description,
|
||||
properties: [
|
||||
{
|
||||
displayName: '',
|
||||
type: 'string',
|
||||
name: '',
|
||||
default: '',
|
||||
routing: loadOptions.routing,
|
||||
} as INodeProperties,
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const inputData: ITaskDataConnections = {
|
||||
main: [[{ json: {} }]],
|
||||
};
|
||||
|
||||
const optionsData = await routingNode.runNode(
|
||||
inputData,
|
||||
runIndex,
|
||||
tempNode,
|
||||
{ node, source: null, data: {} },
|
||||
NodeExecuteFunctions,
|
||||
);
|
||||
|
||||
if (optionsData?.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!Array.isArray(optionsData)) {
|
||||
throw new Error('The returned data is not an array!');
|
||||
}
|
||||
|
||||
return optionsData[0].map((item) => item.json) as unknown as INodePropertyOptions[];
|
||||
}
|
||||
|
||||
async getResourceLocatorResults(
|
||||
methodName: string,
|
||||
path: string,
|
||||
additionalData: IWorkflowExecuteAdditionalData,
|
||||
nodeTypeAndVersion: INodeTypeNameVersion,
|
||||
currentNodeParameters: INodeParameters,
|
||||
credentials?: INodeCredentials,
|
||||
filter?: string,
|
||||
paginationToken?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const nodeType = this.getNodeType(nodeTypeAndVersion);
|
||||
const method = this.getMethod('listSearch', methodName, nodeType);
|
||||
const workflow = this.getWorkflow(nodeTypeAndVersion, currentNodeParameters, credentials);
|
||||
const thisArgs = this.getThisArg(path, additionalData, workflow);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return method.call(thisArgs, filter, paginationToken);
|
||||
}
|
||||
|
||||
/** Returns the available mapping fields for the ResourceMapper component */
|
||||
async getResourceMappingFields(
|
||||
methodName: string,
|
||||
path: string,
|
||||
additionalData: IWorkflowExecuteAdditionalData,
|
||||
nodeTypeAndVersion: INodeTypeNameVersion,
|
||||
currentNodeParameters: INodeParameters,
|
||||
credentials?: INodeCredentials,
|
||||
): Promise<ResourceMapperFields> {
|
||||
const nodeType = this.getNodeType(nodeTypeAndVersion);
|
||||
const method = this.getMethod('resourceMapping', methodName, nodeType);
|
||||
const workflow = this.getWorkflow(nodeTypeAndVersion, currentNodeParameters, credentials);
|
||||
const thisArgs = this.getThisArg(path, additionalData, workflow);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return method.call(thisArgs);
|
||||
}
|
||||
|
||||
private getMethod(
|
||||
type: 'resourceMapping',
|
||||
methodName: string,
|
||||
nodeType: INodeType,
|
||||
): (this: ILoadOptionsFunctions) => Promise<ResourceMapperFields>;
|
||||
private getMethod(
|
||||
type: 'listSearch',
|
||||
methodName: string,
|
||||
nodeType: INodeType,
|
||||
): (
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string | undefined,
|
||||
paginationToken?: string | undefined,
|
||||
) => Promise<INodeListSearchResult>;
|
||||
private getMethod(
|
||||
type: 'loadOptions',
|
||||
methodName: string,
|
||||
nodeType: INodeType,
|
||||
): (this: ILoadOptionsFunctions) => Promise<INodePropertyOptions[]>;
|
||||
|
||||
private getMethod(
|
||||
type: 'resourceMapping' | 'listSearch' | 'loadOptions',
|
||||
methodName: string,
|
||||
nodeType: INodeType,
|
||||
) {
|
||||
const method = nodeType.methods?.[type]?.[methodName];
|
||||
if (typeof method !== 'function') {
|
||||
throw new Error(
|
||||
`The node-type "${nodeType.description.name}" does not have the method "${methodName}" defined!`,
|
||||
);
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
private getNodeType({ name, version }: INodeTypeNameVersion) {
|
||||
return this.nodeTypes.getByNameAndVersion(name, version);
|
||||
}
|
||||
|
||||
private getWorkflow(
|
||||
nodeTypeAndVersion: INodeTypeNameVersion,
|
||||
currentNodeParameters: INodeParameters,
|
||||
credentials?: INodeCredentials,
|
||||
) {
|
||||
const node: INode = {
|
||||
parameters: currentNodeParameters,
|
||||
id: 'uuid-1234',
|
||||
name: 'Temp-Node',
|
||||
type: nodeTypeAndVersion.name,
|
||||
typeVersion: nodeTypeAndVersion.version,
|
||||
position: [0, 0],
|
||||
};
|
||||
|
||||
if (credentials) {
|
||||
node.credentials = credentials;
|
||||
}
|
||||
|
||||
return new Workflow({
|
||||
nodes: [node],
|
||||
connections: {},
|
||||
active: false,
|
||||
nodeTypes: this.nodeTypes,
|
||||
});
|
||||
}
|
||||
|
||||
private getThisArg(
|
||||
path: string,
|
||||
additionalData: IWorkflowExecuteAdditionalData,
|
||||
workflow: Workflow,
|
||||
) {
|
||||
const node = Object.values(workflow.nodes)[0];
|
||||
return NodeExecuteFunctions.getLoadOptionsFunctions(workflow, node, path, additionalData);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user