mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-10 06:34:05 -08:00
fix(core): Make node execution order configurable, and backward-compatible (#6507)
* fix(core): Make node execution order configurable, and backward-compatible * ⚡ Also add new Merge-Node behaviour * ⚡ Fix typo * Fix lint issue * update labels * rename legacy to v0 * remove the unnecessary log * default all new workflows to use v1 execution-order * remove the controller changes * clone default settings to avoid it getting modified --------- Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
f0dfc3cf4e
commit
d97edbcffa
|
@ -338,12 +338,6 @@ export class Start extends BaseCommand {
|
||||||
const editorUrl = GenericHelpers.getBaseUrl();
|
const editorUrl = GenericHelpers.getBaseUrl();
|
||||||
this.log(`\nEditor is now accessible via:\n${editorUrl}`);
|
this.log(`\nEditor is now accessible via:\n${editorUrl}`);
|
||||||
|
|
||||||
const saveManualExecutions = config.getEnv('executions.saveDataManualExecutions');
|
|
||||||
|
|
||||||
if (saveManualExecutions) {
|
|
||||||
this.log('\nManual executions will be visible only for the owner');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow to open n8n editor by pressing "o"
|
// Allow to open n8n editor by pressing "o"
|
||||||
if (Boolean(process.stdout.isTTY) && process.stdin.setRawMode) {
|
if (Boolean(process.stdout.isTTY) && process.stdin.setRawMode) {
|
||||||
process.stdin.setRawMode(true);
|
process.stdin.setRawMode(true);
|
||||||
|
|
|
@ -143,24 +143,8 @@ export class WorkflowExecute {
|
||||||
return this.processRunExecutionData(workflow);
|
return this.processRunExecutionData(workflow);
|
||||||
}
|
}
|
||||||
|
|
||||||
forceInputNodeExecution(workflow: Workflow, node: INode): boolean {
|
forceInputNodeExecution(workflow: Workflow): boolean {
|
||||||
const nodeType = workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
|
return workflow.settings.executionOrder !== 'v1';
|
||||||
|
|
||||||
// Check if the incoming nodes should be forced to execute
|
|
||||||
let forceInputNodeExecution = nodeType.description.forceInputNodeExecution;
|
|
||||||
if (forceInputNodeExecution !== undefined) {
|
|
||||||
if (typeof forceInputNodeExecution === 'string') {
|
|
||||||
forceInputNodeExecution = !!workflow.expression.getSimpleParameterValue(
|
|
||||||
node,
|
|
||||||
forceInputNodeExecution,
|
|
||||||
this.mode,
|
|
||||||
this.additionalData.timezone,
|
|
||||||
{ $version: node.typeVersion },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return forceInputNodeExecution;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -379,6 +363,7 @@ export class WorkflowExecute {
|
||||||
runIndex: number,
|
runIndex: number,
|
||||||
): void {
|
): void {
|
||||||
let stillDataMissing = false;
|
let stillDataMissing = false;
|
||||||
|
const enqueueFn = workflow.settings.executionOrder === 'v1' ? 'unshift' : 'push';
|
||||||
let waitingNodeIndex: number | undefined;
|
let waitingNodeIndex: number | undefined;
|
||||||
|
|
||||||
// Check if node has multiple inputs as then we have to wait for all input data
|
// Check if node has multiple inputs as then we have to wait for all input data
|
||||||
|
@ -510,7 +495,7 @@ export class WorkflowExecute {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.runExecutionData.executionData!.nodeExecutionStack.unshift(executionStackItem);
|
this.runExecutionData.executionData!.nodeExecutionStack[enqueueFn](executionStackItem);
|
||||||
|
|
||||||
// Remove the data from waiting
|
// Remove the data from waiting
|
||||||
delete this.runExecutionData.executionData!.waitingExecution[connectionData.node][
|
delete this.runExecutionData.executionData!.waitingExecution[connectionData.node][
|
||||||
|
@ -554,8 +539,7 @@ export class WorkflowExecute {
|
||||||
// are already on the list to be processed.
|
// are already on the list to be processed.
|
||||||
// If that is not the case add it.
|
// If that is not the case add it.
|
||||||
|
|
||||||
const node = workflow.getNode(connectionData.node);
|
const forceInputNodeExecution = this.forceInputNodeExecution(workflow);
|
||||||
const forceInputNodeExecution = this.forceInputNodeExecution(workflow, node!);
|
|
||||||
|
|
||||||
for (
|
for (
|
||||||
let inputIndex = 0;
|
let inputIndex = 0;
|
||||||
|
@ -680,7 +664,7 @@ export class WorkflowExecute {
|
||||||
if (addEmptyItem) {
|
if (addEmptyItem) {
|
||||||
// Add only node if it does not have any inputs because else it will
|
// Add only node if it does not have any inputs because else it will
|
||||||
// be added by its input node later anyway.
|
// be added by its input node later anyway.
|
||||||
this.runExecutionData.executionData!.nodeExecutionStack.unshift({
|
this.runExecutionData.executionData!.nodeExecutionStack[enqueueFn]({
|
||||||
node: workflow.getNode(nodeToAdd) as INode,
|
node: workflow.getNode(nodeToAdd) as INode,
|
||||||
data: {
|
data: {
|
||||||
main: [
|
main: [
|
||||||
|
@ -744,7 +728,7 @@ export class WorkflowExecute {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// All data is there so add it directly to stack
|
// All data is there so add it directly to stack
|
||||||
this.runExecutionData.executionData!.nodeExecutionStack.unshift({
|
this.runExecutionData.executionData!.nodeExecutionStack[enqueueFn]({
|
||||||
node: workflow.nodes[connectionData.node],
|
node: workflow.nodes[connectionData.node],
|
||||||
data: {
|
data: {
|
||||||
main: connectionDataArray,
|
main: connectionDataArray,
|
||||||
|
@ -774,6 +758,7 @@ export class WorkflowExecute {
|
||||||
Logger.verbose('Workflow execution started', { workflowId: workflow.id });
|
Logger.verbose('Workflow execution started', { workflowId: workflow.id });
|
||||||
|
|
||||||
const startedAt = new Date();
|
const startedAt = new Date();
|
||||||
|
const forceInputNodeExecution = this.forceInputNodeExecution(workflow);
|
||||||
|
|
||||||
this.status = 'running';
|
this.status = 'running';
|
||||||
|
|
||||||
|
@ -937,8 +922,6 @@ export class WorkflowExecute {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const node = workflow.getNode(executionNode.name);
|
|
||||||
|
|
||||||
// Check if all the data which is needed to run the node is available
|
// Check if all the data which is needed to run the node is available
|
||||||
if (workflow.connectionsByDestinationNode.hasOwnProperty(executionNode.name)) {
|
if (workflow.connectionsByDestinationNode.hasOwnProperty(executionNode.name)) {
|
||||||
// Check if the node has incoming connections
|
// Check if the node has incoming connections
|
||||||
|
@ -971,7 +954,7 @@ export class WorkflowExecute {
|
||||||
continue executionLoop;
|
continue executionLoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.forceInputNodeExecution(workflow, node!)) {
|
if (forceInputNodeExecution) {
|
||||||
// Check if it has the data for all the inputs
|
// Check if it has the data for all the inputs
|
||||||
// The most nodes just have one but merge node for example has two and data
|
// The most nodes just have one but merge node for example has two and data
|
||||||
// of both inputs has to be available to be able to process the node.
|
// of both inputs has to be available to be able to process the node.
|
||||||
|
@ -1295,28 +1278,34 @@ export class WorkflowExecute {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const connectionDestinationNode = workflow.getNode(connectionData.node);
|
|
||||||
const forceInputNodeExecution = this.forceInputNodeExecution(
|
|
||||||
workflow,
|
|
||||||
connectionDestinationNode!,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
nodeSuccessData![outputIndex] &&
|
nodeSuccessData![outputIndex] &&
|
||||||
(nodeSuccessData![outputIndex].length !== 0 ||
|
(nodeSuccessData![outputIndex].length !== 0 ||
|
||||||
(connectionData.index > 0 && forceInputNodeExecution))
|
(connectionData.index > 0 && forceInputNodeExecution))
|
||||||
) {
|
) {
|
||||||
// Add the node only if it did execute or if connected to second "optional" input
|
// Add the node only if it did execute or if connected to second "optional" input
|
||||||
|
if (workflow.settings.executionOrder === 'v1') {
|
||||||
const nodeToAdd = workflow.getNode(connectionData.node);
|
const nodeToAdd = workflow.getNode(connectionData.node);
|
||||||
nodesToAdd.push({
|
nodesToAdd.push({
|
||||||
position: nodeToAdd?.position || [0, 0],
|
position: nodeToAdd?.position || [0, 0],
|
||||||
connection: connectionData,
|
connection: connectionData,
|
||||||
outputIndex: parseInt(outputIndex, 10),
|
outputIndex: parseInt(outputIndex, 10),
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
this.addNodeToBeExecuted(
|
||||||
|
workflow,
|
||||||
|
connectionData,
|
||||||
|
parseInt(outputIndex, 10),
|
||||||
|
executionNode.name,
|
||||||
|
nodeSuccessData!,
|
||||||
|
runIndex,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (workflow.settings.executionOrder === 'v1') {
|
||||||
// Always execute the node that is more to the top-left first
|
// Always execute the node that is more to the top-left first
|
||||||
nodesToAdd.sort((a, b) => {
|
nodesToAdd.sort((a, b) => {
|
||||||
if (a.position[1] < b.position[1]) {
|
if (a.position[1] < b.position[1]) {
|
||||||
|
@ -1345,6 +1334,7 @@ export class WorkflowExecute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we got here, it means that we did not stop executing from manual executions / destination.
|
// If we got here, it means that we did not stop executing from manual executions / destination.
|
||||||
// Execute hooks now to make sure that all hooks are executed properly
|
// Execute hooks now to make sure that all hooks are executed properly
|
||||||
|
@ -1382,7 +1372,10 @@ export class WorkflowExecute {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check if the node is only allowed execute if all inputs received data
|
// Check if the node is only allowed execute if all inputs received data
|
||||||
let requiredInputs = nodeType.description.requiredInputs;
|
let requiredInputs =
|
||||||
|
workflow.settings.executionOrder === 'v1'
|
||||||
|
? nodeType.description.requiredInputs
|
||||||
|
: undefined;
|
||||||
if (requiredInputs !== undefined) {
|
if (requiredInputs !== undefined) {
|
||||||
if (typeof requiredInputs === 'string') {
|
if (typeof requiredInputs === 'string') {
|
||||||
requiredInputs = workflow.expression.getSimpleParameterValue(
|
requiredInputs = workflow.expression.getSimpleParameterValue(
|
||||||
|
|
|
@ -4,15 +4,15 @@ import { WorkflowExecute } from '@/WorkflowExecute';
|
||||||
|
|
||||||
import * as Helpers from './helpers';
|
import * as Helpers from './helpers';
|
||||||
import { initLogger } from './helpers/utils';
|
import { initLogger } from './helpers/utils';
|
||||||
import { predefinedWorkflowExecuteTests } from './helpers/constants';
|
import { legacyWorkflowExecuteTests, v1WorkflowExecuteTests } from './helpers/constants';
|
||||||
|
|
||||||
describe('WorkflowExecute', () => {
|
describe('WorkflowExecute', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
initLogger();
|
initLogger();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('run', () => {
|
describe('v0 execution order', () => {
|
||||||
const tests: WorkflowTestData[] = predefinedWorkflowExecuteTests;
|
const tests: WorkflowTestData[] = legacyWorkflowExecuteTests;
|
||||||
|
|
||||||
const executionMode = 'manual';
|
const executionMode = 'manual';
|
||||||
const nodeTypes = Helpers.NodeTypes();
|
const nodeTypes = Helpers.NodeTypes();
|
||||||
|
@ -25,6 +25,9 @@ describe('WorkflowExecute', () => {
|
||||||
connections: testData.input.workflowData.connections,
|
connections: testData.input.workflowData.connections,
|
||||||
active: false,
|
active: false,
|
||||||
nodeTypes,
|
nodeTypes,
|
||||||
|
settings: {
|
||||||
|
executionOrder: 'v0',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const waitPromise = await createDeferredPromise<IRun>();
|
const waitPromise = await createDeferredPromise<IRun>();
|
||||||
|
@ -71,6 +74,70 @@ describe('WorkflowExecute', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('v1 execution order', () => {
|
||||||
|
const tests: WorkflowTestData[] = v1WorkflowExecuteTests;
|
||||||
|
|
||||||
|
const executionMode = 'manual';
|
||||||
|
const nodeTypes = Helpers.NodeTypes();
|
||||||
|
|
||||||
|
for (const testData of tests) {
|
||||||
|
test(testData.description, async () => {
|
||||||
|
const workflowInstance = new Workflow({
|
||||||
|
id: 'test',
|
||||||
|
nodes: testData.input.workflowData.nodes,
|
||||||
|
connections: testData.input.workflowData.connections,
|
||||||
|
active: false,
|
||||||
|
nodeTypes,
|
||||||
|
settings: {
|
||||||
|
executionOrder: 'v1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const waitPromise = await createDeferredPromise<IRun>();
|
||||||
|
const nodeExecutionOrder: string[] = [];
|
||||||
|
const additionalData = Helpers.WorkflowExecuteAdditionalData(
|
||||||
|
waitPromise,
|
||||||
|
nodeExecutionOrder,
|
||||||
|
);
|
||||||
|
|
||||||
|
const workflowExecute = new WorkflowExecute(additionalData, executionMode);
|
||||||
|
|
||||||
|
const executionData = await workflowExecute.run(workflowInstance);
|
||||||
|
|
||||||
|
const result = await waitPromise.promise();
|
||||||
|
|
||||||
|
// Check if the data from WorkflowExecute is identical to data received
|
||||||
|
// by the webhooks
|
||||||
|
expect(executionData).toEqual(result);
|
||||||
|
|
||||||
|
// Check if the output data of the nodes is correct
|
||||||
|
for (const nodeName of Object.keys(testData.output.nodeData)) {
|
||||||
|
if (result.data.resultData.runData[nodeName] === undefined) {
|
||||||
|
throw new Error(`Data for node "${nodeName}" is missing!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultData = result.data.resultData.runData[nodeName].map((nodeData) => {
|
||||||
|
if (nodeData.data === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return nodeData.data.main[0]!.map((entry) => entry.json);
|
||||||
|
});
|
||||||
|
|
||||||
|
// expect(resultData).toEqual(testData.output.nodeData[nodeName]);
|
||||||
|
expect(resultData).toEqual(testData.output.nodeData[nodeName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the nodes did execute in the correct order
|
||||||
|
expect(nodeExecutionOrder).toEqual(testData.output.nodeExecutionOrder);
|
||||||
|
|
||||||
|
// Check if other data has correct value
|
||||||
|
expect(result.finished).toEqual(true);
|
||||||
|
expect(result.data.executionData!.contextData).toEqual({});
|
||||||
|
expect(result.data.executionData!.nodeExecutionStack).toEqual([]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//run tests on json files from specified directory, default 'workflows'
|
//run tests on json files from specified directory, default 'workflows'
|
||||||
//workflows must have pinned data that would be used to test output after execution
|
//workflows must have pinned data that would be used to test output after execution
|
||||||
describe('run test workflows', () => {
|
describe('run test workflows', () => {
|
||||||
|
@ -87,6 +154,7 @@ describe('WorkflowExecute', () => {
|
||||||
connections: testData.input.workflowData.connections,
|
connections: testData.input.workflowData.connections,
|
||||||
active: false,
|
active: false,
|
||||||
nodeTypes,
|
nodeTypes,
|
||||||
|
settings: testData.input.workflowData.settings,
|
||||||
});
|
});
|
||||||
|
|
||||||
const waitPromise = await createDeferredPromise<IRun>();
|
const waitPromise = await createDeferredPromise<IRun>();
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -702,6 +702,7 @@ export interface IWorkflowSettings extends IWorkflowSettingsWorkflow {
|
||||||
maxExecutionTimeout?: number;
|
maxExecutionTimeout?: number;
|
||||||
callerIds?: string;
|
callerIds?: string;
|
||||||
callerPolicy?: WorkflowSettings.CallerPolicy;
|
callerPolicy?: WorkflowSettings.CallerPolicy;
|
||||||
|
executionOrder: NonNullable<IWorkflowSettingsWorkflow['executionOrder']>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITimeoutHMS {
|
export interface ITimeoutHMS {
|
||||||
|
|
|
@ -7,6 +7,7 @@ export async function getNewWorkflow(context: IRestApiContext, name?: string) {
|
||||||
return {
|
return {
|
||||||
name: response.name,
|
name: response.name,
|
||||||
onboardingFlowEnabled: response.onboardingFlowEnabled === true,
|
onboardingFlowEnabled: response.onboardingFlowEnabled === true,
|
||||||
|
settings: response.defaultSettings,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,31 @@
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div v-loading="isLoading" class="workflow-settings" data-test-id="workflow-settings-dialog">
|
<div v-loading="isLoading" class="workflow-settings" data-test-id="workflow-settings-dialog">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="10" class="setting-name">
|
||||||
|
{{ $locale.baseText('workflowSettings.executionOrder') + ':' }}
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="14" class="ignore-key-press">
|
||||||
|
<n8n-select
|
||||||
|
v-model="workflowSettings.executionOrder"
|
||||||
|
placeholder="Select Execution Order"
|
||||||
|
size="medium"
|
||||||
|
filterable
|
||||||
|
:disabled="readOnlyEnv"
|
||||||
|
:limit-popper-width="true"
|
||||||
|
data-test-id="workflow-settings-execution-order"
|
||||||
|
>
|
||||||
|
<n8n-option
|
||||||
|
v-for="option in executionOrderOptions"
|
||||||
|
:key="option.key"
|
||||||
|
:label="option.value"
|
||||||
|
:value="option.key"
|
||||||
|
>
|
||||||
|
</n8n-option>
|
||||||
|
</n8n-select>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="10" class="setting-name">
|
<el-col :span="10" class="setting-name">
|
||||||
{{ $locale.baseText('workflowSettings.errorWorkflow') + ':' }}
|
{{ $locale.baseText('workflowSettings.errorWorkflow') + ':' }}
|
||||||
|
@ -421,9 +446,14 @@ export default defineComponent({
|
||||||
saveDataSuccessExecutionOptions: [] as Array<{ key: string; value: string }>,
|
saveDataSuccessExecutionOptions: [] as Array<{ key: string; value: string }>,
|
||||||
saveExecutionProgressOptions: [] as Array<{ key: string | boolean; value: string }>,
|
saveExecutionProgressOptions: [] as Array<{ key: string | boolean; value: string }>,
|
||||||
saveManualOptions: [] as Array<{ key: string | boolean; value: string }>,
|
saveManualOptions: [] as Array<{ key: string | boolean; value: string }>,
|
||||||
|
executionOrderOptions: [
|
||||||
|
{ key: 'v0', value: 'v0 (legacy)' },
|
||||||
|
{ key: 'v1', value: 'v1 (recommended)' },
|
||||||
|
] as Array<{ key: string; value: string }>,
|
||||||
timezones: [] as Array<{ key: string; value: string }>,
|
timezones: [] as Array<{ key: string; value: string }>,
|
||||||
workflowSettings: {} as IWorkflowSettings,
|
workflowSettings: {} as IWorkflowSettings,
|
||||||
workflows: [] as IWorkflowShortResponse[],
|
workflows: [] as IWorkflowShortResponse[],
|
||||||
|
executionOrder: 'v0',
|
||||||
executionTimeout: 0,
|
executionTimeout: 0,
|
||||||
maxExecutionTimeout: 0,
|
maxExecutionTimeout: 0,
|
||||||
timeoutHMS: { hours: 0, minutes: 0, seconds: 0 } as ITimeoutHMS,
|
timeoutHMS: { hours: 0, minutes: 0, seconds: 0 } as ITimeoutHMS,
|
||||||
|
@ -535,6 +565,9 @@ export default defineComponent({
|
||||||
if (workflowSettings.maxExecutionTimeout === undefined) {
|
if (workflowSettings.maxExecutionTimeout === undefined) {
|
||||||
workflowSettings.maxExecutionTimeout = this.rootStore.maxExecutionTimeout;
|
workflowSettings.maxExecutionTimeout = this.rootStore.maxExecutionTimeout;
|
||||||
}
|
}
|
||||||
|
if (workflowSettings.executionOrder === undefined) {
|
||||||
|
workflowSettings.executionOrder = 'v0';
|
||||||
|
}
|
||||||
|
|
||||||
this.workflowSettings = workflowSettings;
|
this.workflowSettings = workflowSettings;
|
||||||
this.timeoutHMS = this.convertToHMS(workflowSettings.executionTimeout);
|
this.timeoutHMS = this.convertToHMS(workflowSettings.executionTimeout);
|
||||||
|
|
|
@ -1595,6 +1595,7 @@
|
||||||
"workflowSettings.defaultTimezone": "Default - {defaultTimezoneValue}",
|
"workflowSettings.defaultTimezone": "Default - {defaultTimezoneValue}",
|
||||||
"workflowSettings.defaultTimezoneNotValid": "Default Timezone not valid",
|
"workflowSettings.defaultTimezoneNotValid": "Default Timezone not valid",
|
||||||
"workflowSettings.errorWorkflow": "Error Workflow",
|
"workflowSettings.errorWorkflow": "Error Workflow",
|
||||||
|
"workflowSettings.executionOrder": "Execution Order",
|
||||||
"workflowSettings.helpTexts.errorWorkflow": "A second workflow to run if the current one fails.<br />The second workflow should an 'Error Trigger' node.",
|
"workflowSettings.helpTexts.errorWorkflow": "A second workflow to run if the current one fails.<br />The second workflow should an 'Error Trigger' node.",
|
||||||
"workflowSettings.helpTexts.executionTimeout": "How long the workflow should wait before timing out",
|
"workflowSettings.helpTexts.executionTimeout": "How long the workflow should wait before timing out",
|
||||||
"workflowSettings.helpTexts.executionTimeoutToggle": "Whether to cancel workflow execution after a defined time",
|
"workflowSettings.helpTexts.executionTimeoutToggle": "Whether to cancel workflow execution after a defined time",
|
||||||
|
|
|
@ -82,24 +82,29 @@ import {
|
||||||
} from '@/utils';
|
} from '@/utils';
|
||||||
import { useNDVStore } from './ndv.store';
|
import { useNDVStore } from './ndv.store';
|
||||||
import { useNodeTypesStore } from './nodeTypes.store';
|
import { useNodeTypesStore } from './nodeTypes.store';
|
||||||
import { useWorkflowsEEStore } from '@/stores/workflows.ee.store';
|
|
||||||
import { useUsersStore } from '@/stores/users.store';
|
import { useUsersStore } from '@/stores/users.store';
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
import type { NodeMetadataMap } from '@/Interface';
|
import type { NodeMetadataMap } from '@/Interface';
|
||||||
|
|
||||||
const createEmptyWorkflow = (): IWorkflowDb => ({
|
const defaults: Omit<IWorkflowDb, 'id'> & { settings: NonNullable<IWorkflowDb['settings']> } = {
|
||||||
id: PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
|
||||||
name: '',
|
name: '',
|
||||||
active: false,
|
active: false,
|
||||||
createdAt: -1,
|
createdAt: -1,
|
||||||
updatedAt: -1,
|
updatedAt: -1,
|
||||||
connections: {},
|
connections: {},
|
||||||
nodes: [],
|
nodes: [],
|
||||||
settings: {},
|
settings: {
|
||||||
|
executionOrder: 'v1',
|
||||||
|
},
|
||||||
tags: [],
|
tags: [],
|
||||||
pinData: {},
|
pinData: {},
|
||||||
versionId: '',
|
versionId: '',
|
||||||
usedCredentials: [],
|
usedCredentials: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const createEmptyWorkflow = (): IWorkflowDb => ({
|
||||||
|
id: PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
||||||
|
...defaults,
|
||||||
});
|
});
|
||||||
|
|
||||||
let cachedWorkflowKey: string | null = '';
|
let cachedWorkflowKey: string | null = '';
|
||||||
|
@ -135,10 +140,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||||
return this.workflow.versionId;
|
return this.workflow.versionId;
|
||||||
},
|
},
|
||||||
workflowSettings(): IWorkflowSettings {
|
workflowSettings(): IWorkflowSettings {
|
||||||
if (this.workflow.settings === undefined) {
|
return this.workflow.settings ?? { ...defaults.settings };
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return this.workflow.settings;
|
|
||||||
},
|
},
|
||||||
workflowTags(): string[] {
|
workflowTags(): string[] {
|
||||||
return this.workflow.tags as string[];
|
return this.workflow.tags as string[];
|
||||||
|
@ -318,7 +320,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||||
// This has the advantage that it is very fast and does not cause problems with vuex
|
// This has the advantage that it is very fast and does not cause problems with vuex
|
||||||
// when the workflow replaces the node-parameters.
|
// when the workflow replaces the node-parameters.
|
||||||
getNodes(): INodeUi[] {
|
getNodes(): INodeUi[] {
|
||||||
const nodes = useWorkflowsStore().allNodes;
|
const nodes = this.allNodes;
|
||||||
const returnNodes: INodeUi[] = [];
|
const returnNodes: INodeUi[] = [];
|
||||||
|
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
|
@ -331,23 +333,21 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||||
// Returns a workflow instance.
|
// Returns a workflow instance.
|
||||||
getWorkflow(nodes: INodeUi[], connections: IConnections, copyData?: boolean): Workflow {
|
getWorkflow(nodes: INodeUi[], connections: IConnections, copyData?: boolean): Workflow {
|
||||||
const nodeTypes = this.getNodeTypes();
|
const nodeTypes = this.getNodeTypes();
|
||||||
let workflowId: string | undefined = useWorkflowsStore().workflowId;
|
let workflowId: string | undefined = this.workflowId;
|
||||||
if (workflowId && workflowId === PLACEHOLDER_EMPTY_WORKFLOW_ID) {
|
if (workflowId && workflowId === PLACEHOLDER_EMPTY_WORKFLOW_ID) {
|
||||||
workflowId = undefined;
|
workflowId = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowName = useWorkflowsStore().workflowName;
|
|
||||||
|
|
||||||
cachedWorkflow = new Workflow({
|
cachedWorkflow = new Workflow({
|
||||||
id: workflowId,
|
id: workflowId,
|
||||||
name: workflowName,
|
name: this.workflowName,
|
||||||
nodes: copyData ? deepCopy(nodes) : nodes,
|
nodes: copyData ? deepCopy(nodes) : nodes,
|
||||||
connections: copyData ? deepCopy(connections) : connections,
|
connections: copyData ? deepCopy(connections) : connections,
|
||||||
active: false,
|
active: false,
|
||||||
nodeTypes,
|
nodeTypes,
|
||||||
settings: useWorkflowsStore().workflowSettings,
|
settings: this.workflowSettings,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
pinData: useWorkflowsStore().getPinData,
|
pinData: this.getPinData,
|
||||||
});
|
});
|
||||||
|
|
||||||
return cachedWorkflow;
|
return cachedWorkflow;
|
||||||
|
@ -393,11 +393,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||||
},
|
},
|
||||||
|
|
||||||
async getNewWorkflowData(name?: string): Promise<INewWorkflowData> {
|
async getNewWorkflowData(name?: string): Promise<INewWorkflowData> {
|
||||||
const workflowsEEStore = useWorkflowsEEStore();
|
|
||||||
|
|
||||||
let workflowData = {
|
let workflowData = {
|
||||||
name: '',
|
name: '',
|
||||||
onboardingFlowEnabled: false,
|
onboardingFlowEnabled: false,
|
||||||
|
settings: { ...defaults.settings },
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
|
@ -426,6 +425,25 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
resetState(): void {
|
||||||
|
this.removeAllConnections({ setStateDirty: false });
|
||||||
|
this.removeAllNodes({ setStateDirty: false, removePinData: true });
|
||||||
|
|
||||||
|
// Reset workflow execution data
|
||||||
|
this.setWorkflowExecutionData(null);
|
||||||
|
this.resetAllNodesIssues();
|
||||||
|
|
||||||
|
this.setActive(defaults.active);
|
||||||
|
this.setWorkflowId(PLACEHOLDER_EMPTY_WORKFLOW_ID);
|
||||||
|
this.setWorkflowName({ newName: '', setStateDirty: false });
|
||||||
|
this.setWorkflowSettings({ ...defaults.settings });
|
||||||
|
this.setWorkflowTagIds([]);
|
||||||
|
|
||||||
|
this.activeExecutionId = null;
|
||||||
|
this.executingNode = null;
|
||||||
|
this.executionWaitingForWebhook = false;
|
||||||
|
},
|
||||||
|
|
||||||
setWorkflowId(id: string): void {
|
setWorkflowId(id: string): void {
|
||||||
this.workflow.id = id === 'new' ? PLACEHOLDER_EMPTY_WORKFLOW_ID : id;
|
this.workflow.id = id === 'new' ? PLACEHOLDER_EMPTY_WORKFLOW_ID : id;
|
||||||
},
|
},
|
||||||
|
@ -632,7 +650,9 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||||
...(!this.workflow.hasOwnProperty('updatedAt') ? { updatedAt: -1 } : {}),
|
...(!this.workflow.hasOwnProperty('updatedAt') ? { updatedAt: -1 } : {}),
|
||||||
...(!this.workflow.hasOwnProperty('id') ? { id: PLACEHOLDER_EMPTY_WORKFLOW_ID } : {}),
|
...(!this.workflow.hasOwnProperty('id') ? { id: PLACEHOLDER_EMPTY_WORKFLOW_ID } : {}),
|
||||||
...(!this.workflow.hasOwnProperty('nodes') ? { nodes: [] } : {}),
|
...(!this.workflow.hasOwnProperty('nodes') ? { nodes: [] } : {}),
|
||||||
...(!this.workflow.hasOwnProperty('settings') ? { settings: {} } : {}),
|
...(!this.workflow.hasOwnProperty('settings')
|
||||||
|
? { settings: { ...defaults.settings } }
|
||||||
|
: {}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -3523,22 +3523,7 @@ export default defineComponent({
|
||||||
// Ignore all errors
|
// Ignore all errors
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.workflowsStore.removeAllConnections({ setStateDirty: false });
|
this.workflowsStore.resetState();
|
||||||
this.workflowsStore.removeAllNodes({ setStateDirty: false, removePinData: true });
|
|
||||||
|
|
||||||
// Reset workflow execution data
|
|
||||||
this.workflowsStore.setWorkflowExecutionData(null);
|
|
||||||
this.workflowsStore.resetAllNodesIssues();
|
|
||||||
|
|
||||||
this.workflowsStore.setActive(false);
|
|
||||||
this.workflowsStore.setWorkflowId(PLACEHOLDER_EMPTY_WORKFLOW_ID);
|
|
||||||
this.workflowsStore.setWorkflowName({ newName: '', setStateDirty: false });
|
|
||||||
this.workflowsStore.setWorkflowSettings({});
|
|
||||||
this.workflowsStore.setWorkflowTagIds([]);
|
|
||||||
|
|
||||||
this.workflowsStore.activeExecutionId = null;
|
|
||||||
this.workflowsStore.executingNode = null;
|
|
||||||
this.workflowsStore.executionWaitingForWebhook = false;
|
|
||||||
this.uiStore.removeActiveAction('workflowRunning');
|
this.uiStore.removeActiveAction('workflowRunning');
|
||||||
|
|
||||||
this.uiStore.resetSelectedNodes();
|
this.uiStore.resetSelectedNodes();
|
||||||
|
|
|
@ -24,8 +24,7 @@ export class CompareDatasets implements INodeType {
|
||||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||||
inputs: ['main', 'main'],
|
inputs: ['main', 'main'],
|
||||||
inputNames: ['Input A', 'Input B'],
|
inputNames: ['Input A', 'Input B'],
|
||||||
forceInputNodeExecution: '={{ $version < 2.3 }}',
|
requiredInputs: 1,
|
||||||
requiredInputs: '={{ $version < 2.3 ? undefined : 1 }}',
|
|
||||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
|
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
|
||||||
outputs: ['main', 'main', 'main', 'main'],
|
outputs: ['main', 'main', 'main', 'main'],
|
||||||
outputNames: ['In A only', 'Same', 'Different', 'In B only'],
|
outputNames: ['In A only', 'Same', 'Different', 'In B only'],
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
{
|
{
|
||||||
"name": "Compare Datasets Node Test",
|
"name": "My workflow",
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
"parameters": {},
|
"parameters": {},
|
||||||
"id": "0312bddf-aae0-423c-9041-d54fb124934f",
|
"id": "4013708d-2460-44b7-923d-e3cd36eb9287",
|
||||||
"name": "When clicking \"Execute Workflow\"",
|
"name": "When clicking \"Execute Workflow\"",
|
||||||
"type": "n8n-nodes-base.manualTrigger",
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [480, 720]
|
"position": [-6660, 8040]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"jsCode": "return [\n {\n json: {\n number: 0\n }\n },\n {\n json: {\n number: 1\n }\n },\n {\n json: {\n number: 2\n }\n }\n];"
|
"jsCode": "return [\n {\n json: {\n number: 0\n }\n },\n {\n json: {\n number: 1\n }\n },\n {\n json: {\n number: 2\n }\n }\n];"
|
||||||
},
|
},
|
||||||
"id": "0542886d-6ab2-4695-b686-2cd60729ba9a",
|
"id": "be417cbe-7a4f-40eb-8a26-fca253a01882",
|
||||||
"name": "Code",
|
"name": "Code",
|
||||||
"type": "n8n-nodes-base.code",
|
"type": "n8n-nodes-base.code",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [900, 640]
|
"position": [-6240, 8040]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
|
@ -31,173 +31,53 @@
|
||||||
},
|
},
|
||||||
"options": {}
|
"options": {}
|
||||||
},
|
},
|
||||||
"id": "f3e5e43b-a3bf-46c7-acd7-ae3d7d19d9f9",
|
"id": "97d65223-16c9-4d26-b31e-d818e1adbc8a",
|
||||||
"name": "Compare Datasets 2.2 - Old",
|
"name": "Compare Datasets 2.2 - Old",
|
||||||
"type": "n8n-nodes-base.compareDatasets",
|
"type": "n8n-nodes-base.compareDatasets",
|
||||||
"typeVersion": 2.2,
|
"typeVersion": 2.2,
|
||||||
"position": [1260, 40]
|
"position": [-5800, 8140]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"jsCode": "return [\n {\n json: {\n number: 0\n }\n },\n {\n json: {\n number: 1,\n k: 2,\n }\n },\n {\n json: {\n number: 10\n }\n },\n {\n json: {\n number: 11\n }\n },\n {\n json: {\n number: 12\n }\n }\n];"
|
"jsCode": "return [\n {\n json: {\n number: 0\n }\n },\n {\n json: {\n number: 1,\n k: 2,\n }\n },\n {\n json: {\n number: 10\n }\n },\n {\n json: {\n number: 11\n }\n },\n {\n json: {\n number: 12\n }\n }\n];"
|
||||||
},
|
},
|
||||||
"id": "c62e90b3-f84a-48a5-94bf-3267a4c8b69e",
|
"id": "94580b8b-b698-4c49-98b6-12973b6f4220",
|
||||||
"name": "Code1",
|
"name": "Code1",
|
||||||
"type": "n8n-nodes-base.code",
|
"type": "n8n-nodes-base.code",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [900, 60]
|
"position": [-6240, 8260]
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"jsCode": "return [\n {\n json: {\n number: 0\n }\n },\n {\n json: {\n number: 1,\n k: 2,\n }\n },\n {\n json: {\n number: 10\n }\n },\n {\n json: {\n number: 11\n }\n },\n {\n json: {\n number: 12\n }\n }\n];"
|
|
||||||
},
|
|
||||||
"id": "46320ca2-8e8e-4ecf-b4f6-5899807c1500",
|
|
||||||
"name": "Code2",
|
|
||||||
"type": "n8n-nodes-base.code",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [900, 840]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {},
|
"parameters": {},
|
||||||
"id": "4ae12a83-5d3f-4d5b-845b-65c930d8ef5a",
|
"id": "78ec40a4-775d-467b-bacb-805658190b29",
|
||||||
"name": "Old - A only",
|
"name": "Old - A only",
|
||||||
"type": "n8n-nodes-base.noOp",
|
"type": "n8n-nodes-base.noOp",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [1520, -180]
|
"position": [-5540, 7920]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {},
|
"parameters": {},
|
||||||
"id": "d4f5fd94-4b46-4b8c-8b8a-073e8c32ad85",
|
"id": "eaa4cc93-53c6-4d00-9407-bd5da23e868e",
|
||||||
"name": "New - A only",
|
|
||||||
"type": "n8n-nodes-base.noOp",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [1520, 440]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {},
|
|
||||||
"id": "0939f79b-fd75-4d2f-b40b-50780114c3f2",
|
|
||||||
"name": "Old - Same",
|
"name": "Old - Same",
|
||||||
"type": "n8n-nodes-base.noOp",
|
"type": "n8n-nodes-base.noOp",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [1520, -40]
|
"position": [-5540, 8060]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {},
|
"parameters": {},
|
||||||
"id": "199ea52c-b30a-401d-a920-9db5c8e10d38",
|
"id": "d073db39-1902-411d-80dd-6ea8f42ac33b",
|
||||||
"name": "Old - Different",
|
"name": "Old - Different",
|
||||||
"type": "n8n-nodes-base.noOp",
|
"type": "n8n-nodes-base.noOp",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [1520, 100]
|
"position": [-5540, 8200]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {},
|
"parameters": {},
|
||||||
"id": "1ebcb5bb-3061-47ef-8c79-847ae8bdb568",
|
"id": "29f92258-4869-43c1-9cef-9c281397ccc8",
|
||||||
"name": "Old - B only",
|
"name": "Old - B only",
|
||||||
"type": "n8n-nodes-base.noOp",
|
"type": "n8n-nodes-base.noOp",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [1520, 240]
|
"position": [-5540, 8340]
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {},
|
|
||||||
"id": "38689dbf-49f2-4f3b-855b-abd821ec316f",
|
|
||||||
"name": "New - B only",
|
|
||||||
"type": "n8n-nodes-base.noOp",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [1520, 860]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {},
|
|
||||||
"id": "dfcac903-95dc-4519-b49f-a6a65bf8fdb8",
|
|
||||||
"name": "New - Different",
|
|
||||||
"type": "n8n-nodes-base.noOp",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [1520, 720]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {},
|
|
||||||
"id": "b8588ebc-4dc8-41f5-9a0a-64d151d7122e",
|
|
||||||
"name": "New - Same",
|
|
||||||
"type": "n8n-nodes-base.noOp",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [1520, 580]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"jsCode": "return [\n {\n json: {\n number: 0\n }\n },\n {\n json: {\n number: 1,\n k: 2,\n }\n },\n {\n json: {\n number: 10\n }\n },\n {\n json: {\n number: 11\n }\n },\n {\n json: {\n number: 12\n }\n }\n];"
|
|
||||||
},
|
|
||||||
"id": "d35f3f52-c967-46e8-be3a-0bf709b20ef8",
|
|
||||||
"name": "Code3",
|
|
||||||
"type": "n8n-nodes-base.code",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [880, 1340]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {},
|
|
||||||
"id": "75690ad6-c870-4950-9085-12fdd9c12ddd",
|
|
||||||
"name": "New - A only1",
|
|
||||||
"type": "n8n-nodes-base.noOp",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [1520, 1100]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {},
|
|
||||||
"id": "9abb523f-349c-48b0-b36b-0c74064a6219",
|
|
||||||
"name": "New - B only1",
|
|
||||||
"type": "n8n-nodes-base.noOp",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [1520, 1520]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {},
|
|
||||||
"id": "c4230d94-eaa6-420d-baff-288463722a03",
|
|
||||||
"name": "New - Different1",
|
|
||||||
"type": "n8n-nodes-base.noOp",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [1520, 1380]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {},
|
|
||||||
"id": "58287181-dc90-4806-a053-83b3ef36e673",
|
|
||||||
"name": "New - Same1",
|
|
||||||
"type": "n8n-nodes-base.noOp",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [1520, 1240]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"mergeByFields": {
|
|
||||||
"values": [
|
|
||||||
{
|
|
||||||
"field1": "number",
|
|
||||||
"field2": "number"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"id": "5514c636-ee64-45f3-8832-77ad6652cc08",
|
|
||||||
"name": "Compare Datasets 2.3 - New - Connected",
|
|
||||||
"type": "n8n-nodes-base.compareDatasets",
|
|
||||||
"typeVersion": 2.3,
|
|
||||||
"position": [1260, 1320]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"mergeByFields": {
|
|
||||||
"values": [
|
|
||||||
{
|
|
||||||
"field1": "number",
|
|
||||||
"field2": "number"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"id": "a25f4766-9bb4-40a0-9ae2-ef0004f5938a",
|
|
||||||
"name": "Compare Datasets 2.3 - New - Not Connected",
|
|
||||||
"type": "n8n-nodes-base.compareDatasets",
|
|
||||||
"typeVersion": 2.3,
|
|
||||||
"position": [1260, 660]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"pinData": {
|
"pinData": {
|
||||||
|
@ -249,72 +129,6 @@
|
||||||
"number": 12
|
"number": 12
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"New - A only": [
|
|
||||||
{
|
|
||||||
"json": {
|
|
||||||
"number": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"json": {
|
|
||||||
"number": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"json": {
|
|
||||||
"number": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"New - A only1": [
|
|
||||||
{
|
|
||||||
"json": {
|
|
||||||
"number": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"New - Same1": [
|
|
||||||
{
|
|
||||||
"json": {
|
|
||||||
"number": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"New - Different1": [
|
|
||||||
{
|
|
||||||
"json": {
|
|
||||||
"keys": {
|
|
||||||
"number": 1
|
|
||||||
},
|
|
||||||
"same": {
|
|
||||||
"number": 1
|
|
||||||
},
|
|
||||||
"different": {
|
|
||||||
"k": {
|
|
||||||
"inputA": null,
|
|
||||||
"inputB": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"New - B only1": [
|
|
||||||
{
|
|
||||||
"json": {
|
|
||||||
"number": 10
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"json": {
|
|
||||||
"number": 11
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"json": {
|
|
||||||
"number": 12
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"connections": {
|
"connections": {
|
||||||
|
@ -325,11 +139,6 @@
|
||||||
"node": "Code",
|
"node": "Code",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0
|
||||||
},
|
|
||||||
{
|
|
||||||
"node": "Code3",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -337,20 +146,10 @@
|
||||||
"Code": {
|
"Code": {
|
||||||
"main": [
|
"main": [
|
||||||
[
|
[
|
||||||
{
|
|
||||||
"node": "Compare Datasets 2.3 - New - Not Connected",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"node": "Compare Datasets 2.2 - Old",
|
"node": "Compare Datasets 2.2 - Old",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0
|
||||||
},
|
|
||||||
{
|
|
||||||
"node": "Compare Datasets 2.3 - New - Connected",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -366,17 +165,6 @@
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Code2": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Compare Datasets 2.3 - New - Not Connected",
|
|
||||||
"type": "main",
|
|
||||||
"index": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Compare Datasets 2.2 - Old": {
|
"Compare Datasets 2.2 - Old": {
|
||||||
"main": [
|
"main": [
|
||||||
[
|
[
|
||||||
|
@ -408,87 +196,14 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"Code3": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Compare Datasets 2.3 - New - Connected",
|
|
||||||
"type": "main",
|
|
||||||
"index": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Compare Datasets 2.3 - New - Connected": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "New - A only1",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "New - Same1",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "New - Different1",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "New - B only1",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Compare Datasets 2.3 - New - Not Connected": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "New - A only",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "New - Same",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "New - Different",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "New - B only",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"active": false,
|
"active": false,
|
||||||
"settings": {},
|
"settings": {
|
||||||
"versionId": "d5c0f040-7406-4e69-bd5d-a362a739c8d8",
|
"executionOrder": "v0"
|
||||||
"id": "1114",
|
},
|
||||||
|
"versionId": "49a52ed2-ec4b-44b1-9dbd-c13fac4144f2",
|
||||||
|
"id": "ZpXvXjaKKZihfA2x",
|
||||||
"meta": {
|
"meta": {
|
||||||
"instanceId": "021d3c82ba2d3bc090cbf4fc81c9312668bcc34297e022bb3438c5c88a43a5ff"
|
"instanceId": "021d3c82ba2d3bc090cbf4fc81c9312668bcc34297e022bb3438c5c88a43a5ff"
|
||||||
},
|
},
|
||||||
|
|
|
@ -2366,7 +2366,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"active": false,
|
"active": false,
|
||||||
"settings": {},
|
"settings": {
|
||||||
|
"executionOrder": "v1"
|
||||||
|
},
|
||||||
"versionId": "6b37555c-fd78-4135-9c59-4912a71e18db",
|
"versionId": "6b37555c-fd78-4135-9c59-4912a71e18db",
|
||||||
"id": "1107",
|
"id": "1107",
|
||||||
"meta": {
|
"meta": {
|
||||||
|
|
|
@ -30,7 +30,6 @@ const versionDescription: INodeTypeDescription = {
|
||||||
inputs: ['main', 'main'],
|
inputs: ['main', 'main'],
|
||||||
outputs: ['main'],
|
outputs: ['main'],
|
||||||
inputNames: ['Input 1', 'Input 2'],
|
inputNames: ['Input 1', 'Input 2'],
|
||||||
forceInputNodeExecution: true,
|
|
||||||
properties: [
|
properties: [
|
||||||
oldVersionNotice,
|
oldVersionNotice,
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,9 +48,7 @@ const versionDescription: INodeTypeDescription = {
|
||||||
inputNames: ['Input 1', 'Input 2'],
|
inputNames: ['Input 1', 'Input 2'],
|
||||||
// If the node is of version 2.2 or if mode is chooseBranch data from both branches is required
|
// If the node is of version 2.2 or if mode is chooseBranch data from both branches is required
|
||||||
// to continue, else data from any input suffices
|
// to continue, else data from any input suffices
|
||||||
requiredInputs:
|
requiredInputs: '={{ $parameter["mode"] === "chooseBranch" ? [0, 1] : 1 }}',
|
||||||
'={{ $version < 2.2 ? undefined : ($parameter["mode"] === "chooseBranch" ? [0, 1] : 1) }}',
|
|
||||||
forceInputNodeExecution: '={{ $version < 2.2 }}',
|
|
||||||
properties: [
|
properties: [
|
||||||
{
|
{
|
||||||
displayName: 'Mode',
|
displayName: 'Mode',
|
||||||
|
|
|
@ -12,6 +12,7 @@ export async function executeWorkflow(testData: WorkflowTestData, nodeTypes: INo
|
||||||
connections: testData.input.workflowData.connections,
|
connections: testData.input.workflowData.connections,
|
||||||
active: false,
|
active: false,
|
||||||
nodeTypes,
|
nodeTypes,
|
||||||
|
settings: testData.input.workflowData.settings,
|
||||||
});
|
});
|
||||||
const waitPromise = await createDeferredPromise<IRun>();
|
const waitPromise = await createDeferredPromise<IRun>();
|
||||||
const nodeExecutionOrder: string[] = [];
|
const nodeExecutionOrder: string[] = [];
|
||||||
|
|
|
@ -1429,8 +1429,7 @@ export interface INodeTypeDescription extends INodeTypeBaseDescription {
|
||||||
eventTriggerDescription?: string;
|
eventTriggerDescription?: string;
|
||||||
activationMessage?: string;
|
activationMessage?: string;
|
||||||
inputs: string[];
|
inputs: string[];
|
||||||
forceInputNodeExecution?: string | boolean; // TODO: This option should be deprecated after a while
|
requiredInputs?: string | number[] | number; // Ony available with executionOrder => "v1"
|
||||||
requiredInputs?: string | number[] | number;
|
|
||||||
inputNames?: string[];
|
inputNames?: string[];
|
||||||
outputs: string[];
|
outputs: string[];
|
||||||
outputNames?: string[];
|
outputNames?: string[];
|
||||||
|
@ -1770,6 +1769,7 @@ export interface IWorkflowSettings {
|
||||||
saveManualExecutions?: 'DEFAULT' | boolean;
|
saveManualExecutions?: 'DEFAULT' | boolean;
|
||||||
saveExecutionProgress?: 'DEFAULT' | boolean;
|
saveExecutionProgress?: 'DEFAULT' | boolean;
|
||||||
executionTimeout?: number;
|
executionTimeout?: number;
|
||||||
|
executionOrder?: 'v0' | 'v1';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkflowTestData {
|
export interface WorkflowTestData {
|
||||||
|
|
|
@ -1191,18 +1191,7 @@ export class Workflow {
|
||||||
connectionInputData = inputData.main[0] as INodeExecutionData[];
|
connectionInputData = inputData.main[0] as INodeExecutionData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
let forceInputNodeExecution = nodeType.description.forceInputNodeExecution;
|
const forceInputNodeExecution = this.settings.executionOrder !== 'v1';
|
||||||
if (forceInputNodeExecution !== undefined) {
|
|
||||||
if (typeof forceInputNodeExecution === 'string') {
|
|
||||||
forceInputNodeExecution = !!this.expression.getSimpleParameterValue(
|
|
||||||
node,
|
|
||||||
forceInputNodeExecution,
|
|
||||||
mode,
|
|
||||||
additionalData.timezone,
|
|
||||||
{ $version: node.typeVersion },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!forceInputNodeExecution) {
|
if (!forceInputNodeExecution) {
|
||||||
// If the nodes do not get force executed data of some inputs may be missing
|
// If the nodes do not get force executed data of some inputs may be missing
|
||||||
// for that reason do we use the data of the first one that contains any
|
// for that reason do we use the data of the first one that contains any
|
||||||
|
@ -1213,7 +1202,6 @@ export class Workflow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionInputData.length === 0) {
|
if (connectionInputData.length === 0) {
|
||||||
// No data for node so return
|
// No data for node so return
|
||||||
|
|
Loading…
Reference in a new issue