mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(editor): Execute sub-workflow UX and copy updates (no-changelog) (#12834)
This commit is contained in:
committed by
GitHub
parent
13652c5ee2
commit
de49c23971
@@ -11,7 +11,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"alias": ["n8n"],
|
||||
"alias": ["n8n", "call", "sub", "workflow", "sub-workflow", "subworkflow"],
|
||||
"subcategories": {
|
||||
"Core Nodes": ["Helpers", "Flow"]
|
||||
}
|
||||
|
||||
@@ -8,14 +8,12 @@ import type {
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { getWorkflowInfo } from './GenericFunctions';
|
||||
import { localResourceMapping } from './methods';
|
||||
import { generatePairedItemData } from '../../../utils/utilities';
|
||||
import {
|
||||
getCurrentWorkflowInputData,
|
||||
loadWorkflowInputMappings,
|
||||
} from '../../../utils/workflowInputsResourceMapping/GenericFunctions';
|
||||
import { getCurrentWorkflowInputData } from '../../../utils/workflowInputsResourceMapping/GenericFunctions';
|
||||
export class ExecuteWorkflow implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Execute Workflow',
|
||||
displayName: 'Execute Sub-workflow',
|
||||
name: 'executeWorkflow',
|
||||
icon: 'fa:sign-in-alt',
|
||||
iconColor: 'orange-red',
|
||||
@@ -38,7 +36,7 @@ export class ExecuteWorkflow implements INodeType {
|
||||
default: 'call_workflow',
|
||||
options: [
|
||||
{
|
||||
name: 'Call Another Workflow',
|
||||
name: 'Execute a Sub-Workflow',
|
||||
value: 'call_workflow',
|
||||
},
|
||||
],
|
||||
@@ -210,7 +208,7 @@ export class ExecuteWorkflow implements INodeType {
|
||||
typeOptions: {
|
||||
loadOptionsDependsOn: ['workflowId.value'],
|
||||
resourceMapper: {
|
||||
localResourceMapperMethod: 'loadWorkflowInputMappings',
|
||||
localResourceMapperMethod: 'loadSubWorkflowInputs',
|
||||
valuesLabel: 'Workflow Inputs',
|
||||
mode: 'map',
|
||||
fieldWords: {
|
||||
@@ -275,9 +273,7 @@ export class ExecuteWorkflow implements INodeType {
|
||||
};
|
||||
|
||||
methods = {
|
||||
localResourceMapping: {
|
||||
loadWorkflowInputMappings,
|
||||
},
|
||||
localResourceMapping,
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * as localResourceMapping from './localResourceMapping';
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { ILocalLoadOptionsFunctions, ResourceMapperFields } from 'n8n-workflow';
|
||||
|
||||
import { loadWorkflowInputMappings } from '@utils/workflowInputsResourceMapping/GenericFunctions';
|
||||
|
||||
export async function loadSubWorkflowInputs(
|
||||
this: ILocalLoadOptionsFunctions,
|
||||
): Promise<ResourceMapperFields> {
|
||||
const { fields, dataMode, subworkflowInfo } = await loadWorkflowInputMappings.bind(this)();
|
||||
let emptyFieldsNotice: string | undefined;
|
||||
if (fields.length === 0) {
|
||||
const subworkflowLink = subworkflowInfo?.id
|
||||
? `<a href="/workflow/${subworkflowInfo?.id}" target="_blank">sub-workflow’s trigger</a>`
|
||||
: 'sub-workflow’s trigger';
|
||||
|
||||
switch (dataMode) {
|
||||
case 'passthrough':
|
||||
emptyFieldsNotice = `This sub-workflow will consume all input data passed to it. You can define specific expected input in the ${subworkflowLink}.`;
|
||||
break;
|
||||
default:
|
||||
emptyFieldsNotice = `The sub-workflow isn't set up to accept any inputs. Change this in the ${subworkflowLink}.`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { fields, emptyFieldsNotice };
|
||||
}
|
||||
@@ -32,11 +32,16 @@ describe('ExecuteWorkflowTrigger', () => {
|
||||
|
||||
it('should filter out parent input in `Using Fields below` mode', async () => {
|
||||
executeFns.getNodeParameter.mockReturnValueOnce(WORKFLOW_INPUTS);
|
||||
const mockNewParams = [
|
||||
{ name: 'value1', type: 'string' },
|
||||
{ name: 'value2', type: 'number' },
|
||||
{ name: 'foo', type: 'string' },
|
||||
] as FieldValueOption[];
|
||||
const mockNewParams: {
|
||||
fields: FieldValueOption[];
|
||||
noFieldsMessage?: string;
|
||||
} = {
|
||||
fields: [
|
||||
{ name: 'value1', type: 'string' },
|
||||
{ name: 'value2', type: 'number' },
|
||||
{ name: 'foo', type: 'string' },
|
||||
],
|
||||
};
|
||||
const getFieldEntriesMock = (getFieldEntries as jest.Mock).mockReturnValue(mockNewParams);
|
||||
|
||||
const result = await new ExecuteWorkflowTrigger().execute.call(executeFns);
|
||||
|
||||
@@ -30,14 +30,15 @@ export class ExecuteWorkflowTrigger implements INodeType {
|
||||
eventTriggerDescription: '',
|
||||
maxNodes: 1,
|
||||
defaults: {
|
||||
name: 'Workflow Input Trigger',
|
||||
name: 'When Executed by Another Workflow',
|
||||
color: '#ff6d5a',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
hints: [
|
||||
{
|
||||
message: 'Please make sure to define your input fields.',
|
||||
message:
|
||||
"This workflow isn't set to accept any input data. Fill out the workflow input schema or change the workflow to accept any data passed to it.",
|
||||
// This condition checks if we have no input fields, which gets a bit awkward:
|
||||
// For WORKFLOW_INPUTS: keys() only contains `VALUES` if at least one value is provided
|
||||
// For JSON_EXAMPLE: We remove all whitespace and check if we're left with an empty object. Note that we already error if the example is not valid JSON
|
||||
@@ -58,8 +59,8 @@ export class ExecuteWorkflowTrigger implements INodeType {
|
||||
{
|
||||
name: 'Workflow Call',
|
||||
value: 'worklfow_call',
|
||||
description: 'When called by another workflow using Execute Workflow Trigger',
|
||||
action: 'When Called by Another Workflow',
|
||||
description: 'When executed by another workflow using Execute Workflow Trigger',
|
||||
action: 'When executed by Another Workflow',
|
||||
},
|
||||
],
|
||||
default: 'worklfow_call',
|
||||
@@ -142,7 +143,7 @@ export class ExecuteWorkflowTrigger implements INodeType {
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Workflow Inputs',
|
||||
displayName: 'Workflow Input Schema',
|
||||
name: WORKFLOW_INPUTS,
|
||||
placeholder: 'Add field',
|
||||
type: 'fixedCollection',
|
||||
@@ -168,7 +169,8 @@ export class ExecuteWorkflowTrigger implements INodeType {
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. fieldName',
|
||||
description: 'Name of the field',
|
||||
description:
|
||||
'A unique name for this workflow input, used to reference it from another workflows',
|
||||
required: true,
|
||||
noDataExpression: true,
|
||||
},
|
||||
@@ -176,7 +178,8 @@ export class ExecuteWorkflowTrigger implements INodeType {
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
description: 'The field value type',
|
||||
description:
|
||||
"Expected data type for this input value. Determines how this field's values are stored, validated, and displayed.",
|
||||
options: TYPE_OPTIONS,
|
||||
required: true,
|
||||
default: 'string',
|
||||
@@ -208,10 +211,10 @@ export class ExecuteWorkflowTrigger implements INodeType {
|
||||
return [inputData];
|
||||
} else {
|
||||
const newParams = getFieldEntries(this);
|
||||
const newKeys = new Set(newParams.map((x) => x.name));
|
||||
const newKeys = new Set(newParams.fields.map((x) => x.name));
|
||||
const itemsInSchema: INodeExecutionData[] = inputData.map((row, index) => ({
|
||||
json: {
|
||||
...Object.fromEntries(newParams.map((x) => [x.name, FALLBACK_DEFAULT_VALUE])),
|
||||
...Object.fromEntries(newParams.fields.map((x) => [x.name, FALLBACK_DEFAULT_VALUE])),
|
||||
// Need to trim to the expected schema to support legacy Execute Workflow callers passing through all their data
|
||||
// which we do not want to expose past this node.
|
||||
..._.pickBy(row.json, (_value, key) => newKeys.has(key)),
|
||||
|
||||
Reference in New Issue
Block a user