fix(editor): Preserve expression in webhook path when duplicating a workflow (#23140)

This commit is contained in:
Suguru Inoue
2025-12-15 12:20:45 +01:00
committed by GitHub
parent be6d68408d
commit ec0c87dc32
2 changed files with 56 additions and 2 deletions
@@ -350,6 +350,56 @@ describe('useWorkflowSaving', () => {
expect(webHookIdsPreSave).not.toEqual(webHookIdsPostSave);
expect(pathsPreSave).not.toEqual(pathsPostSave);
});
it('should preserve expression-based webhook paths when resetWebhookUrls is true', async () => {
const workflow: WorkflowDataUpdate = {
name: 'Expression webhook test',
active: false,
nodes: [
{
parameters: {
path: '={{ $json.customPath }}',
options: {},
},
id: 'node-with-expression',
name: 'Webhook with expression',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
position: [680, 20],
webhookId: 'original-webhook-id-1',
},
{
parameters: {
path: 'static-path',
options: {},
},
id: 'node-without-expression',
name: 'Webhook with static path',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
position: [700, 40],
webhookId: 'original-webhook-id-2',
},
],
connections: {},
};
const { saveAsNewWorkflow } = useWorkflowSaving({ router });
const expressionPath = workflow.nodes![0].parameters.path;
const staticPath = workflow.nodes![1].parameters.path;
await saveAsNewWorkflow({
name: workflow.name,
resetWebhookUrls: true,
data: workflow,
});
// Expression-based path should be preserved
expect(workflow.nodes![0].parameters.path).toBe(expressionPath);
// Static path should be replaced with new webhook ID
expect(workflow.nodes![1].parameters.path).not.toBe(staticPath);
expect(workflow.nodes![1].parameters.path).toBe(workflow.nodes![1].webhookId);
});
});
describe('saveCurrentWorkflow', () => {
@@ -17,7 +17,7 @@ import { useCanvasStore } from '@/app/stores/canvas.store';
import type { IUpdateInformation, IWorkflowDb } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import type { WorkflowDataCreate, WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
import type { IDataObject, IWorkflowSettings } from 'n8n-workflow';
import { isExpression, type IDataObject, type IWorkflowSettings } from 'n8n-workflow';
import { useToast } from './useToast';
import { useExternalHooks } from './useExternalHooks';
import { useTelemetry } from './useTelemetry';
@@ -298,7 +298,11 @@ export function useWorkflowSaving({
workflowDataRequest.nodes = workflowDataRequest.nodes!.map((node) => {
if (node.webhookId) {
const newId = nodeHelpers.assignWebhookId(node);
node.parameters.path = newId;
if (!isExpression(node.parameters.path)) {
node.parameters.path = newId;
}
changedNodes[node.name] = node.webhookId;
}
return node;