feat(n8n Form Page Node): New node (#10390)

Co-authored-by: Giulio Andreini <g.andreini@gmail.com>
Co-authored-by: Shireen Missi <94372015+ShireenMissi@users.noreply.github.com>
This commit is contained in:
Michael Kret
2024-10-17 16:59:53 +03:00
committed by GitHub
parent 86a94b5523
commit 643d66c0ae
39 changed files with 2101 additions and 229 deletions

View File

@@ -34,7 +34,10 @@ import {
BINARY_ENCODING,
createDeferredPromise,
ErrorReporterProxy as ErrorReporter,
ErrorReporterProxy,
FORM_NODE_TYPE,
NodeHelpers,
NodeOperationError,
} from 'n8n-workflow';
import { finished } from 'stream/promises';
import { Container } from 'typedi';
@@ -120,7 +123,7 @@ export async function executeWebhook(
);
if (nodeType === undefined) {
const errorMessage = `The type of the webhook node "${workflowStartNode.name}" is not known`;
responseCallback(new Error(errorMessage), {});
responseCallback(new ApplicationError(errorMessage), {});
throw new InternalServerError(errorMessage);
}
@@ -143,14 +146,37 @@ export async function executeWebhook(
}
// Get the responseMode
const responseMode = workflow.expression.getSimpleParameterValue(
workflowStartNode,
webhookData.webhookDescription.responseMode,
executionMode,
additionalKeys,
undefined,
'onReceived',
) as WebhookResponseMode;
let responseMode;
// if this is n8n FormTrigger node, check if there is a Form node in child nodes,
// if so, set 'responseMode' to 'formPage' to redirect to URL of that Form later
if (nodeType.description.name === 'formTrigger') {
const connectedNodes = workflow.getChildNodes(workflowStartNode.name);
let hasNextPage = false;
for (const nodeName of connectedNodes) {
const node = workflow.nodes[nodeName];
if (node.type === FORM_NODE_TYPE && !node.disabled) {
hasNextPage = true;
break;
}
}
if (hasNextPage) {
responseMode = 'formPage';
}
}
if (!responseMode) {
responseMode = workflow.expression.getSimpleParameterValue(
workflowStartNode,
webhookData.webhookDescription.responseMode,
executionMode,
additionalKeys,
undefined,
'onReceived',
) as WebhookResponseMode;
}
const responseCode = workflow.expression.getSimpleParameterValue(
workflowStartNode,
webhookData.webhookDescription.responseCode as string,
@@ -169,12 +195,12 @@ export async function executeWebhook(
'firstEntryJson',
);
if (!['onReceived', 'lastNode', 'responseNode'].includes(responseMode)) {
if (!['onReceived', 'lastNode', 'responseNode', 'formPage'].includes(responseMode)) {
// If the mode is not known we error. Is probably best like that instead of using
// the default that people know as early as possible (probably already testing phase)
// that something does not resolve properly.
const errorMessage = `The response mode '${responseMode}' is not valid!`;
responseCallback(new Error(errorMessage), {});
responseCallback(new ApplicationError(errorMessage), {});
throw new InternalServerError(errorMessage);
}
@@ -242,8 +268,26 @@ export async function executeWebhook(
});
} catch (err) {
// Send error response to webhook caller
const errorMessage = 'Workflow Webhook Error: Workflow could not be started!';
responseCallback(new Error(errorMessage), {});
const webhookType = ['formTrigger', 'form'].includes(nodeType.description.name)
? 'Form'
: 'Webhook';
let errorMessage = `Workflow ${webhookType} Error: Workflow could not be started!`;
// if workflow started manually, show an actual error message
if (err instanceof NodeOperationError && err.type === 'manual-form-test') {
errorMessage = err.message;
}
ErrorReporterProxy.error(err, {
extra: {
nodeName: workflowStartNode.name,
nodeType: workflowStartNode.type,
nodeVersion: workflowStartNode.typeVersion,
workflowId: workflow.id,
},
});
responseCallback(new ApplicationError(errorMessage), {});
didSendResponse = true;
// Add error to execution data that it can be logged and send to Editor-UI
@@ -487,6 +531,12 @@ export async function executeWebhook(
responsePromise,
);
if (responseMode === 'formPage' && !didSendResponse) {
res.redirect(`${additionalData.formWaitingBaseUrl}/${executionId}`);
process.nextTick(() => res.end());
didSendResponse = true;
}
Container.get(Logger).debug(
`Started execution of workflow "${workflow.name}" from webhook with execution ID ${executionId}`,
{ executionId },
@@ -562,7 +612,7 @@ export async function executeWebhook(
// Return the JSON data of the first entry
if (returnData.data!.main[0]![0] === undefined) {
responseCallback(new Error('No item to return got found'), {});
responseCallback(new ApplicationError('No item to return got found'), {});
didSendResponse = true;
return undefined;
}
@@ -616,13 +666,13 @@ export async function executeWebhook(
data = returnData.data!.main[0]![0];
if (data === undefined) {
responseCallback(new Error('No item was found to return'), {});
responseCallback(new ApplicationError('No item was found to return'), {});
didSendResponse = true;
return undefined;
}
if (data.binary === undefined) {
responseCallback(new Error('No binary data was found to return'), {});
responseCallback(new ApplicationError('No binary data was found to return'), {});
didSendResponse = true;
return undefined;
}
@@ -637,7 +687,10 @@ export async function executeWebhook(
);
if (responseBinaryPropertyName === undefined && !didSendResponse) {
responseCallback(new Error("No 'responseBinaryPropertyName' is set"), {});
responseCallback(
new ApplicationError("No 'responseBinaryPropertyName' is set"),
{},
);
didSendResponse = true;
}
@@ -646,7 +699,7 @@ export async function executeWebhook(
];
if (binaryData === undefined && !didSendResponse) {
responseCallback(
new Error(
new ApplicationError(
`The binary property '${responseBinaryPropertyName}' which should be returned does not exist`,
),
{},