mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 10:31:15 +00:00
fix(editor): Fix partial chat executions (#15379)
This commit is contained in:
@@ -359,6 +359,64 @@ describe('useRunWorkflow({ router })', () => {
|
||||
expect(result).toEqual(mockExecutionResponse);
|
||||
});
|
||||
|
||||
it('should exclude destinationNode from startNodes when provided', async () => {
|
||||
// ARRANGE
|
||||
const mockExecutionResponse = { executionId: '123' };
|
||||
const { runWorkflow } = useRunWorkflow({ router });
|
||||
const dataCaptor = captor<IStartRunData>();
|
||||
|
||||
const parentNodeName = 'parentNode';
|
||||
const destinationNodeName = 'destinationNode';
|
||||
|
||||
// Mock workflow with parent-child relationship
|
||||
const workflow = {
|
||||
name: 'Test Workflow',
|
||||
id: 'workflowId',
|
||||
getParentNodes: vi.fn().mockImplementation((nodeName: string) => {
|
||||
if (nodeName === destinationNodeName) {
|
||||
return [parentNodeName];
|
||||
}
|
||||
return [];
|
||||
}),
|
||||
nodes: {
|
||||
[parentNodeName]: createTestNode({ name: parentNodeName }),
|
||||
[destinationNodeName]: createTestNode({ name: destinationNodeName }),
|
||||
},
|
||||
} as unknown as Workflow;
|
||||
|
||||
vi.mocked(pushConnectionStore).isConnected = true;
|
||||
vi.mocked(workflowsStore).runWorkflow.mockResolvedValue(mockExecutionResponse);
|
||||
vi.mocked(workflowsStore).nodesIssuesExist = false;
|
||||
vi.mocked(workflowHelpers).getCurrentWorkflow.mockReturnValue(workflow);
|
||||
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue({
|
||||
id: 'workflowId',
|
||||
nodes: [],
|
||||
} as unknown as IWorkflowData);
|
||||
|
||||
vi.mocked(workflowsStore).getWorkflowRunData = {
|
||||
[parentNodeName]: [
|
||||
{
|
||||
startTime: 1,
|
||||
executionTime: 0,
|
||||
source: [],
|
||||
data: { main: [[{ json: { test: 'data' } }]] },
|
||||
},
|
||||
],
|
||||
} as unknown as IRunData;
|
||||
|
||||
// ACT
|
||||
await runWorkflow({ destinationNode: destinationNodeName });
|
||||
|
||||
// ASSERT
|
||||
expect(workflowsStore.runWorkflow).toHaveBeenCalledTimes(1);
|
||||
expect(workflowsStore.runWorkflow).toHaveBeenCalledWith(dataCaptor);
|
||||
|
||||
const startNodes = dataCaptor.value.startNodes ?? [];
|
||||
const destinationInStartNodes = startNodes.some((node) => node.name === destinationNodeName);
|
||||
|
||||
expect(destinationInStartNodes).toBe(false);
|
||||
});
|
||||
|
||||
it('should send dirty nodes for partial executions v2', async () => {
|
||||
vi.mocked(settingsStore).partialExecutionVersion = 2;
|
||||
const composable = useRunWorkflow({ router });
|
||||
|
||||
@@ -163,6 +163,7 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
|
||||
let triggerToStartFrom: IStartRunData['triggerToStartFrom'];
|
||||
if (
|
||||
startNodeNames.length === 0 &&
|
||||
directParentNodes.length === 0 &&
|
||||
'destinationNode' in options &&
|
||||
options.destinationNode !== undefined
|
||||
) {
|
||||
@@ -174,6 +175,8 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
|
||||
);
|
||||
newRunData = { [options.triggerNode]: [options.nodeData] };
|
||||
executedNode = options.triggerNode;
|
||||
} else if (options.destinationNode) {
|
||||
executedNode = options.destinationNode;
|
||||
}
|
||||
|
||||
if (options.triggerNode) {
|
||||
@@ -237,24 +240,35 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
|
||||
const version = settingsStore.partialExecutionVersion;
|
||||
|
||||
// TODO: this will be redundant once we cleanup the partial execution v1
|
||||
const startNodes: StartNodeData[] = sortNodesByYPosition(startNodeNames).map((name) => {
|
||||
// Find for each start node the source data
|
||||
let sourceData = get(runData, [name, 0, 'source', 0], null);
|
||||
if (sourceData === null) {
|
||||
const parentNodes = workflow.getParentNodes(name, NodeConnectionTypes.Main, 1);
|
||||
const executeData = workflowHelpers.executeData(
|
||||
parentNodes,
|
||||
const startNodes: StartNodeData[] = sortNodesByYPosition(startNodeNames)
|
||||
.map((name) => {
|
||||
// Find for each start node the source data
|
||||
let sourceData = get(runData, [name, 0, 'source', 0], null);
|
||||
if (sourceData === null) {
|
||||
const parentNodes = workflow.getParentNodes(name, NodeConnectionTypes.Main, 1);
|
||||
const executeData = workflowHelpers.executeData(
|
||||
parentNodes,
|
||||
name,
|
||||
NodeConnectionTypes.Main,
|
||||
0,
|
||||
);
|
||||
sourceData = get(executeData, ['source', NodeConnectionTypes.Main, 0], null);
|
||||
}
|
||||
return {
|
||||
name,
|
||||
NodeConnectionTypes.Main,
|
||||
0,
|
||||
);
|
||||
sourceData = get(executeData, ['source', NodeConnectionTypes.Main, 0], null);
|
||||
}
|
||||
return {
|
||||
name,
|
||||
sourceData,
|
||||
};
|
||||
});
|
||||
sourceData,
|
||||
};
|
||||
})
|
||||
// If a destination node is specified and it has chat parent, we don't want to include it in the start nodes
|
||||
.filter((node) => {
|
||||
if (
|
||||
options.destinationNode &&
|
||||
workflowsStore.checkIfNodeHasChatParent(options.destinationNode)
|
||||
) {
|
||||
return node.name !== options.destinationNode;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const singleWebhookTrigger = triggers.find((node) =>
|
||||
SINGLE_WEBHOOK_TRIGGERS.includes(node.type),
|
||||
|
||||
Reference in New Issue
Block a user