Skip to content

Commit a38f7e2

Browse files
michael-radencyriascho
authored andcommitted
fix(n8n Form Node): Remove dependence on static data from the form completion page (no-changelog) (#12445)
1 parent b0f5cb9 commit a38f7e2

File tree

3 files changed

+56
-45
lines changed

3 files changed

+56
-45
lines changed

Diff for: packages/nodes-base/nodes/Form/Form.node.ts

+10-32
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
WAIT_INDEFINITELY,
2020
} from 'n8n-workflow';
2121

22-
import { type CompletionPageConfig } from './interfaces';
2322
import { formDescription, formFields, formTitle } from '../Form/common.descriptions';
2423
import { prepareFormReturnItem, renderForm, resolveRawData } from '../Form/utils';
2524

@@ -273,19 +272,19 @@ export class Form extends Node {
273272
const method = context.getRequestObject().method;
274273

275274
if (operation === 'completion' && method === 'GET') {
276-
const staticData = context.getWorkflowStaticData('node');
277-
const id = `${context.getExecutionId()}-${context.getNode().name}`;
278-
const config = staticData?.[id] as CompletionPageConfig;
279-
delete staticData[id];
275+
const completionTitle = context.getNodeParameter('completionTitle', '') as string;
276+
const completionMessage = context.getNodeParameter('completionMessage', '') as string;
277+
const redirectUrl = context.getNodeParameter('redirectUrl', '') as string;
278+
const options = context.getNodeParameter('options', {}) as { formTitle: string };
280279

281-
if (config.redirectUrl) {
280+
if (redirectUrl) {
282281
res.send(
283-
`<html><head><meta http-equiv="refresh" content="0; url=${config.redirectUrl}"></head></html>`,
282+
`<html><head><meta http-equiv="refresh" content="0; url=${redirectUrl}"></head></html>`,
284283
);
285284
return { noWebhookResponse: true };
286285
}
287286

288-
let title = config.pageTitle;
287+
let title = options.formTitle;
289288
if (!title) {
290289
title = context.evaluateExpression(
291290
`{{ $('${trigger?.name}').params.formTitle }}`,
@@ -296,8 +295,8 @@ export class Form extends Node {
296295
) as boolean;
297296

298297
res.render('form-trigger-completion', {
299-
title: config.completionTitle,
300-
message: config.completionMessage,
298+
title: completionTitle,
299+
message: completionMessage,
301300
formTitle: title,
302301
appendAttribution,
303302
});
@@ -419,28 +418,7 @@ export class Form extends Node {
419418
);
420419
}
421420

422-
if (operation !== 'completion') {
423-
await context.putExecutionToWait(WAIT_INDEFINITELY);
424-
} else {
425-
const staticData = context.getWorkflowStaticData('node');
426-
const completionTitle = context.getNodeParameter('completionTitle', 0, '') as string;
427-
const completionMessage = context.getNodeParameter('completionMessage', 0, '') as string;
428-
const redirectUrl = context.getNodeParameter('redirectUrl', 0, '') as string;
429-
const options = context.getNodeParameter('options', 0, {}) as { formTitle: string };
430-
const id = `${context.getExecutionId()}-${context.getNode().name}`;
431-
432-
const config: CompletionPageConfig = {
433-
completionTitle,
434-
completionMessage,
435-
redirectUrl,
436-
pageTitle: options.formTitle,
437-
};
438-
439-
staticData[id] = config;
440-
441-
const waitTill = new Date(WAIT_INDEFINITELY);
442-
await context.putExecutionToWait(waitTill);
443-
}
421+
await context.putExecutionToWait(WAIT_INDEFINITELY);
444422

445423
return [context.getInputData()];
446424
}

Diff for: packages/nodes-base/nodes/Form/interfaces.ts

-7
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,4 @@ export type FormTriggerData = {
3232
buttonLabel?: string;
3333
};
3434

35-
export type CompletionPageConfig = {
36-
pageTitle?: string;
37-
completionMessage?: string;
38-
completionTitle?: string;
39-
redirectUrl?: string;
40-
};
41-
4235
export const FORM_TRIGGER_AUTHENTICATION_PROPERTY = 'authentication';

Diff for: packages/nodes-base/nodes/Form/test/Form.node.test.ts

+46-6
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ describe('Form Node', () => {
172172
]);
173173
});
174174

175-
it('should handle completion operation', async () => {
175+
it('should handle completion operation and render completion page', async () => {
176176
mockWebhookFunctions.getRequestObject.mockReturnValue({ method: 'GET' } as Request);
177177
mockWebhookFunctions.getNodeParameter.mockImplementation((paramName) => {
178178
if (paramName === 'operation') return 'completion';
@@ -181,6 +181,7 @@ describe('Form Node', () => {
181181
if (paramName === 'respondWith') return 'text';
182182
if (paramName === 'completionTitle') return 'Test Title';
183183
if (paramName === 'completionMessage') return 'Test Message';
184+
if (paramName === 'redirectUrl') return '';
184185
return {};
185186
});
186187
mockWebhookFunctions.getParentNodes.mockReturnValue([
@@ -202,16 +203,55 @@ describe('Form Node', () => {
202203
);
203204
mockWebhookFunctions.getNode.mockReturnValue(mock<INode>({ name: formCompletionNodeName }));
204205
mockWebhookFunctions.getExecutionId.mockReturnValue(testExecutionId);
205-
mockWebhookFunctions.getWorkflowStaticData.mockReturnValue({
206-
[`${testExecutionId}-${formCompletionNodeName}`]: { redirectUrl: '' },
206+
207+
const result = await form.webhook(mockWebhookFunctions);
208+
209+
expect(result).toEqual({ noWebhookResponse: true });
210+
expect(mockResponseObject.render).toHaveBeenCalledWith('form-trigger-completion', {
211+
appendAttribution: 'test',
212+
formTitle: 'test',
213+
message: 'Test Message',
214+
title: 'Test Title',
207215
});
216+
});
217+
218+
it('should handle completion operation and redirect', async () => {
219+
mockWebhookFunctions.getRequestObject.mockReturnValue({ method: 'GET' } as Request);
220+
mockWebhookFunctions.getNodeParameter.mockImplementation((paramName) => {
221+
if (paramName === 'operation') return 'completion';
222+
if (paramName === 'useJson') return false;
223+
if (paramName === 'jsonOutput') return '[]';
224+
if (paramName === 'respondWith') return 'text';
225+
if (paramName === 'completionTitle') return 'Test Title';
226+
if (paramName === 'completionMessage') return 'Test Message';
227+
if (paramName === 'redirectUrl') return 'https://n8n.io';
228+
return {};
229+
});
230+
mockWebhookFunctions.getParentNodes.mockReturnValue([
231+
{
232+
type: 'n8n-nodes-base.formTrigger',
233+
name: 'Form Trigger',
234+
typeVersion: 2.1,
235+
disabled: false,
236+
},
237+
]);
238+
mockWebhookFunctions.evaluateExpression.mockReturnValue('test');
239+
240+
const mockResponseObject = {
241+
render: jest.fn(),
242+
redirect: jest.fn(),
243+
send: jest.fn(),
244+
};
245+
mockWebhookFunctions.getResponseObject.mockReturnValue(
246+
mockResponseObject as unknown as Response,
247+
);
248+
mockWebhookFunctions.getNode.mockReturnValue(mock<INode>({ name: formCompletionNodeName }));
208249

209250
const result = await form.webhook(mockWebhookFunctions);
210251

211252
expect(result).toEqual({ noWebhookResponse: true });
212-
expect(mockResponseObject.render).toHaveBeenCalledWith(
213-
'form-trigger-completion',
214-
expect.any(Object),
253+
expect(mockResponseObject.send).toHaveBeenCalledWith(
254+
'<html><head><meta http-equiv="refresh" content="0; url=https://n8n.io"></head></html>',
215255
);
216256
});
217257
});

0 commit comments

Comments
 (0)