mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat(Execute Workflow Trigger Node): Add MVP for explicit input parameters (#11874)
This commit is contained in:
parent
ae08d70ac0
commit
7012d9bb9c
|
@ -1,17 +1,22 @@
|
||||||
import {
|
import {
|
||||||
|
type INodeExecutionData,
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
|
NodeOperationError,
|
||||||
type IExecuteFunctions,
|
type IExecuteFunctions,
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
const WORKFLOW_INPUTS = 'workflowInputs';
|
||||||
|
const VALUES = 'values';
|
||||||
|
|
||||||
export class ExecuteWorkflowTrigger implements INodeType {
|
export class ExecuteWorkflowTrigger implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Execute Workflow Trigger',
|
displayName: 'Execute Workflow Trigger',
|
||||||
name: 'executeWorkflowTrigger',
|
name: 'executeWorkflowTrigger',
|
||||||
icon: 'fa:sign-out-alt',
|
icon: 'fa:sign-out-alt',
|
||||||
group: ['trigger'],
|
group: ['trigger'],
|
||||||
version: 1,
|
version: [1, 1.1],
|
||||||
description:
|
description:
|
||||||
'Helpers for calling other n8n workflows. Used for designing modular, microservice-like workflows.',
|
'Helpers for calling other n8n workflows. Used for designing modular, microservice-like workflows.',
|
||||||
eventTriggerDescription: '',
|
eventTriggerDescription: '',
|
||||||
|
@ -46,10 +51,131 @@ export class ExecuteWorkflowTrigger implements INodeType {
|
||||||
],
|
],
|
||||||
default: 'worklfow_call',
|
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) {
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { mock } from 'jest-mock-extended';
|
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';
|
import { ExecuteWorkflowTrigger } from '../ExecuteWorkflowTrigger.node';
|
||||||
|
|
||||||
|
@ -9,8 +9,10 @@ describe('ExecuteWorkflowTrigger', () => {
|
||||||
{ json: { item: 0, foo: 'bar' } },
|
{ json: { item: 0, foo: 'bar' } },
|
||||||
{ json: { item: 1, foo: 'quz' } },
|
{ json: { item: 1, foo: 'quz' } },
|
||||||
];
|
];
|
||||||
|
const mockNode = { typeVersion: 1 } as INode;
|
||||||
const executeFns = mock<IExecuteFunctions>({
|
const executeFns = mock<IExecuteFunctions>({
|
||||||
getInputData: () => mockInputData,
|
getInputData: () => mockInputData,
|
||||||
|
getNode: () => mockNode,
|
||||||
});
|
});
|
||||||
const result = await new ExecuteWorkflowTrigger().execute.call(executeFns);
|
const result = await new ExecuteWorkflowTrigger().execute.call(executeFns);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue