mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
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:
parent
05ed86c64b
commit
fc60e9a809
|
@ -361,7 +361,7 @@ describe('NDV', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not retrieve remote options when a parameter value changes', () => {
|
it('should not retrieve remote options when a parameter value changes', () => {
|
||||||
cy.intercept('/rest/node-parameter-options?**', cy.spy().as('fetchParameterOptions'));
|
cy.intercept('/rest/dynamic-node-parameters/options?**', cy.spy().as('fetchParameterOptions'));
|
||||||
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' });
|
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' });
|
||||||
// Type something into the field
|
// Type something into the field
|
||||||
ndv.actions.typeIntoParameterInput('otherField', 'test');
|
ndv.actions.typeIntoParameterInput('otherField', 'test');
|
||||||
|
|
|
@ -19,23 +19,12 @@ import type { ServeStaticOptions } from 'serve-static';
|
||||||
import type { FindManyOptions, FindOptionsWhere } from 'typeorm';
|
import type { FindManyOptions, FindOptionsWhere } from 'typeorm';
|
||||||
import { Not, In } from 'typeorm';
|
import { Not, In } from 'typeorm';
|
||||||
|
|
||||||
import {
|
import { InstanceSettings } from 'n8n-core';
|
||||||
LoadMappingOptions,
|
|
||||||
LoadNodeParameterOptions,
|
|
||||||
LoadNodeListSearch,
|
|
||||||
InstanceSettings,
|
|
||||||
} from 'n8n-core';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
INodeCredentials,
|
|
||||||
INodeListSearchResult,
|
|
||||||
INodeParameters,
|
|
||||||
INodePropertyOptions,
|
|
||||||
INodeTypeNameVersion,
|
|
||||||
ICredentialTypes,
|
ICredentialTypes,
|
||||||
ExecutionStatus,
|
ExecutionStatus,
|
||||||
IExecutionsSummary,
|
IExecutionsSummary,
|
||||||
ResourceMapperFields,
|
|
||||||
IN8nUISettings,
|
IN8nUISettings,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { jsonParse } from 'n8n-workflow';
|
import { jsonParse } from 'n8n-workflow';
|
||||||
|
@ -57,17 +46,11 @@ import {
|
||||||
TEMPLATES_DIR,
|
TEMPLATES_DIR,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
import { credentialsController } from '@/credentials/credentials.controller';
|
import { credentialsController } from '@/credentials/credentials.controller';
|
||||||
import type {
|
import type { CurlHelper, ExecutionRequest, WorkflowRequest } from '@/requests';
|
||||||
CurlHelper,
|
|
||||||
ExecutionRequest,
|
|
||||||
NodeListSearchRequest,
|
|
||||||
NodeParameterOptionsRequest,
|
|
||||||
ResourceMapperRequest,
|
|
||||||
WorkflowRequest,
|
|
||||||
} from '@/requests';
|
|
||||||
import { registerController } from '@/decorators';
|
import { registerController } from '@/decorators';
|
||||||
import { AuthController } from '@/controllers/auth.controller';
|
import { AuthController } from '@/controllers/auth.controller';
|
||||||
import { BinaryDataController } from '@/controllers/binaryData.controller';
|
import { BinaryDataController } from '@/controllers/binaryData.controller';
|
||||||
|
import { DynamicNodeParametersController } from '@/controllers/dynamicNodeParameters.controller';
|
||||||
import { LdapController } from '@/controllers/ldap.controller';
|
import { LdapController } from '@/controllers/ldap.controller';
|
||||||
import { MeController } from '@/controllers/me.controller';
|
import { MeController } from '@/controllers/me.controller';
|
||||||
import { MFAController } from '@/controllers/mfa.controller';
|
import { MFAController } from '@/controllers/mfa.controller';
|
||||||
|
@ -93,7 +76,6 @@ import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
|
||||||
import { NodeTypes } from '@/NodeTypes';
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
import * as ResponseHelper from '@/ResponseHelper';
|
||||||
import { WaitTracker } from '@/WaitTracker';
|
import { WaitTracker } from '@/WaitTracker';
|
||||||
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
|
|
||||||
import { toHttpNodeParameters } from '@/CurlConverterHelper';
|
import { toHttpNodeParameters } from '@/CurlConverterHelper';
|
||||||
import { EventBusController } from '@/eventbus/eventBus.controller';
|
import { EventBusController } from '@/eventbus/eventBus.controller';
|
||||||
import { EventBusControllerEE } from '@/eventbus/eventBus.controller.ee';
|
import { EventBusControllerEE } from '@/eventbus/eventBus.controller.ee';
|
||||||
|
@ -277,6 +259,7 @@ export class Server extends AbstractServer {
|
||||||
postHog,
|
postHog,
|
||||||
),
|
),
|
||||||
Container.get(MeController),
|
Container.get(MeController),
|
||||||
|
Container.get(DynamicNodeParametersController),
|
||||||
new NodeTypesController(config, nodeTypes),
|
new NodeTypesController(config, nodeTypes),
|
||||||
Container.get(PasswordResetController),
|
Container.get(PasswordResetController),
|
||||||
Container.get(TagsController),
|
Container.get(TagsController),
|
||||||
|
@ -450,170 +433,6 @@ export class Server extends AbstractServer {
|
||||||
this.logger.warn(`Source Control initialization failed: ${error.message}`);
|
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
|
// 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,
|
IDataObject,
|
||||||
INode,
|
INode,
|
||||||
INodeCredentialTestRequest,
|
INodeCredentialTestRequest,
|
||||||
|
INodeCredentials,
|
||||||
|
INodeParameters,
|
||||||
|
INodeTypeNameVersion,
|
||||||
IPinData,
|
IPinData,
|
||||||
IRunData,
|
IRunData,
|
||||||
IUser,
|
IUser,
|
||||||
|
@ -403,57 +406,43 @@ export declare namespace OAuthRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// /node-parameter-options
|
// /dynamic-node-parameters
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
export declare namespace DynamicNodeParametersRequest {
|
||||||
export type NodeParameterOptionsRequest = AuthenticatedRequest<
|
type BaseRequest<QueryParams = {}> = AuthenticatedRequest<
|
||||||
{},
|
{
|
||||||
|
nodeTypeAndVersion: INodeTypeNameVersion;
|
||||||
|
currentNodeParameters: INodeParameters;
|
||||||
|
credentials?: INodeCredentials;
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
nodeTypeAndVersion: string;
|
|
||||||
methodName: string;
|
|
||||||
path: string;
|
path: string;
|
||||||
|
nodeTypeAndVersion: string;
|
||||||
currentNodeParameters: string;
|
currentNodeParameters: string;
|
||||||
credentials: string;
|
methodName?: string;
|
||||||
}
|
credentials?: string;
|
||||||
|
} & QueryParams
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// ----------------------------------
|
/** GET /dynamic-node-parameters/options */
|
||||||
// /node-list-search
|
type Options = BaseRequest<{
|
||||||
// ----------------------------------
|
loadOptions?: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
export type NodeListSearchRequest = AuthenticatedRequest<
|
/** GET /dynamic-node-parameters/resource-locator-results */
|
||||||
{},
|
type ResourceLocatorResults = BaseRequest<{
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
nodeTypeAndVersion: string;
|
|
||||||
methodName: string;
|
methodName: string;
|
||||||
path: string;
|
|
||||||
currentNodeParameters: string;
|
|
||||||
credentials: string;
|
|
||||||
filter?: string;
|
filter?: string;
|
||||||
paginationToken?: string;
|
paginationToken?: string;
|
||||||
}
|
}>;
|
||||||
>;
|
|
||||||
|
|
||||||
// ----------------------------------
|
/** GET dynamic-node-parameters/resource-mapper-fields */
|
||||||
// /get-mapping-fields
|
type ResourceMapperFields = BaseRequest<{
|
||||||
// ----------------------------------
|
|
||||||
|
|
||||||
export type ResourceMapperRequest = AuthenticatedRequest<
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
nodeTypeAndVersion: string;
|
|
||||||
methodName: string;
|
methodName: string;
|
||||||
path: string;
|
}>;
|
||||||
currentNodeParameters: string;
|
|
||||||
credentials: string;
|
|
||||||
}
|
}
|
||||||
>;
|
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// /tags
|
// /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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +0,0 @@
|
||||||
import type { IWorkflowExecuteAdditionalData, ResourceMapperFields } from 'n8n-workflow';
|
|
||||||
|
|
||||||
import * as NodeExecuteFunctions from './NodeExecuteFunctions';
|
|
||||||
import { LoadNodeDetails } from './LoadNodeDetails';
|
|
||||||
|
|
||||||
export class LoadMappingOptions extends LoadNodeDetails {
|
|
||||||
/**
|
|
||||||
* Returns the available mapping fields for the ResourceMapper component
|
|
||||||
*/
|
|
||||||
async getOptionsViaMethodName(
|
|
||||||
methodName: string,
|
|
||||||
additionalData: IWorkflowExecuteAdditionalData,
|
|
||||||
): Promise<ResourceMapperFields> {
|
|
||||||
const node = this.getTempNode();
|
|
||||||
|
|
||||||
const nodeType = this.workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
|
|
||||||
const method = nodeType?.methods?.resourceMapping?.[methodName];
|
|
||||||
|
|
||||||
if (typeof method !== 'function') {
|
|
||||||
throw new Error(
|
|
||||||
`The node-type "${node.type}" does not have the method "${methodName}" defined!`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const thisArgs = NodeExecuteFunctions.getLoadOptionsFunctions(
|
|
||||||
this.workflow,
|
|
||||||
node,
|
|
||||||
this.path,
|
|
||||||
additionalData,
|
|
||||||
);
|
|
||||||
|
|
||||||
return method.call(thisArgs);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
import type { INode } from 'n8n-workflow';
|
|
||||||
import {
|
|
||||||
Workflow,
|
|
||||||
INodeCredentials,
|
|
||||||
INodeParameters,
|
|
||||||
INodeTypeNameVersion,
|
|
||||||
INodeTypes,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
|
|
||||||
const TEMP_NODE_NAME = 'Temp-Node';
|
|
||||||
const TEMP_WORKFLOW_NAME = 'Temp-Workflow';
|
|
||||||
|
|
||||||
export abstract class LoadNodeDetails {
|
|
||||||
path: string;
|
|
||||||
|
|
||||||
workflow: Workflow;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
nodeTypeNameAndVersion: INodeTypeNameVersion,
|
|
||||||
nodeTypes: INodeTypes,
|
|
||||||
path: string,
|
|
||||||
currentNodeParameters: INodeParameters,
|
|
||||||
credentials?: INodeCredentials,
|
|
||||||
) {
|
|
||||||
const nodeType = nodeTypes.getByNameAndVersion(
|
|
||||||
nodeTypeNameAndVersion.name,
|
|
||||||
nodeTypeNameAndVersion.version,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.path = path;
|
|
||||||
|
|
||||||
if (nodeType === undefined) {
|
|
||||||
throw new Error(
|
|
||||||
`The node-type "${nodeTypeNameAndVersion.name} v${nodeTypeNameAndVersion.version}" is not known!`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodeData: INode = {
|
|
||||||
parameters: currentNodeParameters,
|
|
||||||
id: 'uuid-1234',
|
|
||||||
name: TEMP_NODE_NAME,
|
|
||||||
type: nodeTypeNameAndVersion.name,
|
|
||||||
typeVersion: nodeTypeNameAndVersion.version,
|
|
||||||
position: [0, 0],
|
|
||||||
};
|
|
||||||
|
|
||||||
if (credentials) {
|
|
||||||
nodeData.credentials = credentials;
|
|
||||||
}
|
|
||||||
|
|
||||||
const workflowData = {
|
|
||||||
nodes: [nodeData],
|
|
||||||
connections: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
this.workflow = new Workflow({
|
|
||||||
nodes: workflowData.nodes,
|
|
||||||
connections: workflowData.connections,
|
|
||||||
active: false,
|
|
||||||
nodeTypes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns data of a fake workflow
|
|
||||||
*/
|
|
||||||
getWorkflowData() {
|
|
||||||
return {
|
|
||||||
name: TEMP_WORKFLOW_NAME,
|
|
||||||
active: false,
|
|
||||||
connections: {},
|
|
||||||
nodes: Object.values(this.workflow.nodes),
|
|
||||||
createdAt: new Date(),
|
|
||||||
updatedAt: new Date(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getTempNode() {
|
|
||||||
return this.workflow.getNode(TEMP_NODE_NAME)!;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
import type { INodeListSearchResult, IWorkflowExecuteAdditionalData } from 'n8n-workflow';
|
|
||||||
|
|
||||||
import * as NodeExecuteFunctions from './NodeExecuteFunctions';
|
|
||||||
import { LoadNodeDetails } from './LoadNodeDetails';
|
|
||||||
|
|
||||||
export class LoadNodeListSearch extends LoadNodeDetails {
|
|
||||||
/**
|
|
||||||
* Returns the available options via a predefined method
|
|
||||||
*/
|
|
||||||
async getOptionsViaMethodName(
|
|
||||||
methodName: string,
|
|
||||||
additionalData: IWorkflowExecuteAdditionalData,
|
|
||||||
filter?: string,
|
|
||||||
paginationToken?: string,
|
|
||||||
): Promise<INodeListSearchResult> {
|
|
||||||
const node = this.getTempNode();
|
|
||||||
|
|
||||||
const nodeType = this.workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
|
|
||||||
const method = nodeType?.methods?.listSearch?.[methodName];
|
|
||||||
|
|
||||||
if (typeof method !== 'function') {
|
|
||||||
throw new Error(
|
|
||||||
`The node-type "${node.type}" does not have the method "${methodName}" defined!`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const thisArgs = NodeExecuteFunctions.getLoadOptionsFunctions(
|
|
||||||
this.workflow,
|
|
||||||
node,
|
|
||||||
this.path,
|
|
||||||
additionalData,
|
|
||||||
);
|
|
||||||
|
|
||||||
return method.call(thisArgs, filter, paginationToken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,123 +0,0 @@
|
||||||
import type {
|
|
||||||
ILoadOptions,
|
|
||||||
INodeExecutionData,
|
|
||||||
INodeProperties,
|
|
||||||
INodePropertyOptions,
|
|
||||||
INodeType,
|
|
||||||
IRunExecutionData,
|
|
||||||
ITaskDataConnections,
|
|
||||||
IWorkflowExecuteAdditionalData,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
import { RoutingNode } from 'n8n-workflow';
|
|
||||||
|
|
||||||
import * as NodeExecuteFunctions from './NodeExecuteFunctions';
|
|
||||||
import { LoadNodeDetails } from './LoadNodeDetails';
|
|
||||||
|
|
||||||
export class LoadNodeParameterOptions extends LoadNodeDetails {
|
|
||||||
/**
|
|
||||||
* Returns the available options via a predefined method
|
|
||||||
*/
|
|
||||||
async getOptionsViaMethodName(
|
|
||||||
methodName: string,
|
|
||||||
additionalData: IWorkflowExecuteAdditionalData,
|
|
||||||
): Promise<INodePropertyOptions[]> {
|
|
||||||
const node = this.getTempNode();
|
|
||||||
|
|
||||||
const nodeType = this.workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
|
|
||||||
const method = nodeType?.methods?.loadOptions?.[methodName];
|
|
||||||
|
|
||||||
if (typeof method !== 'function') {
|
|
||||||
throw new Error(
|
|
||||||
`The node-type "${node.type}" does not have the method "${methodName}" defined!`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const thisArgs = NodeExecuteFunctions.getLoadOptionsFunctions(
|
|
||||||
this.workflow,
|
|
||||||
node,
|
|
||||||
this.path,
|
|
||||||
additionalData,
|
|
||||||
);
|
|
||||||
|
|
||||||
return method.call(thisArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the available options via a load request information
|
|
||||||
*/
|
|
||||||
async getOptionsViaRequestProperty(
|
|
||||||
loadOptions: ILoadOptions,
|
|
||||||
additionalData: IWorkflowExecuteAdditionalData,
|
|
||||||
): Promise<INodePropertyOptions[]> {
|
|
||||||
const node = this.getTempNode();
|
|
||||||
|
|
||||||
const nodeType = this.workflow.nodeTypes.getByNameAndVersion(node.type, node?.typeVersion);
|
|
||||||
|
|
||||||
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 "${node.type}" 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 routingNode = new RoutingNode(
|
|
||||||
this.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[];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,9 +10,6 @@ export * from './Credentials';
|
||||||
export * from './DirectoryLoader';
|
export * from './DirectoryLoader';
|
||||||
export * from './Interfaces';
|
export * from './Interfaces';
|
||||||
export { InstanceSettings } from './InstanceSettings';
|
export { InstanceSettings } from './InstanceSettings';
|
||||||
export * from './LoadMappingOptions';
|
|
||||||
export * from './LoadNodeParameterOptions';
|
|
||||||
export * from './LoadNodeListSearch';
|
|
||||||
export * from './NodeExecuteFunctions';
|
export * from './NodeExecuteFunctions';
|
||||||
export * from './WorkflowExecute';
|
export * from './WorkflowExecute';
|
||||||
export { NodeExecuteFunctions };
|
export { NodeExecuteFunctions };
|
||||||
|
|
|
@ -1355,17 +1355,6 @@ export interface ITabBarItem {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IResourceLocatorReqParams {
|
|
||||||
nodeTypeAndVersion: INodeTypeNameVersion;
|
|
||||||
path: string;
|
|
||||||
methodName?: string;
|
|
||||||
searchList?: ILoadOptions;
|
|
||||||
currentNodeParameters: INodeParameters;
|
|
||||||
credentials?: INodeCredentials;
|
|
||||||
filter?: string;
|
|
||||||
paginationToken?: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IResourceLocatorResultExpanded extends INodeListSearchItems {
|
export interface IResourceLocatorResultExpanded extends INodeListSearchItems {
|
||||||
linkAlt?: string;
|
linkAlt?: string;
|
||||||
}
|
}
|
||||||
|
@ -1473,13 +1462,30 @@ export type NodeAuthenticationOption = {
|
||||||
displayOptions?: IDisplayOptions;
|
displayOptions?: IDisplayOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ResourceMapperReqParams {
|
export declare namespace DynamicNodeParameters {
|
||||||
nodeTypeAndVersion: INodeTypeNameVersion;
|
interface BaseRequest {
|
||||||
path: string;
|
path: string;
|
||||||
methodName?: string;
|
nodeTypeAndVersion: INodeTypeNameVersion;
|
||||||
currentNodeParameters: INodeParameters;
|
currentNodeParameters: INodeParameters;
|
||||||
|
methodName?: string;
|
||||||
credentials?: INodeCredentials;
|
credentials?: INodeCredentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface OptionsRequest extends BaseRequest {
|
||||||
|
loadOptions?: ILoadOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResourceLocatorResultsRequest extends BaseRequest {
|
||||||
|
methodName: string;
|
||||||
|
filter?: string;
|
||||||
|
paginationToken?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResourceMapperFieldsRequest extends BaseRequest {
|
||||||
|
methodName: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface EnvironmentVariable {
|
export interface EnvironmentVariable {
|
||||||
id: number;
|
id: number;
|
||||||
key: string;
|
key: string;
|
||||||
|
|
|
@ -1,16 +1,7 @@
|
||||||
import { makeRestApiRequest } from '@/utils/apiUtils';
|
import { makeRestApiRequest } from '@/utils/apiUtils';
|
||||||
|
import type { DynamicNodeParameters, INodeTranslationHeaders, IRestApiContext } from '@/Interface';
|
||||||
import type {
|
import type {
|
||||||
INodeTranslationHeaders,
|
|
||||||
IResourceLocatorReqParams,
|
|
||||||
IRestApiContext,
|
|
||||||
ResourceMapperReqParams,
|
|
||||||
} from '@/Interface';
|
|
||||||
import type {
|
|
||||||
IDataObject,
|
|
||||||
ILoadOptions,
|
|
||||||
INodeCredentials,
|
|
||||||
INodeListSearchResult,
|
INodeListSearchResult,
|
||||||
INodeParameters,
|
|
||||||
INodePropertyOptions,
|
INodePropertyOptions,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
INodeTypeNameVersion,
|
INodeTypeNameVersion,
|
||||||
|
@ -38,38 +29,31 @@ export async function getNodesInformation(
|
||||||
|
|
||||||
export async function getNodeParameterOptions(
|
export async function getNodeParameterOptions(
|
||||||
context: IRestApiContext,
|
context: IRestApiContext,
|
||||||
sendData: {
|
sendData: DynamicNodeParameters.OptionsRequest,
|
||||||
nodeTypeAndVersion: INodeTypeNameVersion;
|
|
||||||
path: string;
|
|
||||||
methodName?: string;
|
|
||||||
loadOptions?: ILoadOptions;
|
|
||||||
currentNodeParameters: INodeParameters;
|
|
||||||
credentials?: INodeCredentials;
|
|
||||||
},
|
|
||||||
): Promise<INodePropertyOptions[]> {
|
): Promise<INodePropertyOptions[]> {
|
||||||
return makeRestApiRequest(context, 'GET', '/node-parameter-options', sendData);
|
return makeRestApiRequest(context, 'GET', '/dynamic-node-parameters/options', sendData);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getResourceLocatorResults(
|
export async function getResourceLocatorResults(
|
||||||
context: IRestApiContext,
|
context: IRestApiContext,
|
||||||
sendData: IResourceLocatorReqParams,
|
sendData: DynamicNodeParameters.ResourceLocatorResultsRequest,
|
||||||
): Promise<INodeListSearchResult> {
|
): Promise<INodeListSearchResult> {
|
||||||
return makeRestApiRequest(
|
return makeRestApiRequest(
|
||||||
context,
|
context,
|
||||||
'GET',
|
'GET',
|
||||||
'/nodes-list-search',
|
'/dynamic-node-parameters/resource-locator-results',
|
||||||
sendData as unknown as IDataObject,
|
sendData,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getResourceMapperFields(
|
export async function getResourceMapperFields(
|
||||||
context: IRestApiContext,
|
context: IRestApiContext,
|
||||||
sendData: ResourceMapperReqParams,
|
sendData: DynamicNodeParameters.ResourceMapperFieldsRequest,
|
||||||
): Promise<ResourceMapperFields> {
|
): Promise<ResourceMapperFields> {
|
||||||
return makeRestApiRequest(
|
return makeRestApiRequest(
|
||||||
context,
|
context,
|
||||||
'GET',
|
'GET',
|
||||||
'/get-mapping-fields',
|
'/dynamic-node-parameters/resource-mapper-fields',
|
||||||
sendData as unknown as IDataObject,
|
sendData,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,7 +166,6 @@ import stringify from 'fast-json-stable-stringify';
|
||||||
import type { EventBus } from 'n8n-design-system/utils';
|
import type { EventBus } from 'n8n-design-system/utils';
|
||||||
import { createEventBus } from 'n8n-design-system/utils';
|
import { createEventBus } from 'n8n-design-system/utils';
|
||||||
import type {
|
import type {
|
||||||
ILoadOptions,
|
|
||||||
INode,
|
INode,
|
||||||
INodeCredentials,
|
INodeCredentials,
|
||||||
INodeListSearchItems,
|
INodeListSearchItems,
|
||||||
|
@ -688,9 +687,6 @@ export default defineComponent({
|
||||||
const loadOptionsMethod = this.getPropertyArgument(this.currentMode, 'searchListMethod') as
|
const loadOptionsMethod = this.getPropertyArgument(this.currentMode, 'searchListMethod') as
|
||||||
| string
|
| string
|
||||||
| undefined;
|
| undefined;
|
||||||
const searchList = this.getPropertyArgument(this.currentMode, 'searchList') as
|
|
||||||
| ILoadOptions
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
const requestParams: IResourceLocatorReqParams = {
|
const requestParams: IResourceLocatorReqParams = {
|
||||||
nodeTypeAndVersion: {
|
nodeTypeAndVersion: {
|
||||||
|
@ -699,7 +695,6 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
path: this.path,
|
path: this.path,
|
||||||
methodName: loadOptionsMethod,
|
methodName: loadOptionsMethod,
|
||||||
searchList,
|
|
||||||
currentNodeParameters: resolvedNodeParameters,
|
currentNodeParameters: resolvedNodeParameters,
|
||||||
credentials: this.node.credentials,
|
credentials: this.node.credentials,
|
||||||
...(params.filter ? { filter: params.filter } : {}),
|
...(params.filter ? { filter: params.filter } : {}),
|
||||||
|
|
|
@ -12,21 +12,14 @@ import {
|
||||||
STORES,
|
STORES,
|
||||||
CREDENTIAL_ONLY_HTTP_NODE_VERSION,
|
CREDENTIAL_ONLY_HTTP_NODE_VERSION,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
import type {
|
import type { INodeTypesState, DynamicNodeParameters } from '@/Interface';
|
||||||
INodeTypesState,
|
|
||||||
IResourceLocatorReqParams,
|
|
||||||
ResourceMapperReqParams,
|
|
||||||
} from '@/Interface';
|
|
||||||
import { addHeaders, addNodeTranslation } from '@/plugins/i18n';
|
import { addHeaders, addNodeTranslation } from '@/plugins/i18n';
|
||||||
import { omit } from '@/utils';
|
import { omit } from '@/utils';
|
||||||
import type {
|
import type {
|
||||||
ConnectionTypes,
|
ConnectionTypes,
|
||||||
ILoadOptions,
|
|
||||||
INode,
|
INode,
|
||||||
INodeCredentials,
|
|
||||||
INodeListSearchResult,
|
INodeListSearchResult,
|
||||||
INodeOutputConfiguration,
|
INodeOutputConfiguration,
|
||||||
INodeParameters,
|
|
||||||
INodePropertyOptions,
|
INodePropertyOptions,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
INodeTypeNameVersion,
|
INodeTypeNameVersion,
|
||||||
|
@ -273,25 +266,20 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||||
addHeaders(headers, rootStore.defaultLocale);
|
addHeaders(headers, rootStore.defaultLocale);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getNodeParameterOptions(sendData: {
|
async getNodeParameterOptions(
|
||||||
nodeTypeAndVersion: INodeTypeNameVersion;
|
sendData: DynamicNodeParameters.OptionsRequest,
|
||||||
path: string;
|
): Promise<INodePropertyOptions[]> {
|
||||||
methodName?: string;
|
|
||||||
loadOptions?: ILoadOptions;
|
|
||||||
currentNodeParameters: INodeParameters;
|
|
||||||
credentials?: INodeCredentials;
|
|
||||||
}): Promise<INodePropertyOptions[]> {
|
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
return getNodeParameterOptions(rootStore.getRestApiContext, sendData);
|
return getNodeParameterOptions(rootStore.getRestApiContext, sendData);
|
||||||
},
|
},
|
||||||
async getResourceLocatorResults(
|
async getResourceLocatorResults(
|
||||||
sendData: IResourceLocatorReqParams,
|
sendData: DynamicNodeParameters.ResourceLocatorResultsRequest,
|
||||||
): Promise<INodeListSearchResult> {
|
): Promise<INodeListSearchResult> {
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
return getResourceLocatorResults(rootStore.getRestApiContext, sendData);
|
return getResourceLocatorResults(rootStore.getRestApiContext, sendData);
|
||||||
},
|
},
|
||||||
async getResourceMapperFields(
|
async getResourceMapperFields(
|
||||||
sendData: ResourceMapperReqParams,
|
sendData: DynamicNodeParameters.ResourceMapperFieldsRequest,
|
||||||
): Promise<ResourceMapperFields | null> {
|
): Promise<ResourceMapperFields | null> {
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
try {
|
try {
|
||||||
|
|
Loading…
Reference in a new issue