mirror of
https://github.com/n8n-io/n8n.git
synced 2026-06-02 09:47:00 +02:00
fix(editor): Preserve custom Form Trigger path on workflow re-import (#30053)
This commit is contained in:
parent
f1fd79f830
commit
c3cf5c7057
|
|
@ -4680,7 +4680,7 @@ describe('useCanvasOperations', () => {
|
|||
});
|
||||
|
||||
it.each(UPDATE_WEBHOOK_ID_NODE_TYPES)(
|
||||
'should regenerate webhook ids for node type "%s" on pasting into canvas',
|
||||
'should regenerate webhook ids for node type "%s" on pasting into canvas and sync default paths',
|
||||
async (type) => {
|
||||
// This mock is needed for addImportedNodesToWorkflow to work
|
||||
vi.mocked(workflowDocumentStoreInstance.createWorkflowObject).mockReturnValue({
|
||||
|
|
@ -4699,7 +4699,7 @@ describe('useCanvasOperations', () => {
|
|||
position: [200, 200] as [number, number],
|
||||
webhookId: 'first-webhook',
|
||||
parameters: {
|
||||
path: 'some-path',
|
||||
path: 'first-webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -4711,7 +4711,7 @@ describe('useCanvasOperations', () => {
|
|||
webhookId: 'second-webhook',
|
||||
parameters: {
|
||||
options: {
|
||||
path: 'some-path',
|
||||
path: 'second-webhook',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -4728,21 +4728,200 @@ describe('useCanvasOperations', () => {
|
|||
|
||||
const canvasOperations = useCanvasOperations();
|
||||
|
||||
// This should not throw even when nodes can't be added due to maxNodes limit
|
||||
const workflow = await canvasOperations.importWorkflowData(workflowDataToImport, 'paste');
|
||||
|
||||
expect(workflow.nodes).toHaveLength(2);
|
||||
expect(workflow.nodes![0].name).toBe('Execute Workflow Trigger 1');
|
||||
expect(workflow.nodes![0].webhookId).not.toBe('first-webhook');
|
||||
expect(workflow.nodes![0].parameters.path).not.toBe('some-path');
|
||||
expect(workflow.nodes![0].parameters.path).toBe(workflow.nodes![0].webhookId);
|
||||
expect(workflow.nodes![1].name).toBe('Execute Workflow Trigger 2');
|
||||
expect(workflow.nodes![1].webhookId).not.toBe('second-webhook');
|
||||
expect((workflow.nodes![1].parameters.options as { path: string }).path).not.toBe(
|
||||
'some-path',
|
||||
expect((workflow.nodes![1].parameters.options as { path: string }).path).toBe(
|
||||
workflow.nodes![1].webhookId,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it.each(UPDATE_WEBHOOK_ID_NODE_TYPES)(
|
||||
'should preserve user-defined paths for node type "%s" on importing into canvas',
|
||||
async (type) => {
|
||||
// This mock is needed for addImportedNodesToWorkflow to work
|
||||
vi.mocked(workflowDocumentStoreInstance.createWorkflowObject).mockReturnValue({
|
||||
nodes: {},
|
||||
connections: {},
|
||||
connectionsBySourceNode: {},
|
||||
renameNode: vi.fn(),
|
||||
} as unknown as Workflow);
|
||||
|
||||
const nodesToImport = [
|
||||
{
|
||||
id: 'import-1',
|
||||
name: 'Execute Workflow Trigger 1',
|
||||
type,
|
||||
typeVersion: 1,
|
||||
position: [200, 200] as [number, number],
|
||||
webhookId: 'first-webhook',
|
||||
parameters: {
|
||||
path: 'my-custom-path',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'import-2',
|
||||
name: 'Execute Workflow Trigger 2',
|
||||
type,
|
||||
typeVersion: 1,
|
||||
position: [300, 300] as [number, number],
|
||||
webhookId: 'second-webhook',
|
||||
parameters: {
|
||||
options: {
|
||||
path: 'my-custom-form',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const workflowDataToImport = {
|
||||
nodes: nodesToImport,
|
||||
connections: {},
|
||||
};
|
||||
|
||||
const canvasOperations = useCanvasOperations();
|
||||
|
||||
const workflow = await canvasOperations.importWorkflowData(workflowDataToImport, 'file');
|
||||
|
||||
expect(workflow.nodes).toHaveLength(2);
|
||||
expect(workflow.nodes![0].webhookId).not.toBe('first-webhook');
|
||||
expect(workflow.nodes![0].parameters.path).toBe('my-custom-path');
|
||||
expect(workflow.nodes![1].webhookId).not.toBe('second-webhook');
|
||||
expect((workflow.nodes![1].parameters.options as { path: string }).path).toBe(
|
||||
'my-custom-form',
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it.each(UPDATE_WEBHOOK_ID_NODE_TYPES)(
|
||||
'should preserve user-defined paths for node type "%s" on pasting into canvas',
|
||||
async (type) => {
|
||||
vi.mocked(workflowDocumentStoreInstance.createWorkflowObject).mockReturnValue({
|
||||
nodes: {},
|
||||
connections: {},
|
||||
connectionsBySourceNode: {},
|
||||
renameNode: vi.fn(),
|
||||
} as unknown as Workflow);
|
||||
|
||||
const nodesToImport = [
|
||||
{
|
||||
id: 'import-1',
|
||||
name: 'Execute Workflow Trigger 1',
|
||||
type,
|
||||
typeVersion: 1,
|
||||
position: [200, 200] as [number, number],
|
||||
webhookId: 'first-webhook',
|
||||
parameters: {
|
||||
options: {
|
||||
path: 'my-custom-form',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const workflowDataToImport = {
|
||||
nodes: nodesToImport,
|
||||
connections: {},
|
||||
};
|
||||
|
||||
const canvasOperations = useCanvasOperations();
|
||||
|
||||
const workflow = await canvasOperations.importWorkflowData(workflowDataToImport, 'paste');
|
||||
|
||||
expect(workflow.nodes).toHaveLength(1);
|
||||
expect(workflow.nodes![0].webhookId).not.toBe('first-webhook');
|
||||
expect((workflow.nodes![0].parameters.options as { path: string }).path).toBe(
|
||||
'my-custom-form',
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it.each(UPDATE_WEBHOOK_ID_NODE_TYPES)(
|
||||
'should regenerate webhook id for node type "%s" when no path is defined',
|
||||
async (type) => {
|
||||
vi.mocked(workflowDocumentStoreInstance.createWorkflowObject).mockReturnValue({
|
||||
nodes: {},
|
||||
connections: {},
|
||||
connectionsBySourceNode: {},
|
||||
renameNode: vi.fn(),
|
||||
} as unknown as Workflow);
|
||||
|
||||
const nodesToImport = [
|
||||
{
|
||||
id: 'import-1',
|
||||
name: 'Execute Workflow Trigger 1',
|
||||
type,
|
||||
typeVersion: 1,
|
||||
position: [200, 200] as [number, number],
|
||||
webhookId: 'first-webhook',
|
||||
parameters: {},
|
||||
},
|
||||
];
|
||||
|
||||
const workflowDataToImport = {
|
||||
nodes: nodesToImport,
|
||||
connections: {},
|
||||
};
|
||||
|
||||
const canvasOperations = useCanvasOperations();
|
||||
|
||||
const workflow = await canvasOperations.importWorkflowData(workflowDataToImport, 'paste');
|
||||
|
||||
expect(workflow.nodes).toHaveLength(1);
|
||||
expect(workflow.nodes![0].webhookId).not.toBe('first-webhook');
|
||||
expect(workflow.nodes![0].parameters.path).toBeUndefined();
|
||||
expect(workflow.nodes![0].parameters.options).toBeUndefined();
|
||||
},
|
||||
);
|
||||
|
||||
it.each(UPDATE_WEBHOOK_ID_NODE_TYPES)(
|
||||
'should preserve user-defined parameters.path even when it equals the empty string for node type "%s"',
|
||||
async (type) => {
|
||||
vi.mocked(workflowDocumentStoreInstance.createWorkflowObject).mockReturnValue({
|
||||
nodes: {},
|
||||
connections: {},
|
||||
connectionsBySourceNode: {},
|
||||
renameNode: vi.fn(),
|
||||
} as unknown as Workflow);
|
||||
|
||||
const nodesToImport = [
|
||||
{
|
||||
id: 'import-1',
|
||||
name: 'Execute Workflow Trigger 1',
|
||||
type,
|
||||
typeVersion: 1,
|
||||
position: [200, 200] as [number, number],
|
||||
webhookId: 'first-webhook',
|
||||
parameters: {
|
||||
options: {
|
||||
path: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const workflowDataToImport = {
|
||||
nodes: nodesToImport,
|
||||
connections: {},
|
||||
};
|
||||
|
||||
const canvasOperations = useCanvasOperations();
|
||||
|
||||
const workflow = await canvasOperations.importWorkflowData(workflowDataToImport, 'file');
|
||||
|
||||
expect(workflow.nodes).toHaveLength(1);
|
||||
expect(workflow.nodes![0].webhookId).not.toBe('first-webhook');
|
||||
// Empty options.path is not equal to previous webhookId, so it should not be replaced
|
||||
expect((workflow.nodes![0].parameters.options as { path: string }).path).toBe('');
|
||||
},
|
||||
);
|
||||
|
||||
it.each([WEBHOOK_NODE_TYPE, MCP_TRIGGER_NODE_TYPE])(
|
||||
'should not regenerate webhook ids for node type "%s" on pasting into canvas',
|
||||
async (type) => {
|
||||
|
|
|
|||
|
|
@ -2672,14 +2672,17 @@ export function useCanvasOperations() {
|
|||
|
||||
// Generate new webhookId
|
||||
if (node.webhookId && UPDATE_WEBHOOK_ID_NODE_TYPES.includes(node.type)) {
|
||||
if (node.webhookId) {
|
||||
nodeHelpers.assignWebhookId(node);
|
||||
const previousWebhookId = node.webhookId;
|
||||
const pathMatchedWebhookId = node.parameters.path === previousWebhookId;
|
||||
const optionsPathMatchedWebhookId =
|
||||
(node.parameters.options as IDataObject)?.path === previousWebhookId;
|
||||
|
||||
if (node.parameters.path) {
|
||||
node.parameters.path = node.webhookId;
|
||||
} else if ((node.parameters.options as IDataObject)?.path) {
|
||||
(node.parameters.options as IDataObject).path = node.webhookId;
|
||||
}
|
||||
nodeHelpers.assignWebhookId(node);
|
||||
|
||||
if (pathMatchedWebhookId) {
|
||||
node.parameters.path = node.webhookId;
|
||||
} else if (optionsPathMatchedWebhookId) {
|
||||
(node.parameters.options as IDataObject).path = node.webhookId;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user