Skip to content

Commit

Permalink
refactor(core): Convert dynamic node-parameter routes to a decorated …
Browse files Browse the repository at this point in the history
…controller (no-changelog)
  • Loading branch information
netroy committed Nov 10, 2023
1 parent 0ddafd2 commit 6971d9e
Show file tree
Hide file tree
Showing 13 changed files with 425 additions and 570 deletions.
189 changes: 4 additions & 185 deletions packages/cli/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -278,6 +260,7 @@ export class Server extends AbstractServer {
postHog,
),
Container.get(MeController),
Container.get(DynamicNodeParametersController),
new NodeTypesController(config, nodeTypes),
Container.get(PasswordResetController),
Container.get(TagsController),
Expand Down Expand Up @@ -447,170 +430,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
// ----------------------------------------
Expand Down
120 changes: 120 additions & 0 deletions packages/cli/src/controllers/dynamicNodeParameters.controller.ts
Original file line number Diff line number Diff line change
@@ -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,
);
}
}
Loading

0 comments on commit 6971d9e

Please sign in to comment.