refactor(editor): Remove part of getCurrentWorkflow usages (#16148)

This commit is contained in:
Alex Grozav
2025-07-24 12:51:32 +03:00
committed by GitHub
parent b09f73701d
commit c30dbc6dd4
29 changed files with 578 additions and 391 deletions

View File

@@ -174,8 +174,7 @@ function useJsonFieldCompletions() {
try {
const activeNode = ndvStore.activeNode;
if (activeNode) {
const workflow = workflowsStore.getCurrentWorkflow();
const input = workflow.connectionsByDestinationNode[activeNode.name];
const input = workflowsStore.connectionsByDestinationNode[activeNode.name];
return input.main[0] ? input.main[0][0].node : null;
}
} catch (e) {

View File

@@ -84,7 +84,7 @@ function getMultipleNodesText(nodeName: string): string {
return '';
const activeNodeConnections =
props.workflow.connectionsByDestinationNode[activeNode.value.name].main || [];
workflowsStore.connectionsByDestinationNode[activeNode.value.name].main || [];
// Collect indexes of connected nodes
const connectedInputIndexes = activeNodeConnections.reduce((acc: number[], node, index) => {
if (node?.[0] && node[0].node === nodeName) return [...acc, index];

View File

@@ -804,7 +804,8 @@ function getNodeHints(): NodeHint[] {
node: node.value,
nodeType: nodeType.value,
nodeOutputData,
workflow: props.workflow,
nodes: workflowsStore.allNodes,
connections: workflowsStore.connectionsBySourceNode,
hasNodeRun: hasNodeRun.value,
hasMultipleInputItems,
});

View File

@@ -114,6 +114,7 @@ const expressionResolveCtx = computed<ExpressionLocalResolveContext | undefined>
nodeName,
additionalKeys: {},
inputNode: findInputNode(),
connections: workflowsStore.connectionsBySourceNode,
};
});

View File

@@ -216,6 +216,8 @@ describe('useCanvasMapping', () => {
});
it('should handle input and output connections', () => {
const workflowsStore = mockedStore(useWorkflowsStore);
const [manualTriggerNode, setNode] = mockNodes.slice(0, 2);
const nodes = [manualTriggerNode, setNode];
const connections = {
@@ -225,6 +227,9 @@ describe('useCanvasMapping', () => {
],
},
};
workflowsStore.workflow.connections = connections;
const workflowObject = createTestWorkflowObject({
nodes,
connections,

View File

@@ -59,6 +59,7 @@ import { useNodeHelpers } from './useNodeHelpers';
import { getTriggerNodeServiceName } from '@/utils/nodeTypesUtils';
import { useNodeDirtiness } from '@/composables/useNodeDirtiness';
import { getNodeIconSource } from '../utils/nodeIcon';
import * as workflowUtils from 'n8n-workflow/common';
export function useCanvasMapping({
nodes,
@@ -571,56 +572,62 @@ export function useCanvasMapping({
}, {});
});
const mappedNodes = computed<CanvasNode[]>(() => [
...nodes.value.map<CanvasNode>((node) => {
const inputConnections = workflowObject.value.connectionsByDestinationNode[node.name] ?? {};
const outputConnections = workflowObject.value.connectionsBySourceNode[node.name] ?? {};
const mappedNodes = computed<CanvasNode[]>(() => {
const connectionsBySourceNode = connections.value;
const connectionsByDestinationNode =
workflowUtils.mapConnectionsByDestination(connectionsBySourceNode);
const data: CanvasNodeData = {
id: node.id,
name: node.name,
subtitle: nodeSubtitleById.value[node.id] ?? '',
type: node.type,
typeVersion: node.typeVersion,
disabled: node.disabled,
inputs: nodeInputsById.value[node.id] ?? [],
outputs: nodeOutputsById.value[node.id] ?? [],
connections: {
[CanvasConnectionMode.Input]: inputConnections,
[CanvasConnectionMode.Output]: outputConnections,
},
issues: {
items: nodeIssuesById.value[node.id],
visible: nodeHasIssuesById.value[node.id],
},
pinnedData: {
count: nodePinnedDataById.value[node.id]?.length ?? 0,
visible: !!nodePinnedDataById.value[node.id],
},
execution: {
status: nodeExecutionStatusById.value[node.id],
waiting: nodeExecutionWaitingById.value[node.id],
waitingForNext: nodeExecutionWaitingForNextById.value[node.id],
running: nodeExecutionRunningById.value[node.id],
},
runData: {
outputMap: nodeExecutionRunDataOutputMapById.value[node.id],
iterations: nodeExecutionRunDataById.value[node.id]?.length ?? 0,
visible: !!nodeExecutionRunDataById.value[node.id],
},
render: renderTypeByNodeId.value[node.id] ?? { type: 'default', options: {} },
};
return [
...nodes.value.map<CanvasNode>((node) => {
const outputConnections = connectionsBySourceNode[node.name] ?? {};
const inputConnections = connectionsByDestinationNode[node.name] ?? {};
return {
id: node.id,
label: node.name,
type: 'canvas-node',
position: { x: node.position[0], y: node.position[1] },
data,
...additionalNodePropertiesById.value[node.id],
};
}),
]);
const data: CanvasNodeData = {
id: node.id,
name: node.name,
subtitle: nodeSubtitleById.value[node.id] ?? '',
type: node.type,
typeVersion: node.typeVersion,
disabled: node.disabled,
inputs: nodeInputsById.value[node.id] ?? [],
outputs: nodeOutputsById.value[node.id] ?? [],
connections: {
[CanvasConnectionMode.Input]: inputConnections,
[CanvasConnectionMode.Output]: outputConnections,
},
issues: {
items: nodeIssuesById.value[node.id],
visible: nodeHasIssuesById.value[node.id],
},
pinnedData: {
count: nodePinnedDataById.value[node.id]?.length ?? 0,
visible: !!nodePinnedDataById.value[node.id],
},
execution: {
status: nodeExecutionStatusById.value[node.id],
waiting: nodeExecutionWaitingById.value[node.id],
waitingForNext: nodeExecutionWaitingForNextById.value[node.id],
running: nodeExecutionRunningById.value[node.id],
},
runData: {
outputMap: nodeExecutionRunDataOutputMapById.value[node.id],
iterations: nodeExecutionRunDataById.value[node.id]?.length ?? 0,
visible: !!nodeExecutionRunDataById.value[node.id],
},
render: renderTypeByNodeId.value[node.id] ?? { type: 'default', options: {} },
};
return {
id: node.id,
label: node.name,
type: 'canvas-node',
position: { x: node.position[0], y: node.position[1] },
data,
...additionalNodePropertiesById.value[node.id],
};
}),
];
});
const mappedConnections = computed<CanvasConnection[]>(() => {
return mapLegacyConnectionsToCanvasConnections(connections.value ?? [], nodes.value ?? []).map(

View File

@@ -253,6 +253,7 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
if (sourceData === null) {
const parentNodes = workflow.getParentNodes(name, NodeConnectionTypes.Main, 1);
const executeData = workflowHelpers.executeData(
workflow.connectionsBySourceNode,
parentNodes,
name,
NodeConnectionTypes.Main,

View File

@@ -14,11 +14,8 @@ import {
createTestWorkflowExecutionResponse,
createTestWorkflowObject,
} from '@/__tests__/mocks';
import {
NodeConnectionTypes,
WEBHOOK_NODE_TYPE,
type AssignmentCollectionValue,
} from 'n8n-workflow';
import { NodeConnectionTypes, WEBHOOK_NODE_TYPE } from 'n8n-workflow';
import type { AssignmentCollectionValue, IConnections } from 'n8n-workflow';
import * as apiWebhooks from '@n8n/rest-api-client/api/webhooks';
import { mockedStore } from '@/__tests__/utils';
@@ -519,7 +516,7 @@ describe('useWorkflowHelpers', () => {
const inputName = 'main';
const runIndex = 0;
const result = executeData(parentNodes, currentNode, inputName, runIndex);
const result = executeData({}, parentNodes, currentNode, inputName, runIndex);
expect(result).toEqual({
node: {},
@@ -538,18 +535,15 @@ describe('useWorkflowHelpers', () => {
const jsonData = {
name: 'Test',
};
workflowsStore.getCurrentWorkflow.mockReturnValue({
connectionsByDestinationNode: {
Set: {
main: [
[
{ node: 'Start', index: 0, type: 'main' },
{ node: 'Set', index: 0, type: 'main' },
],
],
},
const connectionsBySourceNode: IConnections = {
Start: {
main: [[{ node: 'Set', index: 0, type: 'main' }]],
},
} as never);
Set: {
main: [[{ node: 'Start', index: 0, type: 'main' }]],
},
};
workflowsStore.workflowExecutionData = {
data: {
@@ -575,7 +569,13 @@ describe('useWorkflowHelpers', () => {
},
} as unknown as IExecutionResponse;
const result = executeData(parentNodes, currentNode, inputName, runIndex);
const result = executeData(
connectionsBySourceNode,
parentNodes,
currentNode,
inputName,
runIndex,
);
expect(result).toEqual({
node: {},
@@ -609,18 +609,15 @@ describe('useWorkflowHelpers', () => {
const jsonData = {
name: 'Test',
};
workflowsStore.getCurrentWorkflow.mockReturnValue({
connectionsByDestinationNode: {
Set: {
main: [
[
{ node: 'Start', index: 0, type: 'main' },
{ node: 'Set', index: 0, type: 'main' },
],
],
},
const connectionsBySourceNode: IConnections = {
Start: {
main: [[{ node: 'Set', index: 0, type: 'main' }]],
},
} as never);
Set: {
main: [[{ node: 'Start', index: 0, type: 'main' }]],
},
};
workflowsStore.workflowExecutionData = {
data: {
@@ -646,7 +643,13 @@ describe('useWorkflowHelpers', () => {
},
} as unknown as IExecutionResponse;
const result = executeData(parentNodes, currentNode, inputName, runIndex);
const result = executeData(
connectionsBySourceNode,
parentNodes,
currentNode,
inputName,
runIndex,
);
expect(result).toEqual({
node: {},
@@ -686,22 +689,20 @@ describe('useWorkflowHelpers', () => {
name: 'Test B',
};
workflowsStore.getCurrentWorkflow.mockReturnValue({
connectionsByDestinationNode: {
Set: {
main: [
[
{ node: 'Parent A', index: 0, type: 'main' },
{ node: 'Set', index: 0, type: 'main' },
],
[
{ node: 'Parent B', index: 0, type: 'main' },
{ node: 'Set', index: 0, type: 'main' },
],
],
},
const connectionsBySourceNode: IConnections = {
'Parent A': {
main: [[{ node: 'Set', type: 'main', index: 0 }]],
},
} as never);
'Parent B': {
main: [[{ node: 'Set', type: 'main', index: 1 }]],
},
Set: {
main: [
[{ node: 'Set', type: 'main', index: 0 }],
[{ node: 'Set', type: 'main', index: 1 }],
],
},
};
workflowsStore.workflowExecutionData = {
data: {
@@ -742,7 +743,13 @@ describe('useWorkflowHelpers', () => {
},
} as unknown as IExecutionResponse;
const result = executeData(parentNodes, currentNode, inputName, runIndex);
const result = executeData(
connectionsBySourceNode,
parentNodes,
currentNode,
inputName,
runIndex,
);
expect(result).toEqual({
node: {},
@@ -779,7 +786,7 @@ describe('useWorkflowHelpers', () => {
};
workflowsStore.shouldReplaceInputDataWithPinData = true;
const result = executeData(parentNodes, currentNode, inputName, runIndex);
const result = executeData({}, parentNodes, currentNode, inputName, runIndex);
expect(result.data).toEqual({ main: [[{ json: { key: 'value' } }]] });
expect(result.source).toEqual({ main: [{ previousNode: 'ParentNode' }] });
@@ -802,20 +809,23 @@ describe('useWorkflowHelpers', () => {
} as never,
],
};
workflowsStore.getCurrentWorkflow.mockReturnValue({
connectionsByDestinationNode: {
CurrentNode: {
main: [
[
{ node: 'ParentNode', index: 0, type: 'main' },
{ node: 'CurrentNode', index: 0, type: 'main' },
],
],
},
},
} as never);
const result = executeData(parentNodes, currentNode, inputName, runIndex);
const connectionsBySourceNode: IConnections = {
CurrentNode: {
main: [[{ node: 'CurrentNode', index: 0, type: 'main' }]],
},
ParentNode: {
main: [[{ node: 'CurrentNode', index: 0, type: 'main' }]],
},
};
const result = executeData(
connectionsBySourceNode,
parentNodes,
currentNode,
inputName,
runIndex,
);
expect(result.data).toEqual({ main: [[{ json: { key: 'valueFromRunData' } }]] });
expect(result.source).toEqual({
@@ -841,20 +851,24 @@ describe('useWorkflowHelpers', () => {
} as never,
],
};
workflowsStore.getCurrentWorkflow.mockReturnValue({
connectionsByDestinationNode: {
CurrentNode: {
main: [
[
{ node: 'ParentNode', index: 1, type: 'main' },
{ node: 'CurrentNode', index: 0, type: 'main' },
],
],
},
},
} as never);
const result = executeData(parentNodes, currentNode, inputName, runIndex, parentRunIndex);
const connectionsBySourceNode: IConnections = {
CurrentNode: {
main: [[{ node: 'CurrentNode', index: 0, type: 'main' }]],
},
ParentNode: {
main: [[], [{ node: 'CurrentNode', index: 1, type: 'main' }]],
},
};
const result = executeData(
connectionsBySourceNode,
parentNodes,
currentNode,
inputName,
runIndex,
parentRunIndex,
);
expect(result.data).toEqual({ main: [[{ json: { key: 'valueFromRunData' } }]] });
expect(result.source).toEqual({
@@ -874,7 +888,7 @@ describe('useWorkflowHelpers', () => {
workflowsStore.shouldReplaceInputDataWithPinData = false;
workflowsStore.getWorkflowRunData = null;
const result = executeData(parentNodes, currentNode, inputName, runIndex);
const result = executeData({}, parentNodes, currentNode, inputName, runIndex);
expect(result.data).toEqual({});
expect(result.source).toBeNull();

View File

@@ -29,6 +29,7 @@ import {
NodeHelpers,
WEBHOOK_NODE_TYPE,
} from 'n8n-workflow';
import * as workflowUtils from 'n8n-workflow/common';
import type {
ICredentialsResponse,
@@ -71,6 +72,7 @@ export type ResolveParameterOptions = {
additionalKeys?: IWorkflowDataProxyAdditionalKeys;
isForCredential?: boolean;
contextNodeName?: string;
connections?: IConnections;
};
export function resolveParameter<T = IDataObject>(
@@ -81,6 +83,7 @@ export function resolveParameter<T = IDataObject>(
return resolveParameterImpl(
parameter,
() => opts.workflow,
opts.connections,
opts.envVars,
opts.workflow.getNode(opts.nodeName),
opts.execution,
@@ -100,6 +103,7 @@ export function resolveParameter<T = IDataObject>(
return resolveParameterImpl(
parameter,
workflowsStore.getCurrentWorkflow,
workflowsStore.connectionsBySourceNode,
useEnvironmentsStore().variablesAsObject,
useNDVStore().activeNode,
workflowsStore.workflowExecutionData,
@@ -113,6 +117,7 @@ export function resolveParameter<T = IDataObject>(
function resolveParameterImpl<T = IDataObject>(
parameter: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[],
getContextWorkflow: () => Workflow,
connections: IConnections,
envVars: Record<string, string | boolean | number>,
ndvActiveNode: INodeUi | null,
executionData: IExecutionResponse | null,
@@ -200,11 +205,11 @@ function resolveParameterImpl<T = IDataObject>(
}
let _connectionInputData = connectionInputData(
connections,
parentNode,
contextNode!.name,
inputName,
runIndexParent,
getContextWorkflow,
shouldReplaceInputDataWithPinData,
pinData,
executionData?.data?.resultData.runData ?? null,
@@ -215,11 +220,11 @@ function resolveParameterImpl<T = IDataObject>(
// For Sub-Nodes connected to Trigger-Nodes use the data of the root-node
// (Gets for example used by the Memory connected to the Chat-Trigger-Node)
const _executeData = executeDataImpl(
connections,
[contextNode.name],
contextNode.name,
inputName,
0,
getContextWorkflow,
shouldReplaceInputDataWithPinData,
pinData,
executionData?.data?.resultData.runData ?? null,
@@ -265,11 +270,11 @@ function resolveParameterImpl<T = IDataObject>(
runIndexCurrent = workflowRunData[contextNode!.name].length - 1;
}
let _executeData = executeDataImpl(
connections,
parentNode,
contextNode!.name,
inputName,
runIndexCurrent,
getContextWorkflow,
shouldReplaceInputDataWithPinData,
pinData,
executionData?.data?.resultData.runData ?? null,
@@ -279,11 +284,11 @@ function resolveParameterImpl<T = IDataObject>(
if (!_executeData.source) {
// fallback to parent's run index for multi-output case
_executeData = executeDataImpl(
connections,
parentNode,
contextNode!.name,
inputName,
runIndexParent,
getContextWorkflow,
shouldReplaceInputDataWithPinData,
pinData,
executionData?.data?.resultData.runData ?? null,
@@ -310,6 +315,7 @@ export function resolveRequiredParameters(
currentParameter: INodeProperties,
parameters: INodeParameters,
opts: {
connections?: IConnections;
targetItem?: TargetItem;
inputNodeName?: string;
inputRunIndex?: number;
@@ -382,11 +388,11 @@ function getNodeTypes(): INodeTypes {
// TODO: move to separate file
// Returns connectionInputData to be able to execute an expression.
function connectionInputData(
connections: IConnections,
parentNode: string[],
currentNode: string,
inputName: string,
runIndex: number,
getContextWorkflow: () => Workflow,
shouldReplaceInputDataWithPinData: boolean,
pinData: IPinData | undefined,
workflowRunData: IRunData | null,
@@ -394,11 +400,11 @@ function connectionInputData(
): INodeExecutionData[] | null {
let connectionInputData: INodeExecutionData[] | null = null;
const _executeData = executeDataImpl(
connections,
parentNode,
currentNode,
inputName,
runIndex,
getContextWorkflow,
shouldReplaceInputDataWithPinData,
pinData,
workflowRunData,
@@ -431,6 +437,7 @@ function connectionInputData(
}
export function executeData(
connections: IConnections,
parentNodes: string[],
currentNode: string,
inputName: string,
@@ -440,11 +447,11 @@ export function executeData(
const workflowsStore = useWorkflowsStore();
return executeDataImpl(
connections,
parentNodes,
currentNode,
inputName,
runIndex,
workflowsStore.getCurrentWorkflow,
workflowsStore.shouldReplaceInputDataWithPinData,
workflowsStore.pinnedWorkflowData,
workflowsStore.getWorkflowRunData,
@@ -454,16 +461,18 @@ export function executeData(
// TODO: move to separate file
function executeDataImpl(
connections: IConnections,
parentNodes: string[],
currentNode: string,
inputName: string,
runIndex: number,
getContextWorkflow: () => Workflow,
shouldReplaceInputDataWithPinData: boolean,
pinData: IPinData | undefined,
workflowRunData: IRunData | null,
parentRunIndex?: number,
): IExecuteData {
const connectionsByDestinationNode = workflowUtils.mapConnectionsByDestination(connections);
const executeData = {
node: {},
data: {},
@@ -507,15 +516,12 @@ function executeDataImpl(
[inputName]: workflowRunData[currentNode][runIndex].source,
};
} else {
const workflow = getContextWorkflow();
let previousNodeOutput: number | undefined;
// As the node can be connected through either of the outputs find the correct one
// and set it to make pairedItem work on not executed nodes
if (workflow.connectionsByDestinationNode[currentNode]?.main) {
mainConnections: for (const mainConnections of workflow.connectionsByDestinationNode[
currentNode
].main) {
if (connectionsByDestinationNode[currentNode]?.main) {
mainConnections: for (const mainConnections of connectionsByDestinationNode[currentNode]
.main) {
for (const connection of mainConnections ?? []) {
if (
connection.type === NodeConnectionTypes.Main &&

View File

@@ -149,9 +149,8 @@ export const useNDVStore = defineStore(STORES.NDV, () => {
const ndvNodeInputNumber = computed(() => {
const returnData: { [nodeName: string]: number[] } = {};
const workflow = workflowsStore.getCurrentWorkflow();
const activeNodeConections = (
workflow.connectionsByDestinationNode[activeNode.value?.name || ''] ?? {}
workflowsStore.connectionsByDestinationNode[activeNode.value?.name || ''] ?? {}
).main;
if (!activeNodeConections || activeNodeConections.length < 2) return returnData;

View File

@@ -66,6 +66,7 @@ import {
Workflow,
TelemetryHelpers,
} from 'n8n-workflow';
import * as workflowUtils from 'n8n-workflow/common';
import findLast from 'lodash/findLast';
import isEqual from 'lodash/isEqual';
import pick from 'lodash/pick';
@@ -294,10 +295,17 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
const getPastChatMessages = computed(() => Array.from(new Set(chatMessages.value)));
/**
* This section contains functions migrated from the workflow class
*/
const connectionsBySourceNode = computed(() => workflow.value.connections);
const connectionsByDestinationNode = computed(() =>
Workflow.getConnectionsByDestination(workflow.value.connections),
workflowUtils.mapConnectionsByDestination(workflow.value.connections),
);
// End section
const selectableTriggerNodes = computed(() =>
workflowTriggerNodes.value.filter((node) => !node.disabled && !isChatNode(node)),
);
@@ -384,7 +392,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
}
function getNodeByName(nodeName: string): INodeUi | null {
return nodesByName.value[nodeName] || null;
return workflowUtils.getNodeByName(nodesByName.value, nodeName);
}
function getNodeById(nodeId: string): INodeUi | undefined {
@@ -1955,6 +1963,8 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
getWorkflowResultDataByNodeName,
allConnections,
allNodes,
connectionsBySourceNode,
connectionsByDestinationNode,
isWaitingExecution,
isWorkflowRunning,
canvasNames,

View File

@@ -1,5 +1,5 @@
import type { Basic, IExecutionResponse } from '@/Interface';
import type { IWorkflowDataProxyAdditionalKeys, Workflow } from 'n8n-workflow';
import type { IConnections, IWorkflowDataProxyAdditionalKeys, Workflow } from 'n8n-workflow';
type Range = { from: number; to: number };
@@ -40,6 +40,7 @@ export interface ExpressionLocalResolveContext {
envVars: Record<string, Basic>;
additionalKeys: IWorkflowDataProxyAdditionalKeys;
workflow: Workflow;
connections: IConnections;
execution: IExecutionResponse | null;
nodeName: string;
/**

View File

@@ -23,6 +23,8 @@ import { SET_NODE_TYPE, STICKY_NODE_TYPE } from '@/constants';
import { createTestNode } from '@/__tests__/mocks';
import type { GraphNode } from '@vue-flow/core';
import { v4 as uuid } from 'uuid';
import { createTestingPinia } from '@pinia/testing';
import { setActivePinia } from 'pinia';
describe('getGenericHints', () => {
let mockWorkflowNode: MockProxy<INode>;
@@ -34,6 +36,9 @@ describe('getGenericHints', () => {
let hasNodeRun: boolean;
beforeEach(() => {
const pinia = createTestingPinia({});
setActivePinia(pinia);
mockWorkflowNode = mock<INode>();
mockNode = mock<INodeUi>({ type: 'test' });
mockNodeType = mock<INodeTypeDescription>();
@@ -55,8 +60,9 @@ describe('getGenericHints', () => {
nodeType: mockNodeType,
nodeOutputData: mockNodeOutputData,
hasMultipleInputItems,
workflow: mockWorkflow,
hasNodeRun,
nodes: [],
connections: {},
});
expect(hints).toEqual([
@@ -80,8 +86,9 @@ describe('getGenericHints', () => {
nodeType: mockNodeType,
nodeOutputData: mockNodeOutputData,
hasMultipleInputItems,
workflow: mockWorkflow,
hasNodeRun,
nodes: [],
connections: {},
});
expect(hints).toEqual([
@@ -118,8 +125,9 @@ describe('getGenericHints', () => {
nodeType: mockNodeType,
nodeOutputData: mockNodeOutputData,
hasMultipleInputItems,
workflow: mockWorkflow,
hasNodeRun,
nodes: [],
connections: {},
});
expect(hints).toEqual([
@@ -142,8 +150,9 @@ describe('getGenericHints', () => {
nodeType: mockNodeType,
nodeOutputData: mockNodeOutputData,
hasMultipleInputItems,
workflow: mockWorkflow,
hasNodeRun,
nodes: [],
connections: {},
});
expect(hints).toEqual([
@@ -166,8 +175,9 @@ describe('getGenericHints', () => {
nodeType: mockNodeType,
nodeOutputData: mockNodeOutputData,
hasMultipleInputItems,
workflow: mockWorkflow,
hasNodeRun,
nodes: [],
connections: {},
});
expect(hints).toEqual([
@@ -191,8 +201,9 @@ describe('getGenericHints', () => {
nodeType: mockNodeType,
nodeOutputData: mockNodeOutputData,
hasMultipleInputItems,
workflow: mockWorkflow,
hasNodeRun,
nodes: [],
connections: {},
});
expect(hints).toEqual([

View File

@@ -11,11 +11,11 @@ import {
import type { INodeUi, XYPosition } from '@/Interface';
import type {
AssignmentCollectionValue,
IConnections,
INode,
INodeExecutionData,
INodeTypeDescription,
NodeHint,
Workflow,
} from 'n8n-workflow';
import { NodeHelpers, SEND_AND_WAIT_OPERATION } from 'n8n-workflow';
import type { RouteLocation } from 'vue-router';
@@ -27,6 +27,7 @@ import {
type Rect,
type ViewportTransform,
} from '@vue-flow/core';
import * as workflowUtils from 'n8n-workflow/common';
/*
* Canvas constants and functions
@@ -371,7 +372,8 @@ export function getGenericHints({
nodeType,
nodeOutputData,
hasMultipleInputItems,
workflow,
nodes,
connections,
hasNodeRun,
}: {
workflowNode: INode;
@@ -379,7 +381,8 @@ export function getGenericHints({
nodeType: INodeTypeDescription;
nodeOutputData: INodeExecutionData[];
hasMultipleInputItems: boolean;
workflow: Workflow;
nodes: INode[];
connections: IConnections;
hasNodeRun: boolean;
}) {
const nodeHints: NodeHint[] = [];
@@ -417,7 +420,7 @@ export function getGenericHints({
hasMultipleInputItems &&
LIST_LIKE_NODE_OPERATIONS.includes((workflowNode.parameters.operation as string) || '')
) {
const executeOnce = workflow.getNode(node.name)?.executeOnce;
const executeOnce = workflowUtils.getNodeByName(nodes, node.name)?.executeOnce;
if (!executeOnce) {
nodeHints.push({
message:
@@ -429,7 +432,7 @@ export function getGenericHints({
// add sendAndWait hint
if (hasMultipleInputItems && workflowNode.parameters.operation === SEND_AND_WAIT_OPERATION) {
const executeOnce = workflow.getNode(node.name)?.executeOnce;
const executeOnce = workflowUtils.getNodeByName(nodes, node.name)?.executeOnce;
if (!executeOnce) {
nodeHints.push({
message: 'This action will run only once, for the first input item',
@@ -470,9 +473,8 @@ export function getGenericHints({
// Split In Batches setup hints
if (node.type === SPLIT_IN_BATCHES_NODE_TYPE) {
const { connectionsBySourceNode } = workflow;
const firstNodesInLoop = connectionsBySourceNode[node.name]?.main[1] || [];
const firstNodesInLoop =
workflowUtils.mapConnectionsByDestination(connections)[node.name]?.main[1] || [];
if (!firstNodesInLoop.length) {
nodeHints.push({
@@ -482,7 +484,7 @@ export function getGenericHints({
});
} else {
for (const nodeInConnection of firstNodesInLoop || []) {
const nodeChilds = workflow.getChildNodes(nodeInConnection.node) || [];
const nodeChilds = workflowUtils.getChildNodes(connections, nodeInConnection.node) || [];
if (!nodeChilds.includes(node.name)) {
nodeHints.push({
message: