feat(Execute Workflow Trigger Node): Add MVP for explicit input parameters (#11874)

This commit is contained in:
Charlie Kolb 2024-11-26 12:38:06 +01:00 committed by GitHub
parent ae08d70ac0
commit 7012d9bb9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 131 additions and 3 deletions

View file

@ -1,17 +1,22 @@
import {
type INodeExecutionData,
NodeConnectionType,
NodeOperationError,
type IExecuteFunctions,
type INodeType,
type INodeTypeDescription,
} from 'n8n-workflow';
const WORKFLOW_INPUTS = 'workflowInputs';
const VALUES = 'values';
export class ExecuteWorkflowTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Execute Workflow Trigger',
name: 'executeWorkflowTrigger',
icon: 'fa:sign-out-alt',
group: ['trigger'],
version: 1,
version: [1, 1.1],
description:
'Helpers for calling other n8n workflows. Used for designing modular, microservice-like workflows.',
eventTriggerDescription: '',
@ -46,10 +51,131 @@ export class ExecuteWorkflowTrigger implements INodeType {
],
default: 'worklfow_call',
},
{
displayName: 'Workflow Inputs',
name: WORKFLOW_INPUTS,
placeholder: 'Add Field',
type: 'fixedCollection',
description:
'Define expected input fields. If no inputs are provided, all data from the calling workflow will be passed through.',
typeOptions: {
multipleValues: true,
sortable: true,
},
displayOptions: {
show: { '@version': [{ _cnd: { gte: 1.1 } }] },
},
default: {},
options: [
{
name: VALUES,
displayName: 'Values',
values: [
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
placeholder: 'e.g. fieldName',
description: 'Name of the field',
},
// {
// displayName: 'Type',
// name: 'type',
// type: 'options',
// description: 'The field value type',
// // eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
// options: [
// {
// name: 'String',
// value: 'stringValue',
// },
// {
// name: 'Number',
// value: 'numberValue',
// },
// {
// name: 'Boolean',
// value: 'booleanValue',
// },
// {
// name: 'Array',
// value: 'arrayValue',
// },
// {
// name: 'Object',
// value: 'objectValue',
// },
// ],
// default: 'stringValue',
// },
],
},
],
},
],
};
async execute(this: IExecuteFunctions) {
return [this.getInputData()];
const inputData = this.getInputData();
if (this.getNode().typeVersion < 1.1) {
return [inputData];
} else {
// Need to mask type due to bad `getNodeParameter` typing
const marker = Symbol() as unknown as object;
const hasFields =
inputData.length >= 0 &&
inputData.some(
(_x, i) => this.getNodeParameter(`${WORKFLOW_INPUTS}.${VALUES}`, i, marker) !== marker,
);
if (!hasFields) {
return [inputData];
}
const items: INodeExecutionData[] = [];
for (const [itemIndex, item] of inputData.entries()) {
// Fields listed here will explicitly overwrite original fields
const newItem: INodeExecutionData = {
json: {},
// TODO: Ensure we handle sub-execution jumps correctly.
// metadata: {
// subExecution: {
// executionId: 'uhh',
// workflowId: 'maybe?',
// },
// },
pairedItem: { item: itemIndex },
};
try {
const newParams = this.getNodeParameter(
`${WORKFLOW_INPUTS}.${VALUES}`,
itemIndex,
[],
) as Array<{
name: string;
}>;
for (const { name } of newParams) {
/** TODO type check goes here */
newItem.json[name] = name in item.json ? item.json[name] : /* TODO default */ null;
}
// TODO Do we want to copy non-json data (e.g. binary) as well?
items.push(Object.assign({}, item, newItem));
} catch (error) {
if (this.continueOnFail()) {
/** todo error case? */
} else {
throw new NodeOperationError(this.getNode(), error, {
itemIndex,
});
}
}
}
return [items];
}
}
}

View file

@ -1,5 +1,5 @@
import { mock } from 'jest-mock-extended';
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
import type { IExecuteFunctions, INode, INodeExecutionData } from 'n8n-workflow';
import { ExecuteWorkflowTrigger } from '../ExecuteWorkflowTrigger.node';
@ -9,8 +9,10 @@ describe('ExecuteWorkflowTrigger', () => {
{ json: { item: 0, foo: 'bar' } },
{ json: { item: 1, foo: 'quz' } },
];
const mockNode = { typeVersion: 1 } as INode;
const executeFns = mock<IExecuteFunctions>({
getInputData: () => mockInputData,
getNode: () => mockNode,
});
const result = await new ExecuteWorkflowTrigger().execute.call(executeFns);