mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -08:00
feat(editor): Add several performance improvements when adding nodes in new canvas (no-changelog) (#10170)
This commit is contained in:
parent
520f2316d1
commit
112d6b883d
|
@ -5,6 +5,7 @@ import type { Workflow } from 'n8n-workflow';
|
||||||
import type { IWorkflowDb } from '@/Interface';
|
import type { IWorkflowDb } from '@/Interface';
|
||||||
import { useCanvasMapping } from '@/composables/useCanvasMapping';
|
import { useCanvasMapping } from '@/composables/useCanvasMapping';
|
||||||
import type { EventBus } from 'n8n-design-system';
|
import type { EventBus } from 'n8n-design-system';
|
||||||
|
import { createEventBus } from 'n8n-design-system';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
|
@ -21,6 +22,7 @@ const props = withDefaults(
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
id: 'canvas',
|
id: 'canvas',
|
||||||
|
eventBus: () => createEventBus(),
|
||||||
fallbackNodes: () => [],
|
fallbackNodes: () => [],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { useCredentialsStore } from '@/stores/credentials.store';
|
||||||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||||
import { telemetry } from '@/plugins/telemetry';
|
import { telemetry } from '@/plugins/telemetry';
|
||||||
import { useClipboard } from '@/composables/useClipboard';
|
import { useClipboard } from '@/composables/useClipboard';
|
||||||
|
import { waitFor } from '@testing-library/vue';
|
||||||
|
|
||||||
vi.mock('vue-router', async (importOriginal) => {
|
vi.mock('vue-router', async (importOriginal) => {
|
||||||
const actual = await importOriginal<{}>();
|
const actual = await importOriginal<{}>();
|
||||||
|
@ -74,68 +75,89 @@ describe('useCanvasOperations', () => {
|
||||||
|
|
||||||
workflowsStore.resetWorkflow();
|
workflowsStore.resetWorkflow();
|
||||||
workflowsStore.resetState();
|
workflowsStore.resetState();
|
||||||
await workflowHelpers.initState(workflow);
|
workflowHelpers.initState(workflow);
|
||||||
|
|
||||||
canvasOperations = useCanvasOperations({ router, lastClickPosition });
|
canvasOperations = useCanvasOperations({ router, lastClickPosition });
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('addNode', () => {
|
describe('requireNodeTypeDescription', () => {
|
||||||
it('should throw error when node type does not exist', async () => {
|
it('should return node type description when type and version match', () => {
|
||||||
vi.spyOn(nodeTypesStore, 'getNodeTypes').mockResolvedValue(undefined);
|
const type = 'testType';
|
||||||
|
const version = 1;
|
||||||
|
const expectedDescription = mockNodeTypeDescription({ name: type, version });
|
||||||
|
nodeTypesStore.setNodeTypes([expectedDescription]);
|
||||||
|
|
||||||
await expect(canvasOperations.addNode({ type: 'nonexistent' })).rejects.toThrow();
|
const result = canvasOperations.requireNodeTypeDescription(type, version);
|
||||||
|
|
||||||
|
expect(result).toBe(expectedDescription);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create node with default version when version is undefined', async () => {
|
it('should throw an error when node type does not exist', () => {
|
||||||
nodeTypesStore.setNodeTypes([mockNodeTypeDescription({ name: 'type' })]);
|
const type = 'nonexistentType';
|
||||||
|
|
||||||
const result = await canvasOperations.addNode({
|
expect(() => {
|
||||||
name: 'example',
|
canvasOperations.requireNodeTypeDescription(type);
|
||||||
type: 'type',
|
}).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return node type description when only type is provided and it exists', () => {
|
||||||
|
const type = 'testTypeWithoutVersion';
|
||||||
|
const expectedDescription = mockNodeTypeDescription({ name: type });
|
||||||
|
nodeTypesStore.setNodeTypes([expectedDescription]);
|
||||||
|
|
||||||
|
const result = canvasOperations.requireNodeTypeDescription(type);
|
||||||
|
|
||||||
|
expect(result).toBe(expectedDescription);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('addNode', () => {
|
||||||
|
it('should create node with default version when version is undefined', () => {
|
||||||
|
const result = canvasOperations.addNode(
|
||||||
|
{
|
||||||
|
name: 'example',
|
||||||
|
type: 'type',
|
||||||
|
typeVersion: 1,
|
||||||
|
},
|
||||||
|
mockNodeTypeDescription({ name: 'type' }),
|
||||||
|
);
|
||||||
|
|
||||||
expect(result.typeVersion).toBe(1);
|
expect(result.typeVersion).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create node with last version when version is an array', async () => {
|
it('should create node with default position when position is not provided', () => {
|
||||||
nodeTypesStore.setNodeTypes([mockNodeTypeDescription({ name: 'type', version: [1, 2] })]);
|
const result = canvasOperations.addNode(
|
||||||
|
{
|
||||||
const result = await canvasOperations.addNode({
|
type: 'type',
|
||||||
type: 'type',
|
typeVersion: 1,
|
||||||
});
|
},
|
||||||
|
mockNodeTypeDescription({ name: 'type' }),
|
||||||
expect(result.typeVersion).toBe(2);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
it('should create node with default position when position is not provided', async () => {
|
|
||||||
nodeTypesStore.setNodeTypes([mockNodeTypeDescription({ name: 'type' })]);
|
|
||||||
|
|
||||||
const result = await canvasOperations.addNode({
|
|
||||||
type: 'type',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.position).toEqual([460, 460]); // Default last click position
|
expect(result.position).toEqual([460, 460]); // Default last click position
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create node with provided position when position is provided', async () => {
|
it('should create node with provided position when position is provided', () => {
|
||||||
nodeTypesStore.setNodeTypes([mockNodeTypeDescription({ name: 'type' })]);
|
const result = canvasOperations.addNode(
|
||||||
|
{
|
||||||
const result = await canvasOperations.addNode({
|
type: 'type',
|
||||||
type: 'type',
|
typeVersion: 1,
|
||||||
position: [20, 20],
|
position: [20, 20],
|
||||||
});
|
},
|
||||||
|
mockNodeTypeDescription({ name: 'type' }),
|
||||||
|
);
|
||||||
|
|
||||||
expect(result.position).toEqual([20, 20]);
|
expect(result.position).toEqual([20, 20]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create node with default credentials when only one credential is available', async () => {
|
it('should create node with default credentials when only one credential is available', () => {
|
||||||
const credential = mock<ICredentialsResponse>({ id: '1', name: 'cred', type: 'cred' });
|
const credential = mock<ICredentialsResponse>({ id: '1', name: 'cred', type: 'cred' });
|
||||||
const nodeTypeName = 'type';
|
const nodeTypeName = 'type';
|
||||||
|
const nodeTypeDescription = mockNodeTypeDescription({
|
||||||
nodeTypesStore.setNodeTypes([
|
name: nodeTypeName,
|
||||||
mockNodeTypeDescription({ name: nodeTypeName, credentials: [{ name: credential.name }] }),
|
credentials: [{ name: credential.name }],
|
||||||
]);
|
});
|
||||||
|
|
||||||
credentialsStore.addCredentials([credential]);
|
credentialsStore.addCredentials([credential]);
|
||||||
|
|
||||||
|
@ -144,24 +166,25 @@ describe('useCanvasOperations', () => {
|
||||||
credential,
|
credential,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const result = await canvasOperations.addNode({
|
const result = canvasOperations.addNode(
|
||||||
type: nodeTypeName,
|
{
|
||||||
});
|
type: nodeTypeName,
|
||||||
|
typeVersion: 1,
|
||||||
|
},
|
||||||
|
nodeTypeDescription,
|
||||||
|
);
|
||||||
|
|
||||||
expect(result.credentials).toEqual({ [credential.name]: { id: '1', name: credential.name } });
|
expect(result.credentials).toEqual({ [credential.name]: { id: '1', name: credential.name } });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not assign credentials when multiple credentials are available', async () => {
|
it('should not assign credentials when multiple credentials are available', () => {
|
||||||
const credentialA = mock<ICredentialsResponse>({ id: '1', name: 'credA', type: 'cred' });
|
const credentialA = mock<ICredentialsResponse>({ id: '1', name: 'credA', type: 'cred' });
|
||||||
const credentialB = mock<ICredentialsResponse>({ id: '1', name: 'credB', type: 'cred' });
|
const credentialB = mock<ICredentialsResponse>({ id: '1', name: 'credB', type: 'cred' });
|
||||||
const nodeTypeName = 'type';
|
const nodeTypeName = 'type';
|
||||||
|
const nodeTypeDescription = mockNodeTypeDescription({
|
||||||
nodeTypesStore.setNodeTypes([
|
name: nodeTypeName,
|
||||||
mockNodeTypeDescription({
|
credentials: [{ name: credentialA.name }, { name: credentialB.name }],
|
||||||
name: nodeTypeName,
|
});
|
||||||
credentials: [{ name: credentialA.name }, { name: credentialB.name }],
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// @ts-expect-error Known pinia issue when spying on store getters
|
// @ts-expect-error Known pinia issue when spying on store getters
|
||||||
vi.spyOn(credentialsStore, 'getUsableCredentialByType', 'get').mockReturnValue(() => [
|
vi.spyOn(credentialsStore, 'getUsableCredentialByType', 'get').mockReturnValue(() => [
|
||||||
|
@ -169,24 +192,30 @@ describe('useCanvasOperations', () => {
|
||||||
credentialB,
|
credentialB,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const result = await canvasOperations.addNode({
|
const result = canvasOperations.addNode(
|
||||||
type: 'type',
|
{
|
||||||
});
|
type: 'type',
|
||||||
|
typeVersion: 1,
|
||||||
|
},
|
||||||
|
nodeTypeDescription,
|
||||||
|
);
|
||||||
expect(result.credentials).toBeUndefined();
|
expect(result.credentials).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should open NDV when specified', async () => {
|
it('should open NDV when specified', async () => {
|
||||||
nodeTypesStore.setNodeTypes([mockNodeTypeDescription({ name: 'type' })]);
|
const nodeTypeDescription = mockNodeTypeDescription({ name: 'type' });
|
||||||
|
|
||||||
await canvasOperations.addNode(
|
canvasOperations.addNode(
|
||||||
{
|
{
|
||||||
type: 'type',
|
type: 'type',
|
||||||
|
typeVersion: 1,
|
||||||
name: 'Test Name',
|
name: 'Test Name',
|
||||||
},
|
},
|
||||||
|
nodeTypeDescription,
|
||||||
{ openNDV: true },
|
{ openNDV: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(ndvStore.activeNodeName).toBe('Test Name');
|
await waitFor(() => expect(ndvStore.activeNodeName).toBe('Test Name'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -719,7 +748,7 @@ describe('useCanvasOperations', () => {
|
||||||
|
|
||||||
const addConnectionSpy = vi.spyOn(workflowsStore, 'addConnection');
|
const addConnectionSpy = vi.spyOn(workflowsStore, 'addConnection');
|
||||||
|
|
||||||
await canvasOperations.addConnections(connections);
|
canvasOperations.addConnections(connections);
|
||||||
|
|
||||||
expect(addConnectionSpy).toHaveBeenCalledWith({
|
expect(addConnectionSpy).toHaveBeenCalledWith({
|
||||||
connection: [
|
connection: [
|
||||||
|
|
|
@ -97,6 +97,10 @@ type AddNodeData = Partial<INodeUi> & {
|
||||||
type: string;
|
type: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type AddNodeDataWithTypeVersion = AddNodeData & {
|
||||||
|
typeVersion: INodeUi['typeVersion'];
|
||||||
|
};
|
||||||
|
|
||||||
type AddNodeOptions = {
|
type AddNodeOptions = {
|
||||||
dragAndDrop?: boolean;
|
dragAndDrop?: boolean;
|
||||||
openNDV?: boolean;
|
openNDV?: boolean;
|
||||||
|
@ -413,6 +417,18 @@ export function useCanvasOperations({
|
||||||
historyStore.stopRecordingUndo();
|
historyStore.stopRecordingUndo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function requireNodeTypeDescription(type: INodeUi['type'], version?: INodeUi['typeVersion']) {
|
||||||
|
const nodeTypeDescription = nodeTypesStore.getNodeType(type, version);
|
||||||
|
if (!nodeTypeDescription) {
|
||||||
|
throw new Error(
|
||||||
|
i18n.baseText('nodeView.showMessage.addNodeButton.message', {
|
||||||
|
interpolate: { nodeTypeName: type },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return nodeTypeDescription;
|
||||||
|
}
|
||||||
|
|
||||||
async function addNodes(
|
async function addNodes(
|
||||||
nodes: AddedNodesAndConnections['nodes'],
|
nodes: AddedNodesAndConnections['nodes'],
|
||||||
options: {
|
options: {
|
||||||
|
@ -420,6 +436,7 @@ export function useCanvasOperations({
|
||||||
position?: XYPosition;
|
position?: XYPosition;
|
||||||
trackHistory?: boolean;
|
trackHistory?: boolean;
|
||||||
trackBulk?: boolean;
|
trackBulk?: boolean;
|
||||||
|
keepPristine?: boolean;
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
let insertPosition = options.position;
|
let insertPosition = options.position;
|
||||||
|
@ -429,16 +446,32 @@ export function useCanvasOperations({
|
||||||
historyStore.startRecordingUndo();
|
historyStore.startRecordingUndo();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const nodeAddData of nodes) {
|
const nodesWithTypeVersion = nodes.map((node) => {
|
||||||
|
const typeVersion = resolveNodeVersion(requireNodeTypeDescription(node.type));
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
typeVersion,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
await loadNodeTypesProperties(nodesWithTypeVersion);
|
||||||
|
|
||||||
|
if (options.trackBulk) {
|
||||||
|
historyStore.startRecordingUndo();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const nodeAddData of nodesWithTypeVersion) {
|
||||||
const { isAutoAdd, openDetail: openNDV, ...node } = nodeAddData;
|
const { isAutoAdd, openDetail: openNDV, ...node } = nodeAddData;
|
||||||
const position = node.position ?? insertPosition;
|
const position = node.position ?? insertPosition;
|
||||||
|
const nodeTypeDescription = requireNodeTypeDescription(node.type, node.typeVersion);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
lastAddedNode = await addNode(
|
lastAddedNode = addNode(
|
||||||
{
|
{
|
||||||
...node,
|
...node,
|
||||||
position,
|
position,
|
||||||
},
|
},
|
||||||
|
nodeTypeDescription,
|
||||||
{
|
{
|
||||||
...options,
|
...options,
|
||||||
openNDV,
|
openNDV,
|
||||||
|
@ -459,13 +492,16 @@ export function useCanvasOperations({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastAddedNode) {
|
if (lastAddedNode) {
|
||||||
// @TODO Figure out what this does and why it's needed
|
|
||||||
updatePositionForNodeWithMultipleInputs(lastAddedNode);
|
updatePositionForNodeWithMultipleInputs(lastAddedNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.trackBulk) {
|
if (options.trackBulk) {
|
||||||
historyStore.stopRecordingUndo();
|
historyStore.stopRecordingUndo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!options.keepPristine) {
|
||||||
|
uiStore.stateIsDirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePositionForNodeWithMultipleInputs(node: INodeUi) {
|
function updatePositionForNodeWithMultipleInputs(node: INodeUi) {
|
||||||
|
@ -484,16 +520,11 @@ export function useCanvasOperations({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addNode(node: AddNodeData, options: AddNodeOptions = {}): Promise<INodeUi> {
|
function addNode(
|
||||||
const nodeTypeDescription = nodeTypesStore.getNodeType(node.type);
|
node: AddNodeDataWithTypeVersion,
|
||||||
if (!nodeTypeDescription) {
|
nodeTypeDescription: INodeTypeDescription,
|
||||||
throw new Error(
|
options: AddNodeOptions = {},
|
||||||
i18n.baseText('nodeView.showMessage.addNodeButton.message', {
|
): INodeUi {
|
||||||
interpolate: { nodeTypeName: node.type },
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if maximum allowed number of this type of node has been reached
|
// Check if maximum allowed number of this type of node has been reached
|
||||||
if (
|
if (
|
||||||
nodeTypeDescription.maxNodes !== undefined &&
|
nodeTypeDescription.maxNodes !== undefined &&
|
||||||
|
@ -507,35 +538,32 @@ export function useCanvasOperations({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeData = await resolveNodeData(node, nodeTypeDescription);
|
const nodeData = resolveNodeData(node, nodeTypeDescription);
|
||||||
if (!nodeData) {
|
if (!nodeData) {
|
||||||
throw new Error(i18n.baseText('nodeViewV2.showError.failedToCreateNode'));
|
throw new Error(i18n.baseText('nodeViewV2.showError.failedToCreateNode'));
|
||||||
}
|
}
|
||||||
|
|
||||||
historyStore.startRecordingUndo();
|
|
||||||
if (options.trackHistory) {
|
|
||||||
historyStore.pushCommandToUndo(new AddNodeCommand(nodeData));
|
|
||||||
}
|
|
||||||
|
|
||||||
workflowsStore.addNode(nodeData);
|
|
||||||
nodeHelpers.matchCredentials(nodeData);
|
nodeHelpers.matchCredentials(nodeData);
|
||||||
|
|
||||||
if (!options.isAutoAdd) {
|
void nextTick(() => {
|
||||||
createConnectionToLastInteractedWithNode(nodeData, options);
|
if (options.trackHistory) {
|
||||||
}
|
historyStore.pushCommandToUndo(new AddNodeCommand(nodeData));
|
||||||
|
}
|
||||||
|
|
||||||
runAddNodeHooks(nodeData, options);
|
workflowsStore.addNode(nodeData);
|
||||||
|
|
||||||
historyStore.stopRecordingUndo();
|
if (!options.isAutoAdd) {
|
||||||
|
createConnectionToLastInteractedWithNode(nodeData, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
runAddNodeHooks(nodeData, options);
|
||||||
|
|
||||||
|
if (options.openNDV) {
|
||||||
|
ndvStore.setActiveNodeName(nodeData.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
workflowsStore.setNodePristine(nodeData.name, true);
|
workflowsStore.setNodePristine(nodeData.name, true);
|
||||||
uiStore.stateIsDirty = true;
|
|
||||||
|
|
||||||
if (options.openNDV) {
|
|
||||||
void nextTick(() => {
|
|
||||||
ndvStore.setActiveNodeName(nodeData.name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeData;
|
return nodeData;
|
||||||
}
|
}
|
||||||
|
@ -661,11 +689,14 @@ export function useCanvasOperations({
|
||||||
/**
|
/**
|
||||||
* Resolves the data for a new node
|
* Resolves the data for a new node
|
||||||
*/
|
*/
|
||||||
async function resolveNodeData(node: AddNodeData, nodeTypeDescription: INodeTypeDescription) {
|
function resolveNodeData(
|
||||||
|
node: AddNodeDataWithTypeVersion,
|
||||||
|
nodeTypeDescription: INodeTypeDescription,
|
||||||
|
) {
|
||||||
const id = node.id ?? uuid();
|
const id = node.id ?? uuid();
|
||||||
const name = node.name ?? (nodeTypeDescription.defaults.name as string);
|
const name = node.name ?? (nodeTypeDescription.defaults.name as string);
|
||||||
const type = nodeTypeDescription.name;
|
const type = nodeTypeDescription.name;
|
||||||
const typeVersion = resolveNodeVersion(nodeTypeDescription);
|
const typeVersion = node.typeVersion;
|
||||||
const position = resolveNodePosition(node as INodeUi, nodeTypeDescription);
|
const position = resolveNodePosition(node as INodeUi, nodeTypeDescription);
|
||||||
const disabled = node.disabled ?? false;
|
const disabled = node.disabled ?? false;
|
||||||
const parameters = node.parameters ?? {};
|
const parameters = node.parameters ?? {};
|
||||||
|
@ -681,8 +712,6 @@ export function useCanvasOperations({
|
||||||
parameters,
|
parameters,
|
||||||
};
|
};
|
||||||
|
|
||||||
await loadNodeTypesProperties([{ name: nodeData.type, version: nodeData.typeVersion }]);
|
|
||||||
|
|
||||||
resolveNodeParameters(nodeData);
|
resolveNodeParameters(nodeData);
|
||||||
resolveNodeCredentials(nodeData, nodeTypeDescription);
|
resolveNodeCredentials(nodeData, nodeTypeDescription);
|
||||||
resolveNodeName(nodeData);
|
resolveNodeName(nodeData);
|
||||||
|
@ -691,7 +720,9 @@ export function useCanvasOperations({
|
||||||
return nodeData;
|
return nodeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadNodeTypesProperties(nodeInfos: INodeTypeNameVersion[]): Promise<void> {
|
async function loadNodeTypesProperties(
|
||||||
|
nodes: Array<Pick<INodeUi, 'type' | 'typeVersion'>>,
|
||||||
|
): Promise<void> {
|
||||||
const allNodeTypeDescriptions: INodeTypeDescription[] = nodeTypesStore.allNodeTypes;
|
const allNodeTypeDescriptions: INodeTypeDescription[] = nodeTypesStore.allNodeTypes;
|
||||||
|
|
||||||
const nodesToBeFetched: INodeTypeNameVersion[] = [];
|
const nodesToBeFetched: INodeTypeNameVersion[] = [];
|
||||||
|
@ -700,8 +731,8 @@ export function useCanvasOperations({
|
||||||
? nodeTypeDescription.version
|
? nodeTypeDescription.version
|
||||||
: [nodeTypeDescription.version];
|
: [nodeTypeDescription.version];
|
||||||
if (
|
if (
|
||||||
!!nodeInfos.find(
|
!!nodes.find(
|
||||||
(n) => n.name === nodeTypeDescription.name && nodeVersions.includes(n.version),
|
(n) => n.type === nodeTypeDescription.name && nodeVersions.includes(n.typeVersion),
|
||||||
) &&
|
) &&
|
||||||
!nodeTypeDescription.hasOwnProperty('properties')
|
!nodeTypeDescription.hasOwnProperty('properties')
|
||||||
) {
|
) {
|
||||||
|
@ -1154,7 +1185,7 @@ export function useCanvasOperations({
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addConnections(connections: CanvasConnectionCreateData[] | CanvasConnection[]) {
|
function addConnections(connections: CanvasConnectionCreateData[] | CanvasConnection[]) {
|
||||||
for (const { source, target, data } of connections) {
|
for (const { source, target, data } of connections) {
|
||||||
createConnection({
|
createConnection({
|
||||||
source,
|
source,
|
||||||
|
@ -1211,10 +1242,10 @@ export function useCanvasOperations({
|
||||||
|
|
||||||
async function initializeWorkspace(data: IWorkflowDb) {
|
async function initializeWorkspace(data: IWorkflowDb) {
|
||||||
// Set workflow data
|
// Set workflow data
|
||||||
await workflowHelpers.initState(data);
|
workflowHelpers.initState(data);
|
||||||
|
|
||||||
// Add nodes and connections
|
// Add nodes and connections
|
||||||
await addNodes(data.nodes);
|
await addNodes(data.nodes, { keepPristine: true });
|
||||||
workflowsStore.setConnections(data.connections);
|
workflowsStore.setConnections(data.connections);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1383,7 +1414,7 @@ export function useCanvasOperations({
|
||||||
historyStore.startRecordingUndo();
|
historyStore.startRecordingUndo();
|
||||||
|
|
||||||
await addNodes(Object.values(tempWorkflow.nodes));
|
await addNodes(Object.values(tempWorkflow.nodes));
|
||||||
await addConnections(
|
addConnections(
|
||||||
mapLegacyConnectionsToCanvasConnections(
|
mapLegacyConnectionsToCanvasConnections(
|
||||||
tempWorkflow.connectionsBySourceNode,
|
tempWorkflow.connectionsBySourceNode,
|
||||||
Object.values(tempWorkflow.nodes),
|
Object.values(tempWorkflow.nodes),
|
||||||
|
@ -1668,6 +1699,7 @@ export function useCanvasOperations({
|
||||||
editableWorkflow,
|
editableWorkflow,
|
||||||
editableWorkflowObject,
|
editableWorkflowObject,
|
||||||
triggerNodes,
|
triggerNodes,
|
||||||
|
requireNodeTypeDescription,
|
||||||
addNodes,
|
addNodes,
|
||||||
addNode,
|
addNode,
|
||||||
revertAddNode,
|
revertAddNode,
|
||||||
|
|
|
@ -1055,7 +1055,7 @@ export function useWorkflowHelpers(options: { router: ReturnType<typeof useRoute
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initState(workflowData: IWorkflowDb): Promise<void> {
|
function initState(workflowData: IWorkflowDb) {
|
||||||
workflowsStore.addWorkflow(workflowData);
|
workflowsStore.addWorkflow(workflowData);
|
||||||
workflowsStore.setActive(workflowData.active || false);
|
workflowsStore.setActive(workflowData.active || false);
|
||||||
workflowsStore.setWorkflowId(workflowData.id);
|
workflowsStore.setWorkflowId(workflowData.id);
|
||||||
|
|
|
@ -825,9 +825,11 @@ async function onAddNodesAndConnections(
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
await addConnections(mappedConnections);
|
addConnections(mappedConnections);
|
||||||
|
|
||||||
uiStore.resetLastInteractedWith();
|
void nextTick(() => {
|
||||||
|
uiStore.resetLastInteractedWith();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onRevertAddNode({ node }: { node: INodeUi }) {
|
async function onRevertAddNode({ node }: { node: INodeUi }) {
|
||||||
|
|
|
@ -1389,7 +1389,7 @@ export default defineComponent({
|
||||||
|
|
||||||
this.resetWorkspace();
|
this.resetWorkspace();
|
||||||
|
|
||||||
await this.workflowHelpers.initState(workflow);
|
this.workflowHelpers.initState(workflow);
|
||||||
|
|
||||||
if (workflow.sharedWithProjects) {
|
if (workflow.sharedWithProjects) {
|
||||||
this.workflowsEEStore.setWorkflowSharedWith({
|
this.workflowsEEStore.setWorkflowSharedWith({
|
||||||
|
|
|
@ -125,7 +125,7 @@ async function fetchWorkflow() {
|
||||||
try {
|
try {
|
||||||
await workflowsStore.fetchActiveWorkflows();
|
await workflowsStore.fetchActiveWorkflows();
|
||||||
const data = await workflowsStore.fetchWorkflow(workflowId.value);
|
const data = await workflowsStore.fetchWorkflow(workflowId.value);
|
||||||
await workflowHelpers.initState(data);
|
workflowHelpers.initState(data);
|
||||||
await nodeHelpers.addNodes(data.nodes, data.connections);
|
await nodeHelpers.addNodes(data.nodes, data.connections);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.showError(error, i18n.baseText('nodeView.showError.openWorkflow.title'));
|
toast.showError(error, i18n.baseText('nodeView.showError.openWorkflow.title'));
|
||||||
|
|
Loading…
Reference in a new issue