Skip to content

Commit

Permalink
fix(editor): Use crypto.randomUUID() to initialize node id if missi…
Browse files Browse the repository at this point in the history
…ng on new canvas (#11873)
  • Loading branch information
alexgrozav authored Nov 26, 2024
1 parent 3320436 commit bc4857a
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 21 deletions.
17 changes: 8 additions & 9 deletions packages/editor-ui/src/composables/useCanvasOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ import type {
Workflow,
} from 'n8n-workflow';
import { deepCopy, NodeConnectionType, NodeHelpers, TelemetryHelpers } from 'n8n-workflow';
import { v4 as uuid } from 'uuid';
import { computed, nextTick, ref } from 'vue';
import type { useRouter } from 'vue-router';
import { useClipboard } from '@/composables/useClipboard';
Expand Down Expand Up @@ -759,7 +758,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
node: AddNodeDataWithTypeVersion,
nodeTypeDescription: INodeTypeDescription,
) {
const id = node.id ?? uuid();
const id = node.id ?? nodeHelpers.assignNodeId(node as INodeUi);
const name = node.name ?? (nodeTypeDescription.defaults.name as string);
const type = nodeTypeDescription.name;
const typeVersion = node.typeVersion;
Expand Down Expand Up @@ -1027,7 +1026,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR

function resolveNodeWebhook(node: INodeUi, nodeTypeDescription: INodeTypeDescription) {
if (nodeTypeDescription.webhooks?.length && !node.webhookId) {
node.webhookId = uuid();
nodeHelpers.assignWebhookId(node);
}

// if it's a webhook and the path is empty set the UUID as the default path
Expand Down Expand Up @@ -1613,7 +1612,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
(n) => n.webhookId === node.webhookId,
);
if (isDuplicate) {
node.webhookId = uuid();
nodeHelpers.assignWebhookId(node);

if (node.parameters.path) {
node.parameters.path = node.webhookId as string;
Expand All @@ -1623,13 +1622,13 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
}
}

// set all new ids when pasting/importing workflows
// Set all new ids when pasting/importing workflows
if (node.id) {
const newId = uuid();
nodeIdMap[newId] = node.id;
node.id = newId;
const previousId = node.id;
const newId = nodeHelpers.assignNodeId(node);
nodeIdMap[newId] = previousId;
} else {
node.id = uuid();
nodeHelpers.assignNodeId(node);
}
});
}
Expand Down
26 changes: 26 additions & 0 deletions packages/editor-ui/src/composables/useNodeHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,30 @@ describe('useNodeHelpers()', () => {
},
);
});

describe('assignNodeId()', () => {
it('should assign a unique id to the node', () => {
const { assignNodeId } = useNodeHelpers();
const node = createTestNode({
id: '',
});

assignNodeId(node);
expect(node.id).not.toBe('');
expect(node.id).toMatch(/\w+(-\w+)+/);
});
});

