load on demand selected workflow inputs

This commit is contained in:
Ivan Atanasov 2024-12-04 22:30:41 +01:00
parent dacd1907f3
commit c83bbcf50a
No known key found for this signature in database
6 changed files with 97 additions and 37 deletions

View file

@ -300,6 +300,11 @@ export class DynamicNodeParametersService {
}
private getLocalLoadOptionsContext(path: string, additionalData: IWorkflowExecuteAdditionalData) {
return new LocalLoadOptionsContext(additionalData, path, this.workflowLoaderService);
return new LocalLoadOptionsContext(
this.nodeTypes,
additionalData,
path,
this.workflowLoaderService,
);
}
}

View file

@ -1,22 +1,27 @@
import { get } from 'lodash';
import { ApplicationError } from 'n8n-workflow';
import { ApplicationError, deepCopy, Workflow } from 'n8n-workflow';
import type {
INodeParameterResourceLocator,
IWorkflowExecuteAdditionalData,
NodeParameterValueType,
ILocalLoadOptionsFunctions,
IWorkflowLoader,
IWorkflowNodeContext,
INode,
INodeTypes,
} from 'n8n-workflow';
import { LoadWorkflowNodeContext } from './workflow-node-context';
export class LocalLoadOptionsContext implements ILocalLoadOptionsFunctions {
constructor(
private nodeTypes: INodeTypes,
private additionalData: IWorkflowExecuteAdditionalData,
private path: string,
private workflowLoader: IWorkflowLoader,
) {}
async getExecuteWorkflowTriggerNode(): Promise<INode | undefined> {
async getWorkflowNodeContext(nodeType: string): Promise<IWorkflowNodeContext | null> {
const { value } = this.getCurrentNodeParameter('workflowId') as INodeParameterResourceLocator;
const workflowId = value as string;
@ -24,9 +29,25 @@ export class LocalLoadOptionsContext implements ILocalLoadOptionsFunctions {
throw new ApplicationError('No workflowId parameter defined on node!');
}
const workflow = await this.workflowLoader.get(workflowId);
const dbWorkflow = await this.workflowLoader.get(workflowId);
return workflow.nodes.find((node) => node.type === 'n8n-nodes-base.executeWorkflowTrigger');
const workflowNode = dbWorkflow.nodes.find((node) => node.type === nodeType) as INode;
if (workflowNode) {
const workflow = new Workflow({
nodes: [workflowNode],
connections: {},
active: false,
nodeTypes: this.nodeTypes,
});
const workflowAdditionalData = deepCopy(this.additionalData);
workflowAdditionalData.currentNodeParameters = workflowNode.parameters;
return new LoadWorkflowNodeContext(workflow, workflowNode, workflowAdditionalData);
}
return null;
}
getCurrentNodeParameter(parameterPath: string): NodeParameterValueType | object | undefined {

View file

@ -0,0 +1,32 @@
import type {
IGetNodeParameterOptions,
INode,
IWorkflowExecuteAdditionalData,
Workflow,
IWorkflowNodeContext,
} from 'n8n-workflow';
import { NodeExecutionContext } from './node-execution-context';
export class LoadWorkflowNodeContext extends NodeExecutionContext implements IWorkflowNodeContext {
readonly getNodeParameter: IWorkflowNodeContext['getNodeParameter'];
constructor(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData) {
super(workflow, node, additionalData, 'internal');
{
this.getNodeParameter = ((
parameterName: string,
itemIndex: number,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fallbackValue?: any,
options?: IGetNodeParameterOptions,
) =>
this._getNodeParameter(
parameterName,
itemIndex,
fallbackValue,
options,
)) as IWorkflowNodeContext['getNodeParameter'];
}
}
}

View file

@ -1,39 +1,37 @@
import type {
ILocalLoadOptionsFunctions,
INode,
ResourceMapperField,
ResourceMapperFields,
import {
EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE,
type ILocalLoadOptionsFunctions,
type ResourceMapperField,
type ResourceMapperFields,
} from 'n8n-workflow';
import { getFieldEntries } from '../../GenericFunctions';
export async function getWorkflowInputs(
this: ILocalLoadOptionsFunctions,
): Promise<ResourceMapperFields> {
const executeWorkflowTriggerNode = (await this.getExecuteWorkflowTriggerNode()) as INode;
const nodeLoadContext = await this.getWorkflowNodeContext(EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE);
let fields: ResourceMapperField[] = [];
// const fieldValues = getFieldEntries(executeWorkflowTriggerNode);
if (nodeLoadContext) {
const fieldValues = getFieldEntries(nodeLoadContext);
const workflowInputFields = [
{ name: 'field1', type: 'string' as const },
{ name: 'field2', type: 'number' as const },
{ name: 'field3', type: 'boolean' as const },
{ name: 'field4', type: 'any' as const },
];
fields = fieldValues.map((currentWorkflowInput) => {
const field: ResourceMapperField = {
id: currentWorkflowInput.name,
displayName: currentWorkflowInput.name,
required: false,
defaultMatch: true,
display: true,
canBeUsedToMatch: true,
};
const fields: ResourceMapperField[] = workflowInputFields.map((currentWorkflowInput) => {
const field: ResourceMapperField = {
id: currentWorkflowInput.name,
displayName: currentWorkflowInput.name,
required: false,
defaultMatch: true,
display: true,
canBeUsedToMatch: true,
};
if (currentWorkflowInput.type !== 'any') {
field.type = currentWorkflowInput.type;
}
if (currentWorkflowInput.type !== 'any') {
field.type = currentWorkflowInput.type;
}
return field;
});
return field;
});
}
return { fields };
}

View file

@ -1,6 +1,6 @@
import { json as generateSchemaFromExample, type SchemaObject } from 'generate-schema';
import type { JSONSchema7 } from 'json-schema';
import type { FieldValueOption, FieldType, IExecuteFunctions } from 'n8n-workflow';
import type { FieldValueOption, FieldType, IWorkflowNodeContext } from 'n8n-workflow';
import { jsonParse, NodeOperationError } from 'n8n-workflow';
import { JSON_EXAMPLE, INPUT_SOURCE, WORKFLOW_INPUTS, VALUES, TYPE_OPTIONS } from './constants';
@ -40,14 +40,14 @@ function parseJsonSchema(schema: JSONSchema7): FieldValueOption[] | string {
return result;
}
function parseJsonExample(context: IExecuteFunctions): JSONSchema7 {
function parseJsonExample(context: IWorkflowNodeContext): JSONSchema7 {
const jsonString = context.getNodeParameter(JSON_EXAMPLE, 0, '') as string;
const json = jsonParse<SchemaObject>(jsonString);
return generateSchemaFromExample(json) as JSONSchema7;
}
export function getFieldEntries(context: IExecuteFunctions): FieldValueOption[] {
export function getFieldEntries(context: IWorkflowNodeContext): FieldValueOption[] {
const inputSource = context.getNodeParameter(INPUT_SOURCE, 0);
let result: FieldValueOption[] | string = 'Internal Error: Invalid input source';
try {

View file

@ -1067,13 +1067,17 @@ export interface ILoadOptionsFunctions extends FunctionsBase {
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object | undefined;
getCurrentNodeParameters(): INodeParameters | undefined;
helpers: RequestHelperFunctions & SSHTunnelFunctions;
}
export type FieldValueOption = { name: string; type: FieldType | 'any' };
export type IWorkflowNodeContext = ExecuteFunctions.GetNodeParameterFn &
Pick<FunctionsBase, 'getNode'>;
export interface ILocalLoadOptionsFunctions {
getExecuteWorkflowTriggerNode(): Promise<INode | undefined>;
getWorkflowNodeContext(nodeType: string): Promise<IWorkflowNodeContext | null>;
}
export interface IWorkflowLoader {