Refactor rendering of Form Node and Form Completion Node

This commit is contained in:
Dana Lee 2025-01-27 16:31:38 +01:00
parent 0c483a2551
commit 09409ad9f0
No known key found for this signature in database
3 changed files with 128 additions and 95 deletions

View file

@ -5,6 +5,7 @@ import type {
INodeProperties,
INodeTypeDescription,
IWebhookFunctions,
IWebhookResponseData,
NodeTypeAndVersion,
} from 'n8n-workflow';
import {
@ -15,12 +16,13 @@ import {
FORM_TRIGGER_NODE_TYPE,
tryToParseJsonToFormFields,
NodeConnectionType,
WAIT_NODE_TYPE,
WAIT_INDEFINITELY,
} from 'n8n-workflow';
import { renderFormCompletion } from './formCompletionUtils';
import { renderFormNode } from './formNodeUtils';
import { formDescription, formFields, formTitle } from '../Form/common.descriptions';
import { prepareFormReturnItem, renderForm, resolveRawData } from '../Form/utils';
import { prepareFormReturnItem, resolveRawData } from '../Form/utils';
export const formFieldsProperties: INodeProperties[] = [
{
@ -235,7 +237,7 @@ export class Form extends Node {
],
};
async webhook(context: IWebhookFunctions) {
async webhook(context: IWebhookFunctions): Promise<IWebhookResponseData> {
const res = context.getResponseObject();
const operation = context.getNodeParameter('operation', '') as string;
@ -280,36 +282,7 @@ export class Form extends Node {
const method = context.getRequestObject().method;
if (operation === 'completion' && method === 'GET') {
const completionTitle = context.getNodeParameter('completionTitle', '') as string;
const completionMessage = context.getNodeParameter('completionMessage', '') as string;
const redirectUrl = context.getNodeParameter('redirectUrl', '') as string;
const options = context.getNodeParameter('options', {}) as { formTitle: string };
if (redirectUrl) {
res.send(
`<html><head><meta http-equiv="refresh" content="0; url=${redirectUrl}"></head></html>`,
);
return { noWebhookResponse: true };
}
let title = options.formTitle;
if (!title) {
title = context.evaluateExpression(
`{{ $('${trigger?.name}').params.formTitle }}`,
) as string;
}
const appendAttribution = context.evaluateExpression(
`{{ $('${trigger?.name}').params.options?.appendAttribution === false ? false : true }}`,
) as boolean;
res.render('form-trigger-completion', {
title: completionTitle,
message: completionMessage,
formTitle: title,
appendAttribution,
});
return { noWebhookResponse: true };
return await renderFormCompletion(context, res, trigger);
}
if (operation === 'completion' && method === 'POST') {
@ -319,68 +292,7 @@ export class Form extends Node {
}
if (method === 'GET') {
const options = context.getNodeParameter('options', {}) as {
formTitle: string;
formDescription: string;
buttonLabel: string;
};
let title = options.formTitle;
if (!title) {
title = context.evaluateExpression(
`{{ $('${trigger?.name}').params.formTitle }}`,
) as string;
}
let description = options.formDescription;
if (!description) {
description = context.evaluateExpression(
`{{ $('${trigger?.name}').params.formDescription }}`,
) as string;
}
let buttonLabel = options.buttonLabel;
if (!buttonLabel) {
buttonLabel =
(context.evaluateExpression(
`{{ $('${trigger?.name}').params.options?.buttonLabel }}`,
) as string) || 'Submit';
}
const responseMode = 'onReceived';
let redirectUrl;
const connectedNodes = context.getChildNodes(context.getNode().name);
const hasNextPage = connectedNodes.some(
(node) => !node.disabled && (node.type === FORM_NODE_TYPE || node.type === WAIT_NODE_TYPE),
);
if (hasNextPage) {
redirectUrl = context.evaluateExpression('{{ $execution.resumeFormUrl }}') as string;
}
const appendAttribution = context.evaluateExpression(
`{{ $('${trigger?.name}').params.options?.appendAttribution === false ? false : true }}`,
) as boolean;
renderForm({
context,
res,
formTitle: title,
formDescription: description,
formFields: fields,
responseMode,
mode,
redirectUrl,
appendAttribution,
buttonLabel,
});
return {
noWebhookResponse: true,
};
return await renderFormNode(context, res, trigger, fields, mode);
}
let useWorkflowTimezone = context.evaluateExpression(

View file

@ -0,0 +1,41 @@
import { type Response } from 'express';
import {
type NodeTypeAndVersion,
type IWebhookFunctions,
type IWebhookResponseData,
} from 'n8n-workflow';
export const renderFormCompletion = async (
context: IWebhookFunctions,
res: Response,
trigger: NodeTypeAndVersion,
): Promise<IWebhookResponseData> => {
const completionTitle = context.getNodeParameter('completionTitle', '') as string;
const completionMessage = context.getNodeParameter('completionMessage', '') as string;
const redirectUrl = context.getNodeParameter('redirectUrl', '') as string;
const options = context.getNodeParameter('options', {}) as { formTitle: string };
if (redirectUrl) {
res.send(
`<html><head><meta http-equiv="refresh" content="0; url=${redirectUrl}"></head></html>`,
);
return { noWebhookResponse: true };
}
let title = options.formTitle;
if (!title) {
title = context.evaluateExpression(`{{ $('${trigger?.name}').params.formTitle }}`) as string;
}
const appendAttribution = context.evaluateExpression(
`{{ $('${trigger?.name}').params.options?.appendAttribution === false ? false : true }}`,
) as boolean;
res.render('form-trigger-completion', {
title: completionTitle,
message: completionMessage,
formTitle: title,
appendAttribution,
});
return { noWebhookResponse: true };
};

View file

@ -0,0 +1,80 @@
import { type Response } from 'express';
import {
type NodeTypeAndVersion,
type IWebhookFunctions,
FORM_NODE_TYPE,
WAIT_NODE_TYPE,
type FormFieldsParameter,
type IWebhookResponseData,
} from 'n8n-workflow';
import { renderForm } from './utils';
export const renderFormNode = async (
context: IWebhookFunctions,
res: Response,
trigger: NodeTypeAndVersion,
fields: FormFieldsParameter,
mode: 'test' | 'production',
): Promise<IWebhookResponseData> => {
const options = context.getNodeParameter('options', {}) as {
formTitle: string;
formDescription: string;
buttonLabel: string;
};
let title = options.formTitle;
if (!title) {
title = context.evaluateExpression(`{{ $('${trigger?.name}').params.formTitle }}`) as string;
}
let description = options.formDescription;
if (!description) {
description = context.evaluateExpression(
`{{ $('${trigger?.name}').params.formDescription }}`,
) as string;
}
let buttonLabel = options.buttonLabel;
if (!buttonLabel) {
buttonLabel =
(context.evaluateExpression(
`{{ $('${trigger?.name}').params.options?.buttonLabel }}`,
) as string) || 'Submit';
}
const responseMode = 'onReceived';
let redirectUrl;
const connectedNodes = context.getChildNodes(context.getNode().name);
const hasNextPage = connectedNodes.some(
(node) => !node.disabled && (node.type === FORM_NODE_TYPE || node.type === WAIT_NODE_TYPE),
);
if (hasNextPage) {
redirectUrl = context.evaluateExpression('{{ $execution.resumeFormUrl }}') as string;
}
const appendAttribution = context.evaluateExpression(
`{{ $('${trigger?.name}').params.options?.appendAttribution === false ? false : true }}`,
) as boolean;
renderForm({
context,
res,
formTitle: title,
formDescription: description,
formFields: fields,
responseMode,
mode,
redirectUrl,
appendAttribution,
buttonLabel,
});
return {
noWebhookResponse: true,
};
};