mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-31 16:57:08 +02:00
refactor(editor): Migrate updateNodeAtIndex and usages to workflowState (#20586)
This commit is contained in:
parent
f3f925605e
commit
18a871efe8
|
|
@ -60,6 +60,7 @@ import {
|
|||
N8nText,
|
||||
type IMenuItem,
|
||||
} from '@n8n/design-system';
|
||||
import { injectWorkflowState } from '@/composables/useWorkflowState';
|
||||
|
||||
type Props = {
|
||||
modalName: string;
|
||||
|
|
@ -74,6 +75,7 @@ const ndvStore = useNDVStore();
|
|||
const settingsStore = useSettingsStore();
|
||||
const uiStore = useUIStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const workflowState = injectWorkflowState();
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
|
||||
|
|
@ -1040,7 +1042,7 @@ async function onAuthTypeChanged(type: string): Promise<void> {
|
|||
uiStore.activeCredentialType = credentialsForType.name;
|
||||
resetCredentialData();
|
||||
// Update current node auth type so credentials dropdown can be displayed properly
|
||||
updateNodeAuthType(ndvStore.activeNode, type);
|
||||
updateNodeAuthType(workflowState, ndvStore.activeNode, type);
|
||||
// Also update credential name but only if the default name is still used
|
||||
if (hasUnsavedChanges.value && !hasUserSpecifiedName.value) {
|
||||
const newDefaultName = await credentialsStore.getNewCredentialName({
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import {
|
|||
N8nText,
|
||||
N8nTooltip,
|
||||
} from '@n8n/design-system';
|
||||
import { injectWorkflowState } from '@/composables/useWorkflowState';
|
||||
type Props = {
|
||||
node: INodeUi;
|
||||
overrideCredType?: NodeParameterValueType;
|
||||
|
|
@ -69,6 +70,7 @@ const nodeTypesStore = useNodeTypesStore();
|
|||
const ndvStore = useNDVStore();
|
||||
const uiStore = useUIStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const workflowState = injectWorkflowState();
|
||||
|
||||
const nodeHelpers = useNodeHelpers();
|
||||
const toast = useToast();
|
||||
|
|
@ -348,7 +350,7 @@ function onCredentialSelected(
|
|||
);
|
||||
const authOption = getAuthTypeForNodeCredential(nodeType.value, nodeCredentialDescription);
|
||||
if (authOption) {
|
||||
updateNodeAuthType(props.node, authOption.value);
|
||||
updateNodeAuthType(workflowState, props.node, authOption.value);
|
||||
const parameterData = {
|
||||
name: `parameters.${mainNodeAuthField.value.name}`,
|
||||
value: authOption.value,
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ const valueChanged = (parameterData: IUpdateInformation) => {
|
|||
value: newValue,
|
||||
};
|
||||
|
||||
workflowsStore.setNodeValue(updateInformation);
|
||||
workflowState.setNodeValue(updateInformation);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -468,7 +468,7 @@ const onNodeExecute = () => {
|
|||
|
||||
const credentialSelected = (updateInformation: INodeUpdatePropertiesInformation) => {
|
||||
// Update the values on the node
|
||||
workflowsStore.updateNodeProperties(updateInformation);
|
||||
workflowState.updateNodeProperties(updateInformation);
|
||||
|
||||
const node = workflowsStore.getNodeByName(updateInformation.name);
|
||||
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ import { getParameterDisplayableOptions } from '@/utils/nodes/nodeTransforms';
|
|||
|
||||
import { ElColorPicker, ElDatePicker, ElDialog, ElSwitch } from 'element-plus';
|
||||
import { N8nIcon, N8nInput, N8nInputNumber, N8nOption, N8nSelect } from '@n8n/design-system';
|
||||
import { injectWorkflowState } from '@/composables/useWorkflowState';
|
||||
type Picker = { $emit: (arg0: string, arg1: Date) => void };
|
||||
|
||||
type Props = {
|
||||
|
|
@ -157,6 +158,7 @@ const telemetry = useTelemetry();
|
|||
const credentialsStore = useCredentialsStore();
|
||||
const ndvStore = useNDVStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const workflowState = injectWorkflowState();
|
||||
const settingsStore = useSettingsStore();
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
const uiStore = useUIStore();
|
||||
|
|
@ -645,7 +647,7 @@ function isRemoteParameterOption(option: INodePropertyOptions) {
|
|||
|
||||
function credentialSelected(updateInformation: INodeUpdatePropertiesInformation) {
|
||||
// Update the values on the node
|
||||
workflowsStore.updateNodeProperties(updateInformation);
|
||||
workflowState.updateNodeProperties(updateInformation);
|
||||
|
||||
const updateNode = workflowsStore.getNodeByName(updateInformation.name);
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ import {
|
|||
N8nText,
|
||||
N8nTooltip,
|
||||
} from '@n8n/design-system';
|
||||
import { injectWorkflowState } from '@/composables/useWorkflowState';
|
||||
const LazyRunDataTable = defineAsyncComponent(
|
||||
async () => await import('@/components/RunDataTable.vue'),
|
||||
);
|
||||
|
|
@ -231,6 +232,7 @@ const dataContainerRef = ref<HTMLDivElement>();
|
|||
const nodeTypesStore = useNodeTypesStore();
|
||||
const ndvStore = useNDVStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const workflowState = injectWorkflowState();
|
||||
const sourceControlStore = useSourceControlStore();
|
||||
const rootStore = useRootStore();
|
||||
const schemaPreviewStore = useSchemaPreviewStore();
|
||||
|
|
@ -1344,7 +1346,7 @@ function enableNode() {
|
|||
},
|
||||
};
|
||||
|
||||
workflowsStore.updateNodeProperties(updateInformation);
|
||||
workflowState.updateNodeProperties(updateInformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -639,7 +639,7 @@ describe('useCanvasOperations', () => {
|
|||
{ id: 'node1', position: { x: 96, y: 96 } },
|
||||
{ id: 'node2', position: { x: 208, y: 208 } },
|
||||
];
|
||||
const setNodePositionByIdSpy = vi.spyOn(workflowsStore, 'setNodePositionById');
|
||||
const setNodePositionByIdSpy = vi.spyOn(workflowState, 'setNodePositionById');
|
||||
workflowsStore.getNodeById
|
||||
.mockReturnValueOnce(
|
||||
createTestNode({
|
||||
|
|
@ -713,7 +713,7 @@ describe('useCanvasOperations', () => {
|
|||
boundingBox: { height: 96, width: 96, x: 0, y: 0 },
|
||||
},
|
||||
};
|
||||
const setNodePositionByIdSpy = vi.spyOn(workflowsStore, 'setNodePositionById');
|
||||
const setNodePositionByIdSpy = vi.spyOn(workflowState, 'setNodePositionById');
|
||||
workflowsStore.getNodeById
|
||||
.mockReturnValueOnce(
|
||||
createTestNode({
|
||||
|
|
@ -836,13 +836,14 @@ describe('useCanvasOperations', () => {
|
|||
position: [0, 0],
|
||||
name: 'Node 1',
|
||||
});
|
||||
const setNodePositionByIdSpy = vi.spyOn(workflowState, 'setNodePositionById');
|
||||
|
||||
workflowsStore.getNodeById.mockReturnValueOnce(node);
|
||||
|
||||
const { updateNodePosition } = useCanvasOperations();
|
||||
updateNodePosition(id, position);
|
||||
|
||||
expect(workflowsStore.setNodePositionById).toHaveBeenCalledWith(id, [position.x, position.y]);
|
||||
expect(setNodePositionByIdSpy).toHaveBeenCalledWith(id, [position.x, position.y]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -958,7 +959,7 @@ describe('useCanvasOperations', () => {
|
|||
mockNode({ id: 'c', name: 'Node C', type: nodeTypeName, position: [96, 256] }),
|
||||
];
|
||||
|
||||
const setNodePositionByIdSpy = vi.spyOn(workflowsStore, 'setNodePositionById');
|
||||
const setNodePositionByIdSpy = vi.spyOn(workflowState, 'setNodePositionById');
|
||||
workflowsStore.getNodeByName.mockReturnValueOnce(nodes[1]).mockReturnValueOnce(nodes[2]);
|
||||
workflowsStore.getNodeById.mockReturnValueOnce(nodes[1]).mockReturnValueOnce(nodes[2]);
|
||||
|
||||
|
|
@ -1478,6 +1479,7 @@ describe('useCanvasOperations', () => {
|
|||
createTestNode({ id: '2', name: 'B' }),
|
||||
];
|
||||
workflowsStore.getNodesByIds.mockReturnValue(nodes);
|
||||
const updateNodePropertiesSpy = vi.spyOn(workflowState, 'updateNodeProperties');
|
||||
|
||||
const { toggleNodesDisabled } = useCanvasOperations();
|
||||
toggleNodesDisabled([nodes[0].id, nodes[1].id], {
|
||||
|
|
@ -1485,7 +1487,7 @@ describe('useCanvasOperations', () => {
|
|||
trackBulk: true,
|
||||
});
|
||||
|
||||
expect(workflowsStore.updateNodeProperties).toHaveBeenCalledWith({
|
||||
expect(updateNodePropertiesSpy).toHaveBeenCalledWith({
|
||||
name: nodes[0].name,
|
||||
properties: {
|
||||
disabled: true,
|
||||
|
|
@ -1500,7 +1502,7 @@ describe('useCanvasOperations', () => {
|
|||
const nodeName = 'testNode';
|
||||
const node = createTestNode({ name: nodeName });
|
||||
workflowsStore.getNodeByName.mockReturnValue(node);
|
||||
const updateNodePropertiesSpy = vi.spyOn(workflowsStore, 'updateNodeProperties');
|
||||
const updateNodePropertiesSpy = vi.spyOn(workflowState, 'updateNodeProperties');
|
||||
|
||||
const { revertToggleNodeDisabled } = useCanvasOperations();
|
||||
revertToggleNodeDisabled(nodeName);
|
||||
|
|
@ -3783,8 +3785,6 @@ describe('useCanvasOperations', () => {
|
|||
describe('replaceNodeParameters', () => {
|
||||
it('should replace node parameters and track history', () => {
|
||||
const workflowsStore = mockedStore(useWorkflowsStore);
|
||||
workflowState = useWorkflowState();
|
||||
vi.mocked(injectWorkflowState).mockReturnValue(workflowState);
|
||||
const historyStore = mockedStore(useHistoryStore);
|
||||
|
||||
const nodeId = 'node1';
|
||||
|
|
@ -3798,14 +3798,13 @@ describe('useCanvasOperations', () => {
|
|||
parameters: currentParameters,
|
||||
});
|
||||
|
||||
workflowsStore.workflow.nodes = [node];
|
||||
workflowsStore.getNodeById.mockReturnValue(node);
|
||||
const setNodeParameters = vi.spyOn(workflowState, 'setNodeParameters');
|
||||
workflowState.setNodeParameters = vi.fn();
|
||||
|
||||
const { replaceNodeParameters } = useCanvasOperations();
|
||||
replaceNodeParameters(nodeId, currentParameters, newParameters, { trackHistory: true });
|
||||
|
||||
expect(setNodeParameters).toHaveBeenCalledWith({
|
||||
expect(workflowState.setNodeParameters).toHaveBeenCalledWith({
|
||||
name: node.name,
|
||||
value: newParameters,
|
||||
});
|
||||
|
|
@ -3834,14 +3833,13 @@ describe('useCanvasOperations', () => {
|
|||
parameters: currentParameters,
|
||||
});
|
||||
|
||||
workflowsStore.workflow.nodes = [node];
|
||||
workflowsStore.getNodeById.mockReturnValue(node);
|
||||
const setNodeParameters = vi.spyOn(workflowState, 'setNodeParameters');
|
||||
workflowState.setNodeParameters = vi.fn();
|
||||
|
||||
const { replaceNodeParameters } = useCanvasOperations();
|
||||
replaceNodeParameters(nodeId, currentParameters, newParameters, { trackHistory: false });
|
||||
|
||||
expect(setNodeParameters).toHaveBeenCalledWith({
|
||||
expect(workflowState.setNodeParameters).toHaveBeenCalledWith({
|
||||
name: node.name,
|
||||
value: newParameters,
|
||||
});
|
||||
|
|
@ -3855,14 +3853,13 @@ describe('useCanvasOperations', () => {
|
|||
const currentParameters = { param1: 'value1' };
|
||||
const newParameters = { param1: 'value2' };
|
||||
|
||||
workflowsStore.workflow.nodes = [];
|
||||
workflowsStore.getNodeById.mockReturnValue(undefined);
|
||||
const setNodeParameters = vi.spyOn(workflowState, 'setNodeParameters');
|
||||
workflowState.setNodeParameters = vi.fn();
|
||||
|
||||
const { replaceNodeParameters } = useCanvasOperations();
|
||||
replaceNodeParameters(nodeId, currentParameters, newParameters);
|
||||
|
||||
expect(setNodeParameters).not.toHaveBeenCalled();
|
||||
expect(workflowState.setNodeParameters).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle bulk tracking when replacing parameters for multiple nodes', () => {
|
||||
|
|
@ -3889,9 +3886,8 @@ describe('useCanvasOperations', () => {
|
|||
parameters: currentParameters2,
|
||||
});
|
||||
|
||||
workflowsStore.workflow.nodes = [node1, node2];
|
||||
workflowState.setNodeParameters = vi.fn();
|
||||
workflowsStore.getNodeById.mockReturnValueOnce(node1).mockReturnValueOnce(node2);
|
||||
const setNodeParameters = vi.spyOn(workflowState, 'setNodeParameters');
|
||||
|
||||
const { replaceNodeParameters } = useCanvasOperations();
|
||||
replaceNodeParameters(nodeId1, currentParameters1, newParameters1, {
|
||||
|
|
@ -3905,7 +3901,7 @@ describe('useCanvasOperations', () => {
|
|||
|
||||
expect(historyStore.startRecordingUndo).not.toHaveBeenCalled();
|
||||
expect(historyStore.stopRecordingUndo).not.toHaveBeenCalled();
|
||||
expect(setNodeParameters).toHaveBeenCalledTimes(2);
|
||||
expect(workflowState.setNodeParameters).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should revert replaced node parameters', async () => {
|
||||
|
|
@ -3922,14 +3918,13 @@ describe('useCanvasOperations', () => {
|
|||
parameters: newParameters,
|
||||
});
|
||||
|
||||
workflowsStore.workflow.nodes = [node];
|
||||
workflowState.setNodeParameters = vi.fn();
|
||||
workflowsStore.getNodeById.mockReturnValue(node);
|
||||
const setNodeParameters = vi.spyOn(workflowState, 'setNodeParameters');
|
||||
|
||||
const { revertReplaceNodeParameters } = useCanvasOperations();
|
||||
await revertReplaceNodeParameters(nodeId, currentParameters, newParameters);
|
||||
|
||||
expect(setNodeParameters).toHaveBeenCalledWith({
|
||||
expect(workflowState.setNodeParameters).toHaveBeenCalledWith({
|
||||
name: node.name,
|
||||
value: currentParameters,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ export function useCanvasOperations() {
|
|||
const oldPosition: XYPosition = [...node.position];
|
||||
const newPosition: XYPosition = [position.x, position.y];
|
||||
|
||||
workflowsStore.setNodePositionById(id, newPosition);
|
||||
workflowState.setNodePositionById(id, newPosition);
|
||||
|
||||
if (trackHistory) {
|
||||
historyStore.pushCommandToUndo(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,15 @@ import { createTestingPinia } from '@pinia/testing';
|
|||
import { NodeConnectionTypes, type IConnections, type IRunData } from 'n8n-workflow';
|
||||
import { defineComponent } from 'vue';
|
||||
import { createRouter, createWebHistory, type RouteLocationNormalizedLoaded } from 'vue-router';
|
||||
import { useWorkflowState } from './useWorkflowState';
|
||||
import { useWorkflowState, injectWorkflowState, type WorkflowState } from './useWorkflowState';
|
||||
|
||||
vi.mock('@/composables/useWorkflowState', async () => {
|
||||
const actual = await vi.importActual('@/composables/useWorkflowState');
|
||||
return {
|
||||
...actual,
|
||||
injectWorkflowState: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe(useNodeDirtiness, () => {
|
||||
let nodeTypeStore: ReturnType<typeof useNodeTypesStore>;
|
||||
|
|
@ -21,6 +29,7 @@ describe(useNodeDirtiness, () => {
|
|||
let historyHelper: ReturnType<typeof useHistoryHelper>;
|
||||
let canvasOperations: ReturnType<typeof useCanvasOperations>;
|
||||
let uiStore: ReturnType<typeof useUIStore>;
|
||||
let workflowState: WorkflowState;
|
||||
|
||||
const NODE_RUN_AT = new Date('2025-01-01T00:00:01');
|
||||
const WORKFLOW_UPDATED_AT = new Date('2025-01-01T00:00:10');
|
||||
|
|
@ -33,6 +42,9 @@ describe(useNodeDirtiness, () => {
|
|||
nodeTypeStore = useNodeTypesStore();
|
||||
workflowsStore = useWorkflowsStore();
|
||||
historyHelper = useHistoryHelper({} as RouteLocationNormalizedLoaded);
|
||||
workflowState = useWorkflowState();
|
||||
vi.mocked(injectWorkflowState).mockReturnValue(workflowState);
|
||||
|
||||
canvasOperations = useCanvasOperations();
|
||||
uiStore = useUIStore();
|
||||
|
||||
|
|
@ -167,7 +179,7 @@ describe(useNodeDirtiness, () => {
|
|||
it('should not update dirtiness when the notes field is updated', () => {
|
||||
setupTestWorkflow('a🚨✅ -> b✅ -> c✅');
|
||||
|
||||
workflowsStore.setNodeValue({ key: 'notes', name: 'b', value: 'test' });
|
||||
workflowState.setNodeValue({ key: 'notes', name: 'b', value: 'test' });
|
||||
|
||||
expect(useNodeDirtiness().dirtinessByName.value).toEqual({});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import { useTelemetry } from './useTelemetry';
|
|||
import { hasPermission } from '@/utils/rbac/permissions';
|
||||
import { useCanvasStore } from '@/stores/canvas.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { injectWorkflowState } from './useWorkflowState';
|
||||
|
||||
declare namespace HttpRequestNode {
|
||||
namespace V2 {
|
||||
|
|
@ -67,6 +68,7 @@ export function useNodeHelpers() {
|
|||
const historyStore = useHistoryStore();
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const workflowState = injectWorkflowState();
|
||||
const settingsStore = useSettingsStore();
|
||||
const i18n = useI18n();
|
||||
const canvasStore = useCanvasStore();
|
||||
|
|
@ -282,7 +284,7 @@ export function useNodeHelpers() {
|
|||
|
||||
const nodeInputIssues = getNodeInputIssues(workflowObject.value, node, nodeType);
|
||||
|
||||
workflowsStore.setNodeIssue({
|
||||
workflowState.setNodeIssue({
|
||||
node: node.name,
|
||||
type: 'input',
|
||||
value: nodeInputIssues?.input ? nodeInputIssues.input : null,
|
||||
|
|
@ -301,7 +303,7 @@ export function useNodeHelpers() {
|
|||
const nodes = workflowsStore.allNodes;
|
||||
|
||||
for (const node of nodes) {
|
||||
workflowsStore.setNodeIssue({
|
||||
workflowState.setNodeIssue({
|
||||
node: node.name,
|
||||
type: 'execution',
|
||||
value: hasNodeExecutionIssues(node) ? true : null,
|
||||
|
|
@ -333,7 +335,7 @@ export function useNodeHelpers() {
|
|||
newIssues = fullNodeIssues.credentials!;
|
||||
}
|
||||
|
||||
workflowsStore.setNodeIssue({
|
||||
workflowState.setNodeIssue({
|
||||
node: node.name,
|
||||
type: 'credentials',
|
||||
value: newIssues,
|
||||
|
|
@ -368,7 +370,7 @@ export function useNodeHelpers() {
|
|||
newIssues = fullNodeIssues.parameters!;
|
||||
}
|
||||
|
||||
workflowsStore.setNodeIssue({
|
||||
workflowState.setNodeIssue({
|
||||
node: node.name,
|
||||
type: 'parameters',
|
||||
value: newIssues,
|
||||
|
|
@ -599,7 +601,7 @@ export function useNodeHelpers() {
|
|||
for (const node of nodes) {
|
||||
issues = getNodeCredentialIssues(node);
|
||||
|
||||
workflowsStore.setNodeIssue({
|
||||
workflowState.setNodeIssue({
|
||||
node: node.name,
|
||||
type: 'credentials',
|
||||
value: issues?.credentials ?? null,
|
||||
|
|
@ -730,7 +732,7 @@ export function useNodeHelpers() {
|
|||
workflow_id: workflowsStore.workflowId,
|
||||
});
|
||||
|
||||
workflowsStore.updateNodeProperties(updateInformation);
|
||||
workflowState.updateNodeProperties(updateInformation);
|
||||
workflowsStore.clearNodeExecutionData(node.name);
|
||||
updateNodeParameterIssues(node);
|
||||
updateNodeCredentialIssues(node);
|
||||
|
|
|
|||
|
|
@ -399,7 +399,7 @@ export function useWorkflowSaving({ router }: { router: ReturnType<typeof useRou
|
|||
value: changedNodes[nodeName],
|
||||
name: nodeName,
|
||||
} as IUpdateInformation;
|
||||
workflowsStore.setNodeValue(changes);
|
||||
workflowState.setNodeValue(changes);
|
||||
});
|
||||
|
||||
const createdTags = (workflowData.tags || []) as ITag[];
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
createTestTaskData,
|
||||
createTestWorkflowExecutionResponse,
|
||||
} from '@/__tests__/mocks';
|
||||
import type { IWorkflowDb } from '@/Interface';
|
||||
|
||||
describe('useWorkflowState', () => {
|
||||
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
|
||||
|
|
@ -122,4 +123,96 @@ describe('useWorkflowState', () => {
|
|||
expect(workflowsStore.getParametersLastUpdate('a')).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
describe('setNodeValue()', () => {
|
||||
it('should update a node', () => {
|
||||
const nodeName = 'Edit Fields';
|
||||
workflowsStore.addNode({
|
||||
parameters: {},
|
||||
id: '554c7ff4-7ee2-407c-8931-e34234c5056a',
|
||||
name: nodeName,
|
||||
type: 'n8n-nodes-base.set',
|
||||
position: [680, 180],
|
||||
typeVersion: 3.4,
|
||||
});
|
||||
|
||||
expect(workflowsStore.nodeMetadata[nodeName].parametersLastUpdatedAt).toBe(undefined);
|
||||
|
||||
workflowState.setNodeValue({ name: 'Edit Fields', key: 'executeOnce', value: true });
|
||||
|
||||
expect(workflowsStore.workflow.nodes[0].executeOnce).toBe(true);
|
||||
expect(workflowsStore.nodeMetadata[nodeName].parametersLastUpdatedAt).toEqual(
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setNodePositionById', () => {
|
||||
it('should NOT update parametersLastUpdatedAt', () => {
|
||||
const nodeName = 'Edit Fields';
|
||||
const nodeId = '554c7ff4-7ee2-407c-8931-e34234c5056a';
|
||||
workflowsStore.addNode({
|
||||
parameters: {},
|
||||
id: nodeId,
|
||||
name: nodeName,
|
||||
type: 'n8n-nodes-base.set',
|
||||
position: [680, 180],
|
||||
typeVersion: 3.4,
|
||||
});
|
||||
|
||||
expect(workflowsStore.nodeMetadata[nodeName].parametersLastUpdatedAt).toBe(undefined);
|
||||
|
||||
workflowState.setNodePositionById(nodeId, [0, 0]);
|
||||
|
||||
expect(workflowsStore.workflow.nodes[0].position).toStrictEqual([0, 0]);
|
||||
expect(workflowsStore.nodeMetadata[nodeName].parametersLastUpdatedAt).toBe(undefined);
|
||||
});
|
||||
});
|
||||
describe('updateNodeAtIndex', () => {
|
||||
it.each([
|
||||
{
|
||||
description: 'should update node at given index with provided data',
|
||||
nodeIndex: 0,
|
||||
nodeData: { name: 'Updated Node' },
|
||||
initialNodes: [{ name: 'Original Node' }],
|
||||
expectedNodes: [{ name: 'Updated Node' }],
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
description: 'should not update node if index is invalid',
|
||||
nodeIndex: -1,
|
||||
nodeData: { name: 'Updated Node' },
|
||||
initialNodes: [{ name: 'Original Node' }],
|
||||
expectedNodes: [{ name: 'Original Node' }],
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
description: 'should return false if node data is unchanged',
|
||||
nodeIndex: 0,
|
||||
nodeData: { name: 'Original Node' },
|
||||
initialNodes: [{ name: 'Original Node' }],
|
||||
expectedNodes: [{ name: 'Original Node' }],
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
description: 'should update multiple properties of a node',
|
||||
nodeIndex: 0,
|
||||
nodeData: { name: 'Updated Node', type: 'newType' },
|
||||
initialNodes: [{ name: 'Original Node', type: 'oldType' }],
|
||||
expectedNodes: [{ name: 'Updated Node', type: 'newType' }],
|
||||
expectedResult: true,
|
||||
},
|
||||
])('$description', ({ nodeIndex, nodeData, initialNodes, expectedNodes, expectedResult }) => {
|
||||
workflowsStore.workflow.nodes = initialNodes as unknown as IWorkflowDb['nodes'];
|
||||
|
||||
const result = workflowState.updateNodeAtIndex(nodeIndex, nodeData);
|
||||
|
||||
expect(result).toBe(expectedResult);
|
||||
expect(workflowsStore.workflow.nodes).toEqual(expectedNodes);
|
||||
});
|
||||
|
||||
it('should throw error if out of bounds', () => {
|
||||
workflowsStore.workflow.nodes = [];
|
||||
expect(() => workflowState.updateNodeAtIndex(0, { name: 'Updated Node' })).toThrowError();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,12 +8,15 @@ import type {
|
|||
IExecutionsStopData,
|
||||
INewWorkflowData,
|
||||
INodeUi,
|
||||
INodeUpdatePropertiesInformation,
|
||||
IUpdateInformation,
|
||||
} from '@/Interface';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { getPairedItemsMapping } from '@/utils/pairedItemUtils';
|
||||
import {
|
||||
type INodeIssueData,
|
||||
type INodeIssueObjectProperty,
|
||||
NodeHelpers,
|
||||
type IDataObject,
|
||||
type INodeParameters,
|
||||
|
|
@ -31,6 +34,15 @@ import { useWorkflowStateStore } from '@/stores/workflowState.store';
|
|||
import { isObject } from '@/utils/objectUtils';
|
||||
import findLast from 'lodash/findLast';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import pick from 'lodash/pick';
|
||||
import { createEventBus } from '@n8n/utils/event-bus';
|
||||
|
||||
export type WorkflowStateBusEvents = {
|
||||
updateNodeProperties: [WorkflowState, INodeUpdatePropertiesInformation];
|
||||
};
|
||||
|
||||
export const workflowStateEventBus = createEventBus<WorkflowStateBusEvents>();
|
||||
|
||||
export function useWorkflowState() {
|
||||
const ws = useWorkflowsStore();
|
||||
|
|
@ -224,6 +236,26 @@ export function useWorkflowState() {
|
|||
// Node modification
|
||||
////
|
||||
|
||||
/**
|
||||
* @returns `true` if the object was changed
|
||||
*/
|
||||
function updateNodeAtIndex(nodeIndex: number, nodeData: Partial<INodeUi>): boolean {
|
||||
if (nodeIndex !== -1) {
|
||||
const node = ws.workflow.nodes[nodeIndex];
|
||||
const existingData = pick<Partial<INodeUi>>(node, Object.keys(nodeData));
|
||||
const changed = !isEqual(existingData, nodeData);
|
||||
|
||||
if (changed) {
|
||||
Object.assign(node, nodeData);
|
||||
ws.workflow.nodes[nodeIndex] = node;
|
||||
ws.workflowObject.setNodes(ws.workflow.nodes);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setNodeParameters(updateInformation: IUpdateInformation, append?: boolean): void {
|
||||
// Find the node that should be updated
|
||||
const nodeIndex = ws.workflow.nodes.findIndex((node) => {
|
||||
|
|
@ -243,7 +275,7 @@ export function useWorkflowState() {
|
|||
? { ...parameters, ...updateInformation.value }
|
||||
: updateInformation.value;
|
||||
|
||||
const changed = ws.updateNodeAtIndex(nodeIndex, {
|
||||
const changed = updateNodeAtIndex(nodeIndex, {
|
||||
parameters: newParameters as INodeParameters,
|
||||
});
|
||||
|
||||
|
|
@ -275,6 +307,95 @@ export function useWorkflowState() {
|
|||
}
|
||||
}
|
||||
|
||||
function setNodeValue(updateInformation: IUpdateInformation): void {
|
||||
// Find the node that should be updated
|
||||
const nodeIndex = ws.workflow.nodes.findIndex((node) => {
|
||||
return node.name === updateInformation.name;
|
||||
});
|
||||
|
||||
if (nodeIndex === -1 || !updateInformation.key) {
|
||||
throw new Error(
|
||||
`Node with the name "${updateInformation.name}" could not be found to set parameter.`,
|
||||
);
|
||||
}
|
||||
|
||||
const changed = updateNodeAtIndex(nodeIndex, {
|
||||
[updateInformation.key]: updateInformation.value,
|
||||
});
|
||||
|
||||
uiStore.stateIsDirty = uiStore.stateIsDirty || changed;
|
||||
|
||||
const excludeKeys = ['position', 'notes', 'notesInFlow'];
|
||||
|
||||
if (changed && !excludeKeys.includes(updateInformation.key)) {
|
||||
ws.nodeMetadata[ws.workflow.nodes[nodeIndex].name].parametersLastUpdatedAt = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
function setNodePositionById(id: string, position: INodeUi['position']): void {
|
||||
const node = ws.workflow.nodes.find((n) => n.id === id);
|
||||
if (!node) return;
|
||||
|
||||
setNodeValue({ name: node.name, key: 'position', value: position });
|
||||
}
|
||||
|
||||
function updateNodeProperties(
|
||||
this: WorkflowState,
|
||||
updateInformation: INodeUpdatePropertiesInformation,
|
||||
): void {
|
||||
// Find the node that should be updated
|
||||
const nodeIndex = ws.workflow.nodes.findIndex((node) => {
|
||||
return node.name === updateInformation.name;
|
||||
});
|
||||
|
||||
if (nodeIndex !== -1) {
|
||||
for (const key of Object.keys(updateInformation.properties)) {
|
||||
const typedKey = key as keyof INodeUpdatePropertiesInformation['properties'];
|
||||
const property = updateInformation.properties[typedKey];
|
||||
|
||||
const changed = updateNodeAtIndex(nodeIndex, { [key]: property });
|
||||
|
||||
if (changed) {
|
||||
uiStore.stateIsDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
workflowStateEventBus.emit('updateNodeProperties', [this, updateInformation]);
|
||||
}
|
||||
|
||||
function setNodeIssue(nodeIssueData: INodeIssueData): void {
|
||||
const nodeIndex = ws.workflow.nodes.findIndex((node) => {
|
||||
return node.name === nodeIssueData.node;
|
||||
});
|
||||
if (nodeIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const node = ws.workflow.nodes[nodeIndex];
|
||||
|
||||
if (nodeIssueData.value === null) {
|
||||
// Remove the value if one exists
|
||||
if (node.issues?.[nodeIssueData.type] === undefined) {
|
||||
// No values for type exist so nothing has to get removed
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { [nodeIssueData.type]: _removedNodeIssue, ...remainingNodeIssues } = node.issues;
|
||||
updateNodeAtIndex(nodeIndex, {
|
||||
issues: remainingNodeIssues,
|
||||
});
|
||||
} else {
|
||||
updateNodeAtIndex(nodeIndex, {
|
||||
issues: {
|
||||
...node.issues,
|
||||
[nodeIssueData.type]: nodeIssueData.value as INodeIssueObjectProperty,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// Workflow editing state
|
||||
resetState,
|
||||
|
|
@ -296,6 +417,11 @@ export function useWorkflowState() {
|
|||
// Node modification
|
||||
setNodeParameters,
|
||||
setLastNodeParameters,
|
||||
setNodeValue,
|
||||
setNodePositionById,
|
||||
setNodeIssue,
|
||||
updateNodeAtIndex,
|
||||
updateNodeProperties,
|
||||
|
||||
// reexport
|
||||
executingNode: workflowStateStore.executingNode,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, useTemplateRef } from 'vue';
|
||||
import { computed, onMounted, onUnmounted, ref, useTemplateRef } from 'vue';
|
||||
import get from 'lodash/get';
|
||||
import set from 'lodash/set';
|
||||
import unset from 'lodash/unset';
|
||||
|
|
@ -60,6 +60,11 @@ import {
|
|||
N8nSelect,
|
||||
N8nText,
|
||||
} from '@n8n/design-system';
|
||||
import {
|
||||
injectWorkflowState,
|
||||
type WorkflowStateBusEvents,
|
||||
workflowStateEventBus,
|
||||
} from '@/composables/useWorkflowState';
|
||||
|
||||
defineOptions({ name: 'EventDestinationSettingsModal' });
|
||||
|
||||
|
|
@ -83,6 +88,7 @@ const telemetry = useTelemetry();
|
|||
const logStreamingStore = useLogStreamingStore();
|
||||
const ndvStore = useNDVStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const workflowState = injectWorkflowState();
|
||||
const uiStore = useUIStore();
|
||||
|
||||
const unchanged = ref(!isNew);
|
||||
|
|
@ -160,22 +166,24 @@ const canManageLogStreaming = computed(() =>
|
|||
hasPermission(['rbac'], { rbac: { scope: 'logStreaming:manage' } }),
|
||||
);
|
||||
|
||||
function onUpdateNodeProperties(event: WorkflowStateBusEvents['updateNodeProperties']) {
|
||||
const updateInformation = event[1];
|
||||
if (updateInformation.name === destination.id) {
|
||||
if ('credentials' in updateInformation.properties) {
|
||||
unchanged.value = false;
|
||||
nodeParameters.value.credentials = updateInformation.properties
|
||||
.credentials as NodeParameterValueType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setupNode(Object.assign(deepCopy(defaultMessageEventBusDestinationOptions), destination));
|
||||
workflowsStore.$onAction(({ name, args }) => {
|
||||
if (name === 'updateNodeProperties') {
|
||||
for (const arg of args) {
|
||||
if (arg.name === destination.id) {
|
||||
if ('credentials' in arg.properties) {
|
||||
unchanged.value = false;
|
||||
nodeParameters.value.credentials = arg.properties.credentials as NodeParameterValueType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
workflowStateEventBus.on('updateNodeProperties', onUpdateNodeProperties);
|
||||
});
|
||||
|
||||
onUnmounted(() => workflowStateEventBus.off('updateNodeProperties', onUpdateNodeProperties));
|
||||
|
||||
function onInput() {
|
||||
unchanged.value = false;
|
||||
testMessageSent.value = false;
|
||||
|
|
@ -259,7 +267,7 @@ function valueChanged(parameterData: IUpdateInformation) {
|
|||
}
|
||||
|
||||
nodeParameters.value = deepCopy(nodeParametersCopy);
|
||||
workflowsStore.updateNodeProperties({
|
||||
workflowState.updateNodeProperties({
|
||||
name: node.value.name,
|
||||
properties: { parameters: nodeParameters.value, position: [0, 0] },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@ import { useCredentialsStore } from '@/stores/credentials.store';
|
|||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import type { TemplateCredentialKey } from '../utils/templateTransforms';
|
||||
import { useCredentialSetupState } from './useCredentialSetupState';
|
||||
import { injectWorkflowState } from '@/composables/useWorkflowState';
|
||||
|
||||
export const useSetupWorkflowCredentialsModalState = () => {
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const workflowState = injectWorkflowState();
|
||||
const credentialsStore = useCredentialsStore();
|
||||
const nodeHelpers = useNodeHelpers();
|
||||
|
||||
|
|
@ -63,7 +65,7 @@ export const useSetupWorkflowCredentialsModalState = () => {
|
|||
};
|
||||
|
||||
usages.usedBy.forEach((node) => {
|
||||
workflowsStore.updateNodeProperties({
|
||||
workflowState.updateNodeProperties({
|
||||
name: node.name,
|
||||
properties: {
|
||||
credentials: {
|
||||
|
|
@ -97,7 +99,7 @@ export const useSetupWorkflowCredentialsModalState = () => {
|
|||
const credentials = { ...node.credentials };
|
||||
delete credentials[usages.credentialType];
|
||||
|
||||
workflowsStore.updateNodeProperties({
|
||||
workflowState.updateNodeProperties({
|
||||
name: node.name,
|
||||
properties: {
|
||||
credentials,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import { v4 as uuid } from 'uuid';
|
|||
import { useWorkflowsStore } from './workflows.store';
|
||||
import { computed, ref } from 'vue';
|
||||
import type { TelemetryNdvSource } from '@/types/telemetry';
|
||||
import { injectWorkflowState } from '@/composables/useWorkflowState';
|
||||
|
||||
const DEFAULT_MAIN_PANEL_DIMENSIONS = {
|
||||
relativeLeft: 1,
|
||||
|
|
@ -94,6 +95,7 @@ export const useNDVStore = defineStore(STORES.NDV, () => {
|
|||
const lastSetActiveNodeSource = ref<TelemetryNdvSource>();
|
||||
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const workflowState = injectWorkflowState();
|
||||
|
||||
const activeNode = computed(() => {
|
||||
return workflowsStore.getNodeByName(activeNodeName.value || '');
|
||||
|
|
@ -371,7 +373,7 @@ export const useNDVStore = defineStore(STORES.NDV, () => {
|
|||
return node.name === activeNode.name;
|
||||
});
|
||||
|
||||
workflowsStore.updateNodeAtIndex(nodeIndex, {
|
||||
workflowState.updateNodeAtIndex(nodeIndex, {
|
||||
issues: {
|
||||
...activeNode.issues,
|
||||
...issues,
|
||||
|
|
|
|||
|
|
@ -1030,51 +1030,6 @@ describe('useWorkflowsStore', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('setNodeValue()', () => {
|
||||
it('should update a node', () => {
|
||||
const nodeName = 'Edit Fields';
|
||||
workflowsStore.addNode({
|
||||
parameters: {},
|
||||
id: '554c7ff4-7ee2-407c-8931-e34234c5056a',
|
||||
name: nodeName,
|
||||
type: 'n8n-nodes-base.set',
|
||||
position: [680, 180],
|
||||
typeVersion: 3.4,
|
||||
});
|
||||
|
||||
expect(workflowsStore.nodeMetadata[nodeName].parametersLastUpdatedAt).toBe(undefined);
|
||||
|
||||
workflowsStore.setNodeValue({ name: 'Edit Fields', key: 'executeOnce', value: true });
|
||||
|
||||
expect(workflowsStore.workflow.nodes[0].executeOnce).toBe(true);
|
||||
expect(workflowsStore.nodeMetadata[nodeName].parametersLastUpdatedAt).toEqual(
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setNodePositionById()', () => {
|
||||
it('should NOT update parametersLastUpdatedAt', () => {
|
||||
const nodeName = 'Edit Fields';
|
||||
const nodeId = '554c7ff4-7ee2-407c-8931-e34234c5056a';
|
||||
workflowsStore.addNode({
|
||||
parameters: {},
|
||||
id: nodeId,
|
||||
name: nodeName,
|
||||
type: 'n8n-nodes-base.set',
|
||||
position: [680, 180],
|
||||
typeVersion: 3.4,
|
||||
});
|
||||
|
||||
expect(workflowsStore.nodeMetadata[nodeName].parametersLastUpdatedAt).toBe(undefined);
|
||||
|
||||
workflowsStore.setNodePositionById(nodeId, [0, 0]);
|
||||
|
||||
expect(workflowsStore.workflow.nodes[0].position).toStrictEqual([0, 0]);
|
||||
expect(workflowsStore.nodeMetadata[nodeName].parametersLastUpdatedAt).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setNodes()', () => {
|
||||
it('should transform credential-only nodes', () => {
|
||||
const setNodeId = '1';
|
||||
|
|
@ -1103,55 +1058,6 @@ describe('useWorkflowsStore', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('updateNodeAtIndex', () => {
|
||||
it.each([
|
||||
{
|
||||
description: 'should update node at given index with provided data',
|
||||
nodeIndex: 0,
|
||||
nodeData: { name: 'Updated Node' },
|
||||
initialNodes: [{ name: 'Original Node' }],
|
||||
expectedNodes: [{ name: 'Updated Node' }],
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
description: 'should not update node if index is invalid',
|
||||
nodeIndex: -1,
|
||||
nodeData: { name: 'Updated Node' },
|
||||
initialNodes: [{ name: 'Original Node' }],
|
||||
expectedNodes: [{ name: 'Original Node' }],
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
description: 'should return false if node data is unchanged',
|
||||
nodeIndex: 0,
|
||||
nodeData: { name: 'Original Node' },
|
||||
initialNodes: [{ name: 'Original Node' }],
|
||||
expectedNodes: [{ name: 'Original Node' }],
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
description: 'should update multiple properties of a node',
|
||||
nodeIndex: 0,
|
||||
nodeData: { name: 'Updated Node', type: 'newType' },
|
||||
initialNodes: [{ name: 'Original Node', type: 'oldType' }],
|
||||
expectedNodes: [{ name: 'Updated Node', type: 'newType' }],
|
||||
expectedResult: true,
|
||||
},
|
||||
])('$description', ({ nodeIndex, nodeData, initialNodes, expectedNodes, expectedResult }) => {
|
||||
workflowsStore.workflow.nodes = initialNodes as unknown as IWorkflowDb['nodes'];
|
||||
|
||||
const result = workflowsStore.updateNodeAtIndex(nodeIndex, nodeData);
|
||||
|
||||
expect(result).toBe(expectedResult);
|
||||
expect(workflowsStore.workflow.nodes).toEqual(expectedNodes);
|
||||
});
|
||||
|
||||
it('should throw error if out of bounds', () => {
|
||||
workflowsStore.workflow.nodes = [];
|
||||
expect(() => workflowsStore.updateNodeAtIndex(0, { name: 'Updated Node' })).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('findNodeByPartialId', () => {
|
||||
test.each([
|
||||
[[], 'D', undefined],
|
||||
|
|
@ -1596,7 +1502,7 @@ describe('useWorkflowsStore', () => {
|
|||
await waitFor(() => expect(workflowsStore.selectedTriggerNodeName).toBe('n0'));
|
||||
workflowsStore.removeNode(n0);
|
||||
await waitFor(() => expect(workflowsStore.selectedTriggerNodeName).toBe('n1'));
|
||||
workflowsStore.setNodeValue({ name: 'n1', key: 'disabled', value: true });
|
||||
useWorkflowState().setNodeValue({ name: 'n1', key: 'disabled', value: true });
|
||||
await waitFor(() => expect(workflowsStore.selectedTriggerNodeName).toBe(undefined));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,9 +17,7 @@ import type {
|
|||
IExecutionsListResponse,
|
||||
INodeMetadata,
|
||||
INodeUi,
|
||||
INodeUpdatePropertiesInformation,
|
||||
IStartRunData,
|
||||
IUpdateInformation,
|
||||
IUsedCredential,
|
||||
IWorkflowDb,
|
||||
IWorkflowsMap,
|
||||
|
|
@ -44,8 +42,6 @@ import type {
|
|||
INodeCredentials,
|
||||
INodeCredentialsDetails,
|
||||
INodeExecutionData,
|
||||
INodeIssueData,
|
||||
INodeIssueObjectProperty,
|
||||
INodeTypes,
|
||||
IPinData,
|
||||
IRunData,
|
||||
|
|
@ -63,8 +59,6 @@ import {
|
|||
TelemetryHelpers,
|
||||
} from 'n8n-workflow';
|
||||
import * as workflowUtils from 'n8n-workflow/common';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import pick from 'lodash/pick';
|
||||
|
||||
import { useRootStore } from '@n8n/stores/useRootStore';
|
||||
import * as workflowsApi from '@/api/workflows';
|
||||
|
|
@ -516,13 +510,6 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
|||
return workflow.value.nodes.map((node) => ({ ...node }));
|
||||
}
|
||||
|
||||
function setNodePositionById(id: string, position: INodeUi['position']): void {
|
||||
const node = workflow.value.nodes.find((n) => n.id === id);
|
||||
if (!node) return;
|
||||
|
||||
setNodeValue({ name: node.name, key: 'position', value: position });
|
||||
}
|
||||
|
||||
function convertTemplateNodeToNodeUi(node: IWorkflowTemplateNode): INodeUi {
|
||||
const filteredCredentials = Object.keys(node.credentials ?? {}).reduce<INodeCredentials>(
|
||||
(credentials, curr) => {
|
||||
|
|
@ -1229,57 +1216,6 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
|||
workflowObject.value.setConnections(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns `true` if the object was changed
|
||||
*/
|
||||
function updateNodeAtIndex(nodeIndex: number, nodeData: Partial<INodeUi>): boolean {
|
||||
if (nodeIndex !== -1) {
|
||||
const node = workflow.value.nodes[nodeIndex];
|
||||
const existingData = pick<Partial<INodeUi>>(node, Object.keys(nodeData));
|
||||
const changed = !isEqual(existingData, nodeData);
|
||||
|
||||
if (changed) {
|
||||
Object.assign(node, nodeData);
|
||||
workflow.value.nodes[nodeIndex] = node;
|
||||
workflowObject.value.setNodes(workflow.value.nodes);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setNodeIssue(nodeIssueData: INodeIssueData): void {
|
||||
const nodeIndex = workflow.value.nodes.findIndex((node) => {
|
||||
return node.name === nodeIssueData.node;
|
||||
});
|
||||
if (nodeIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const node = workflow.value.nodes[nodeIndex];
|
||||
|
||||
if (nodeIssueData.value === null) {
|
||||
// Remove the value if one exists
|
||||
if (node.issues?.[nodeIssueData.type] === undefined) {
|
||||
// No values for type exist so nothing has to get removed
|
||||
return;
|
||||
}
|
||||
|
||||
const { [nodeIssueData.type]: removedNodeIssue, ...remainingNodeIssues } = node.issues;
|
||||
updateNodeAtIndex(nodeIndex, {
|
||||
issues: remainingNodeIssues,
|
||||
});
|
||||
} else {
|
||||
updateNodeAtIndex(nodeIndex, {
|
||||
issues: {
|
||||
...node.issues,
|
||||
[nodeIssueData.type]: nodeIssueData.value as INodeIssueObjectProperty,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addNode(nodeData: INodeUi): void {
|
||||
// @TODO(ckolb): Reminder to refactor useActions:setAddedNodeActionParameters
|
||||
// which listens to this function being called, when this is moved to workflowState soon
|
||||
|
|
@ -1321,51 +1257,6 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
|||
}
|
||||
}
|
||||
|
||||
function updateNodeProperties(updateInformation: INodeUpdatePropertiesInformation): void {
|
||||
// Find the node that should be updated
|
||||
const nodeIndex = workflow.value.nodes.findIndex((node) => {
|
||||
return node.name === updateInformation.name;
|
||||
});
|
||||
|
||||
if (nodeIndex !== -1) {
|
||||
for (const key of Object.keys(updateInformation.properties)) {
|
||||
const typedKey = key as keyof INodeUpdatePropertiesInformation['properties'];
|
||||
const property = updateInformation.properties[typedKey];
|
||||
|
||||
const changed = updateNodeAtIndex(nodeIndex, { [key]: property });
|
||||
|
||||
if (changed) {
|
||||
uiStore.stateIsDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setNodeValue(updateInformation: IUpdateInformation): void {
|
||||
// Find the node that should be updated
|
||||
const nodeIndex = workflow.value.nodes.findIndex((node) => {
|
||||
return node.name === updateInformation.name;
|
||||
});
|
||||
|
||||
if (nodeIndex === -1 || !updateInformation.key) {
|
||||
throw new Error(
|
||||
`Node with the name "${updateInformation.name}" could not be found to set parameter.`,
|
||||
);
|
||||
}
|
||||
|
||||
const changed = updateNodeAtIndex(nodeIndex, {
|
||||
[updateInformation.key]: updateInformation.value,
|
||||
});
|
||||
|
||||
uiStore.stateIsDirty = uiStore.stateIsDirty || changed;
|
||||
|
||||
const excludeKeys = ['position', 'notes', 'notesInFlow'];
|
||||
|
||||
if (changed && !excludeKeys.includes(updateInformation.key)) {
|
||||
nodeMetadata.value[workflow.value.nodes[nodeIndex].name].parametersLastUpdatedAt = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
async function trackNodeExecution(pushData: PushPayload<'nodeExecuteAfter'>): Promise<void> {
|
||||
const nodeName = pushData.nodeName;
|
||||
|
||||
|
|
@ -1905,12 +1796,8 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
|||
removeConnection,
|
||||
removeAllNodeConnection,
|
||||
renameNodeSelectedAndExecution,
|
||||
updateNodeAtIndex,
|
||||
setNodeIssue,
|
||||
addNode,
|
||||
removeNode,
|
||||
updateNodeProperties,
|
||||
setNodeValue,
|
||||
updateNodeExecutionRunData,
|
||||
updateNodeExecutionStatus,
|
||||
clearNodeExecutionData,
|
||||
|
|
@ -1931,7 +1818,6 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
|||
resetChatMessages,
|
||||
appendChatMessage,
|
||||
checkIfNodeHasChatParent,
|
||||
setNodePositionById,
|
||||
removeNodeById,
|
||||
removeNodeConnectionsById,
|
||||
removeNodeExecutionDataById,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import {
|
|||
import { i18n as locale } from '@n8n/i18n';
|
||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { isJsonKeyObject } from '@/utils/typesUtils';
|
||||
import {
|
||||
isResourceLocatorValue,
|
||||
|
|
@ -23,6 +22,7 @@ import {
|
|||
type ResourceMapperField,
|
||||
type Themed,
|
||||
} from 'n8n-workflow';
|
||||
import type { WorkflowState } from '@/composables/useWorkflowState';
|
||||
|
||||
/*
|
||||
Constants and utility functions mainly used to get information about
|
||||
|
|
@ -360,7 +360,11 @@ export const getCredentialsRelatedFields = (
|
|||
return fields;
|
||||
};
|
||||
|
||||
export const updateNodeAuthType = (node: INodeUi | null, type: string) => {
|
||||
export const updateNodeAuthType = (
|
||||
workflowState: WorkflowState,
|
||||
node: INodeUi | null,
|
||||
type: string,
|
||||
) => {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -377,7 +381,7 @@ export const updateNodeAuthType = (node: INodeUi | null, type: string) => {
|
|||
},
|
||||
},
|
||||
};
|
||||
useWorkflowsStore().updateNodeProperties(updateInformation);
|
||||
workflowState.updateNodeProperties(updateInformation);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user