mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Use crypto.randomUUID()
to initialize node id if missing on new canvas (#11873)
This commit is contained in:
parent
3320436a6f
commit
bc4857a1b3
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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+)+/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 {
|
||||
|
@ -1187,7 +1186,7 @@ export function useNodeHelpers() {
|
|||
};
|
||||
|
||||
if (!newNode.id) {
|
||||
newNode.id = uuid();
|
||||
assignNodeId(newNode);
|
||||
}
|
||||
|
||||
nodeType = nodeTypesStore.getNodeType(newNode.type, newNode.typeVersion);
|
||||
|
@ -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,
|
||||
|
@ -1292,5 +1303,7 @@ export function useNodeHelpers() {
|
|||
addPinDataConnections,
|
||||
removePinDataConnections,
|
||||
getNodeTaskData,
|
||||
assignNodeId,
|
||||
assignWebhookId,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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: '',
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue