mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
refactor(core): Standardize filename casing for services and Public API (no-changelog) (#10579)
This commit is contained in:
257
packages/cli/src/services/dynamic-node-parameters.service.ts
Normal file
257
packages/cli/src/services/dynamic-node-parameters.service.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
import { Service } from 'typedi';
|
||||
import type {
|
||||
ILoadOptions,
|
||||
ILoadOptionsFunctions,
|
||||
INode,
|
||||
INodeExecutionData,
|
||||
INodeListSearchResult,
|
||||
INodeProperties,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
IRunExecutionData,
|
||||
ITaskDataConnections,
|
||||
IWorkflowExecuteAdditionalData,
|
||||
ResourceMapperFields,
|
||||
INodeCredentials,
|
||||
INodeParameters,
|
||||
INodeTypeNameVersion,
|
||||
NodeParameterValueType,
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
import { Workflow, RoutingNode, ApplicationError } from 'n8n-workflow';
|
||||
import { NodeExecuteFunctions } from 'n8n-core';
|
||||
import { NodeTypes } from '@/node-types';
|
||||
|
||||
@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 ApplicationError(
|
||||
'Node type does not exist or does not have "requestDefaults.baseURL" defined!',
|
||||
{ tags: { nodeType: nodeType.description.name } },
|
||||
);
|
||||
}
|
||||
|
||||
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['Temp-Node'];
|
||||
|
||||
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 ApplicationError('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);
|
||||
}
|
||||
|
||||
/** Returns the result of the action handler */
|
||||
async getActionResult(
|
||||
handler: string,
|
||||
path: string,
|
||||
additionalData: IWorkflowExecuteAdditionalData,
|
||||
nodeTypeAndVersion: INodeTypeNameVersion,
|
||||
currentNodeParameters: INodeParameters,
|
||||
payload: IDataObject | string | undefined,
|
||||
credentials?: INodeCredentials,
|
||||
): Promise<NodeParameterValueType> {
|
||||
const nodeType = this.getNodeType(nodeTypeAndVersion);
|
||||
const method = this.getMethod('actionHandler', handler, 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, payload);
|
||||
}
|
||||
|
||||
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: 'actionHandler',
|
||||
methodName: string,
|
||||
nodeType: INodeType,
|
||||
): (this: ILoadOptionsFunctions, payload?: string) => Promise<NodeParameterValueType>;
|
||||
|
||||
private getMethod(
|
||||
type: 'resourceMapping' | 'listSearch' | 'loadOptions' | 'actionHandler',
|
||||
methodName: string,
|
||||
nodeType: INodeType,
|
||||
) {
|
||||
const method = nodeType.methods?.[type]?.[methodName];
|
||||
if (typeof method !== 'function') {
|
||||
throw new ApplicationError('Node type does not have method defined', {
|
||||
tags: { nodeType: nodeType.description.name },
|
||||
extra: { methodName },
|
||||
});
|
||||
}
|
||||
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 = workflow.nodes['Temp-Node'];
|
||||
return NodeExecuteFunctions.getLoadOptionsFunctions(workflow, node, path, additionalData);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user