describe('assignWebhookId', () => {
it('should assign a unique id to the webhook', () => {
const { assignWebhookId } = useNodeHelpers();
const webhook = createTestNode({
id: '',
});

assignWebhookId(webhook);
expect(webhook.webhookId).not.toBe('');
expect(webhook.webhookId).toMatch(/\w+(-\w+)+/);
});
});
});
17 changes: 15 additions & 2 deletions packages/editor-ui/src/composables/useNodeHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ref, nextTick } from 'vue';
import { useRoute } from 'vue-router';
import { v4 as uuid } from 'uuid';
import type { Connection, ConnectionDetachedParams } from '@jsplumb/core';
import { useHistoryStore } from '@/stores/history.store';
import {
Expand Down Expand Up @@ -1187,7 +1186,7 @@ export function useNodeHelpers() {
};

if (!newNode.id) {
newNode.id = uuid();
assignNodeId(newNode);
}

nodeType = nodeTypesStore.getNodeType(newNode.type, newNode.typeVersion);
Expand Down Expand Up @@ -1257,6 +1256,18 @@ export function useNodeHelpers() {
canvasStore.jsPlumbInstance?.setSuspendDrawing(false, true);
}

function assignNodeId(node: INodeUi) {
const id = window.crypto.randomUUID();
node.id = id;
return id;
}

function assignWebhookId(node: INodeUi) {
const id = window.crypto.randomUUID();
node.webhookId = id;
return id;
}

return {
hasProxyAuth,
isCustomApiCallSelected,
Expand Down Expand Up @@ -1292,5 +1303,7 @@ export function useNodeHelpers() {
addPinDataConnections,
removePinDataConnections,
getNodeTaskData,
assignNodeId,
assignWebhookId,
};
}
9 changes: 3 additions & 6 deletions packages/editor-ui/src/composables/useWorkflowHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ import { useTemplatesStore } from '@/stores/templates.store';
import { useUIStore } from '@/stores/ui.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { getSourceItems } from '@/utils/pairedItemUtils';
import { v4 as uuid } from 'uuid';
import { useSettingsStore } from '@/stores/settings.store';
import { getCredentialTypeName, isCredentialOnlyNodeType } from '@/utils/credentialOnlyNodes';
import { useDocumentTitle } from '@/composables/useDocumentTitle';
Expand All @@ -64,13 +63,12 @@ import { useCanvasStore } from '@/stores/canvas.store';
import { useSourceControlStore } from '@/stores/sourceControl.store';
import { tryToParseNumber } from '@/utils/typesUtils';
import { useI18n } from '@/composables/useI18n';
import type { useRouter } from 'vue-router';
import type { useRouter, NavigationGuardNext } from 'vue-router';
import { useTelemetry } from '@/composables/useTelemetry';
import { useProjectsStore } from '@/stores/projects.store';
import { useTagsStore } from '@/stores/tags.store';
import { useWorkflowsEEStore } from '@/stores/workflows.ee.store';
import { useNpsSurveyStore } from '@/stores/npsSurvey.store';
import type { NavigationGuardNext } from 'vue-router';

type ResolveParameterOptions = {
targetItem?: TargetItem;
Expand Down Expand Up @@ -937,7 +935,7 @@ export function useWorkflowHelpers(options: { router: ReturnType<typeof useRoute

if (resetNodeIds) {
workflowDataRequest.nodes = workflowDataRequest.nodes!.map((node) => {
node.id = uuid();
nodeHelpers.assignNodeId(node);

return node;
});
Expand All @@ -946,8 +944,7 @@ export function useWorkflowHelpers(options: { router: ReturnType<typeof useRoute
if (resetWebhookUrls) {
workflowDataRequest.nodes = workflowDataRequest.nodes!.map((node) => {
if (node.webhookId) {
const newId = uuid();
node.webhookId = newId;
const newId = nodeHelpers.assignWebhookId(node);
node.parameters.path = newId;
changedNodes[node.name] = node.webhookId;
}
Expand Down
15 changes: 11 additions & 4 deletions packages/editor-ui/src/stores/workflows.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import { useRouter } from 'vue-router';
import { useSettingsStore } from './settings.store';
import { openPopUpWindow } from '@/utils/executionUtils';
import { useNodeHelpers } from '@/composables/useNodeHelpers';

const defaults: Omit<IWorkflowDb, 'id'> & { settings: NonNullable<IWorkflowDb['settings']> } = {
name: '',
Expand Down Expand Up @@ -117,6 +118,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
const workflowHelpers = useWorkflowHelpers({ router });
const settingsStore = useSettingsStore();
const rootStore = useRootStore();
const nodeHelpers = useNodeHelpers();

// -1 means the backend chooses the default
// 0 is the old flow
Expand Down Expand Up @@ -1037,10 +1039,15 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {

function setNodes(nodes: INodeUi[]): void {
workflow.value.nodes = nodes;
nodeMetadata.value = nodes.reduce<NodeMetadataMap>((acc, node) => {
acc[node.name] = { pristine: true };
return acc;
}, {});
nodes.forEach((node) => {
if (!node.id) {
nodeHelpers.assignNodeId(node);
}

if (!nodeMetadata.value[node.name]) {
nodeMetadata.value[node.name] = { pristine: true };
}
});
}

function setConnections(connections: IConnections, updateWorkflow = false): void {
Expand Down

0 comments on commit bc4857a

Please sign in to comment.