mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-26 05:04:05 -08:00
fix(editor): Rerun failed nodes in manual executions (#9050)
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
parent
f6ce81e7da
commit
bc6575afbb
|
@ -7,7 +7,7 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import type { IPinData, IRunData, Workflow } from 'n8n-workflow';
|
import { ExpressionError, type IPinData, type IRunData, type Workflow } from 'n8n-workflow';
|
||||||
|
|
||||||
vi.mock('@/stores/n8nRoot.store', () => ({
|
vi.mock('@/stores/n8nRoot.store', () => ({
|
||||||
useRootStore: vi.fn().mockReturnValue({ pushConnectionActive: true }),
|
useRootStore: vi.fn().mockReturnValue({ pushConnectionActive: true }),
|
||||||
|
@ -281,5 +281,34 @@ describe('useRunWorkflow({ router })', () => {
|
||||||
expect(result.startNodeNames).toContain('node1');
|
expect(result.startNodeNames).toContain('node1');
|
||||||
expect(result.runData).toBeUndefined();
|
expect(result.runData).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should rerun failed parent nodes, adding them to the returned list of start nodes and not adding their result to runData', () => {
|
||||||
|
const { consolidateRunDataAndStartNodes } = useRunWorkflow({ router });
|
||||||
|
const directParentNodes = ['node1'];
|
||||||
|
const runData = {
|
||||||
|
node1: [
|
||||||
|
{
|
||||||
|
error: new ExpressionError('error'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as unknown as IRunData;
|
||||||
|
const workflowMock = {
|
||||||
|
getParentNodes: vi.fn().mockReturnValue([]),
|
||||||
|
nodes: {
|
||||||
|
node1: { disabled: false },
|
||||||
|
node2: { disabled: false },
|
||||||
|
},
|
||||||
|
} as unknown as Workflow;
|
||||||
|
|
||||||
|
const result = consolidateRunDataAndStartNodes(
|
||||||
|
directParentNodes,
|
||||||
|
runData,
|
||||||
|
undefined,
|
||||||
|
workflowMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.startNodeNames).toContain('node1');
|
||||||
|
expect(result.runData).toEqual(undefined);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -349,14 +349,19 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
|
||||||
parentNodes.push(directParentNode);
|
parentNodes.push(directParentNode);
|
||||||
|
|
||||||
for (const parentNode of parentNodes) {
|
for (const parentNode of parentNodes) {
|
||||||
if (!runData[parentNode]?.length && !pinData?.[parentNode]?.length) {
|
// We want to execute nodes that don't have run data neither pin data
|
||||||
|
// in addition, if a node failed we want to execute it again
|
||||||
|
if (
|
||||||
|
(!runData[parentNode]?.length && !pinData?.[parentNode]?.length) ||
|
||||||
|
runData[parentNode]?.[0]?.error !== undefined
|
||||||
|
) {
|
||||||
// When we hit a node which has no data we stop and set it
|
// When we hit a node which has no data we stop and set it
|
||||||
// as a start node the execution from and then go on with other
|
// as a start node the execution from and then go on with other
|
||||||
// direct input nodes
|
// direct input nodes
|
||||||
startNodeNames.push(parentNode);
|
startNodeNames.push(parentNode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (runData[parentNode]) {
|
if (runData[parentNode] && !runData[parentNode]?.[0]?.error) {
|
||||||
newRunData[parentNode] = runData[parentNode]?.slice(0, 1);
|
newRunData[parentNode] = runData[parentNode]?.slice(0, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue