1
1
mirror of https://github.com/n8n-io/n8n.git synced 2024-10-07 18:18:19 +03:00
n8n/packages/nodes-base/nodes/Form/utils.ts

203 lines
5.5 KiB
TypeScript

import {
NodeOperationError,
jsonParse,
type IDataObject,
type IWebhookFunctions,
} from 'n8n-workflow';
import type { FormField, FormTriggerData, FormTriggerInput } from './interfaces';
export const prepareFormData = (
formTitle: string,
formDescription: string,
formSubmittedText: string | undefined,
redirectUrl: string | undefined,
formFields: FormField[],
testRun: boolean,
instanceId?: string,
useResponseData?: boolean,
appendAttribution = true,
) => {
const validForm = formFields.length > 0;
const utm_campaign = instanceId ? `&utm_campaign=${instanceId}` : '';
const n8nWebsiteLink = `https://n8n.io/?utm_source=n8n-internal&utm_medium=form-trigger${utm_campaign}`;
if (formSubmittedText === undefined) {
formSubmittedText = 'Your response has been recorded';
}
const formData: FormTriggerData = {
testRun,
validForm,
formTitle,
formDescription,
formSubmittedText,
n8nWebsiteLink,
formFields: [],
useResponseData,
appendAttribution,
};
if (redirectUrl) {
if (!redirectUrl.includes('://')) {
redirectUrl = `http://${redirectUrl}`;
}
formData.redirectUrl = redirectUrl;
}
if (!validForm) {
return formData;
}
for (const [index, field] of formFields.entries()) {
const { fieldType, requiredField, multiselect } = field;
const input: IDataObject = {
id: `field-${index}`,
errorId: `error-field-${index}`,
label: field.fieldLabel,
inputRequired: requiredField ? 'form-required' : '',
};
if (multiselect) {
input.isMultiSelect = true;
input.multiSelectOptions =
field.fieldOptions?.values.map((e, i) => ({
id: `option${i}`,
label: e.option,
})) ?? [];
} else if (fieldType === 'dropdown') {
input.isSelect = true;
const fieldOptions = field.fieldOptions?.values ?? [];
input.selectOptions = fieldOptions.map((e) => e.option);
} else if (fieldType === 'textarea') {
input.isTextarea = true;
} else {
input.isInput = true;
input.type = fieldType as 'text' | 'number' | 'date';
}
formData.formFields.push(input as FormTriggerInput);
}
return formData;
};
const checkResponseModeConfiguration = (context: IWebhookFunctions) => {
const responseMode = context.getNodeParameter('responseMode', 'onReceived') as string;
const connectedNodes = context.getChildNodes(context.getNode().name);
const isRespondToWebhookConnected = connectedNodes.some(
(node) => node.type === 'n8n-nodes-base.respondToWebhook',
);
if (!isRespondToWebhookConnected && responseMode === 'responseNode') {
throw new NodeOperationError(
context.getNode(),
new Error('No Respond to Webhook node found in the workflow'),
{
description:
'Insert a Respond to Webhook node to your workflow to respond to the form submission or choose another option for the “Respond When” parameter',
},
);
}
if (isRespondToWebhookConnected && responseMode !== 'responseNode') {
throw new NodeOperationError(
context.getNode(),
new Error(`${context.getNode().name} node not correctly configured`),
{
description:
'Set the “Respond When” parameter to “Using Respond to Webhook Node” or remove the Respond to Webhook node',
},
);
}
};
export async function formWebhook(context: IWebhookFunctions) {
const mode = context.getMode() === 'manual' ? 'test' : 'production';
const formFields = context.getNodeParameter('formFields.values', []) as FormField[];
const method = context.getRequestObject().method;
checkResponseModeConfiguration(context);
//Show the form on GET request
if (method === 'GET') {
const formTitle = context.getNodeParameter('formTitle', '') as string;
const formDescription = context.getNodeParameter('formDescription', '') as string;
const instanceId = context.getInstanceId();
const responseMode = context.getNodeParameter('responseMode', '') as string;
const options = context.getNodeParameter('options', {}) as IDataObject;
let formSubmittedText;
let redirectUrl;
let appendAttribution = true;
if (options.respondWithOptions) {
const values = (options.respondWithOptions as IDataObject).values as IDataObject;
if (values.respondWith === 'text') {
formSubmittedText = values.formSubmittedText as string;
}
if (values.respondWith === 'redirect') {
redirectUrl = values.redirectUrl as string;
}
} else {
formSubmittedText = options.formSubmittedText as string;
}
if (options.appendAttribution === false) {
appendAttribution = false;
}
const useResponseData = responseMode === 'responseNode';
const data = prepareFormData(
formTitle,
formDescription,
formSubmittedText,
redirectUrl,
formFields,
mode === 'test',
instanceId,
useResponseData,
appendAttribution,
);
const res = context.getResponseObject();
res.render('form-trigger', data);
return {
noWebhookResponse: true,
};
}
const bodyData = (context.getBodyData().data as IDataObject) ?? {};
const returnData: IDataObject = {};
for (const [index, field] of formFields.entries()) {
const key = `field-${index}`;
let value = bodyData[key] ?? null;
if (value === null) returnData[field.fieldLabel] = null;
if (field.fieldType === 'number') {
value = Number(value);
}
if (field.fieldType === 'text') {
value = String(value).trim();
}
if (field.multiselect && typeof value === 'string') {
value = jsonParse(value);
}
returnData[field.fieldLabel] = value;
}
returnData.submittedAt = new Date().toISOString();
returnData.formMode = mode;
const webhookResponse: IDataObject = { status: 200 };
return {
webhookResponse,
workflowData: [context.helpers.returnJsonArray(returnData)],
};
}