add workflow loader

This commit is contained in:
Ivan Atanasov 2024-12-04 13:19:24 +01:00
parent 5efdde1f6d
commit e0c9e9eb0d
No known key found for this signature in database
5 changed files with 47 additions and 50 deletions

View file

@ -24,6 +24,8 @@ import { Service } from 'typedi';
import { NodeTypes } from '@/node-types';
import { WorkflowLoaderService } from './workflow-loader.service';
type LocalResourceMappingMethod = (
this: ILocalLoadOptionsFunctions,
) => Promise<ResourceMapperFields>;
@ -48,7 +50,10 @@ type NodeMethod =
@Service()
export class DynamicNodeParametersService {
constructor(private nodeTypes: NodeTypes) {}
constructor(
private nodeTypes: NodeTypes,
private workflowLoaderService: WorkflowLoaderService,
) {}
/** Returns the available options via a predefined method */
async getOptionsViaMethodName(
@ -193,8 +198,7 @@ export class DynamicNodeParametersService {
): Promise<ResourceMapperFields> {
const nodeType = this.getNodeType(nodeTypeAndVersion);
const method = this.getMethod('localResourceMapping', methodName, nodeType);
const workflow = this.getWorkflow(nodeTypeAndVersion, currentNodeParameters, credentials);
const thisArgs = this.getLocalLoadOptionsContext(path, additionalData, workflow);
const thisArgs = this.getLocalLoadOptionsContext(path, additionalData);
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return method.call(thisArgs);
}
@ -297,12 +301,7 @@ export class DynamicNodeParametersService {
return new LoadOptionsContext(workflow, node, additionalData, path);
}
private getLocalLoadOptionsContext(
path: string,
additionalData: IWorkflowExecuteAdditionalData,
workflow: Workflow,
) {
const node = workflow.nodes['Temp-Node'];
return new LocalLoadOptionsContext(workflow, node, additionalData, path);
private getLocalLoadOptionsContext(path: string, additionalData: IWorkflowExecuteAdditionalData) {
return new LocalLoadOptionsContext(additionalData, path, this.workflowLoaderService);
}
}

View file

@ -0,0 +1,19 @@
import { ApplicationError, type IWorkflowBase, type IWorkflowLoader } from 'n8n-workflow';
import { Service } from 'typedi';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
@Service()
export class WorkflowLoaderService implements IWorkflowLoader {
constructor(private readonly workflowRepository: WorkflowRepository) {}
async get(workflowId: string): Promise<IWorkflowBase> {
const workflow = await this.workflowRepository.findById(workflowId);
if (!workflow) {
throw new ApplicationError(`Failed to find workflow with ID "${workflowId}"`);
}
return workflow;
}
}

View file

@ -2,33 +2,21 @@ import { get } from 'lodash';
import { ApplicationError } from 'n8n-workflow';
import type {
INodeParameterResourceLocator,
IGetNodeParameterOptions,
INode,
IWorkflowExecuteAdditionalData,
NodeParameterValueType,
Workflow,
ILocalLoadOptionsFunctions,
FieldValueOption,
IWorkflowLoader,
} from 'n8n-workflow';
import { extractValue } from '@/ExtractValue';
import { NodeExecutionContext } from './node-execution-context';
export class LocalLoadOptionsContext
extends NodeExecutionContext
implements ILocalLoadOptionsFunctions
{
export class LocalLoadOptionsContext implements ILocalLoadOptionsFunctions {
constructor(
workflow: Workflow,
node: INode,
additionalData: IWorkflowExecuteAdditionalData,
private readonly path: string,
) {
super(workflow, node, additionalData, 'internal');
}
private additionalData: IWorkflowExecuteAdditionalData,
private path: string,
private workflowLoader: IWorkflowLoader,
) {}
getWorkflowInputValues(): FieldValueOption[] {
async getWorkflowInputValues(): Promise<FieldValueOption[]> {
const { value } = this.getCurrentNodeParameter('workflowId') as INodeParameterResourceLocator;
const workflowId = value as string;
@ -36,6 +24,10 @@ export class LocalLoadOptionsContext
throw new ApplicationError('No workflowId parameter defined on node!');
}
const workflow = await this.workflowLoader.get(workflowId);
workflow.nodes.find((node) => node.type === 'n8n-nodes-base.start');
// TODO: load the inputs from the workflow
const dummyFields = [
{ name: 'field1', type: 'string' as const },
@ -47,31 +39,14 @@ export class LocalLoadOptionsContext
return dummyFields;
}
getCurrentNodeParameter(
parameterPath: string,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object | undefined {
getCurrentNodeParameter(parameterPath: string): NodeParameterValueType | object | undefined {
const nodeParameters = this.additionalData.currentNodeParameters;
if (parameterPath.startsWith('&')) {
parameterPath = `${this.path.split('.').slice(1, -1).join('.')}.${parameterPath.slice(1)}`;
}
let returnData = get(nodeParameters, parameterPath);
// This is outside the try/catch because it throws errors with proper messages
if (options?.extractValue) {
const nodeType = this.workflow.nodeTypes.getByNameAndVersion(
this.node.type,
this.node.typeVersion,
);
returnData = extractValue(
returnData,
parameterPath,
this.node,
nodeType,
) as NodeParameterValueType;
}
const returnData = get(nodeParameters, parameterPath);
return returnData;
}

View file

@ -8,7 +8,7 @@ import type {
export async function getWorkflowInputs(
this: ILocalLoadOptionsFunctions,
): Promise<ResourceMapperFields> {
const workflowInputFields = this.getWorkflowInputValues() as FieldValueOption[];
const workflowInputFields = (await this.getWorkflowInputValues()) as FieldValueOption[];
const fields: ResourceMapperField[] = workflowInputFields.map((currentWorkflowInput) => {
const field: ResourceMapperField = {

View file

@ -1073,7 +1073,11 @@ export interface ILoadOptionsFunctions extends FunctionsBase {
export type FieldValueOption = { name: string; type: FieldType | 'any' };
export interface ILocalLoadOptionsFunctions {
getWorkflowInputValues(): FieldValueOption[];
getWorkflowInputValues(): Promise<FieldValueOption[]>;
}
export interface IWorkflowLoader {
get(workflowId: string): Promise<IWorkflowBase>;
}
export interface IPollFunctions