Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 94 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from 84 commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
983022a
setup
michael-radency Aug 5, 2024
d0f8d79
baseUrl fix
michael-radency Aug 5, 2024
06439b4
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1474…
michael-radency Aug 5, 2024
9c13384
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1474…
michael-radency Aug 6, 2024
c9f3fb9
form page node fix
michael-radency Aug 6, 2024
a7934ea
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1474…
michael-radency Aug 12, 2024
fbad7a6
redirect to next form page
michael-radency Aug 12, 2024
9ed871f
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1474…
michael-radency Aug 13, 2024
8af98aa
refactor formWebhook
michael-radency Aug 13, 2024
70fe188
import fix
michael-radency Aug 13, 2024
496b3a0
form page webhook update
michael-radency Aug 13, 2024
9f76644
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1474…
michael-radency Aug 13, 2024
f1d894e
added evaluateExpression to WebhookFunctions
michael-radency Aug 13, 2024
4b67839
autoselect formPage response mode, inherit trigger parameters in page…
michael-radency Aug 14, 2024
c760daf
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Aug 14, 2024
0d93bde
complition mode setup, tests fix
michael-radency Aug 14, 2024
82f21c0
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Aug 19, 2024
6bee310
show completion page
michael-radency Aug 19, 2024
17abf71
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Aug 19, 2024
a666588
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Aug 20, 2024
a549b17
waiting forms refactoring
michael-radency Aug 20, 2024
ccd6f63
fix error when trying to open form from wait node ndv
michael-radency Aug 20, 2024
b8a93d7
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Aug 20, 2024
8c9f066
completion page last executed node fix
michael-radency Aug 20, 2024
927fa93
json fields input
michael-radency Aug 20, 2024
4491d04
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Aug 20, 2024
81c0082
form json fields better error when json invalid, check for fields syntax
michael-radency Aug 21, 2024
696f708
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Aug 21, 2024
308d6d4
field dropdown options syntax check
michael-radency Aug 21, 2024
58fe621
redirect option for completion screen
michael-radency Aug 21, 2024
121bfb9
renamed node
michael-radency Aug 21, 2024
16c596c
renamed json parameter
michael-radency Aug 21, 2024
8b1ff6a
triger parameters update on form page connection
michael-radency Aug 21, 2024
5e49daf
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Aug 21, 2024
2a5ad47
form response mode fix
michael-radency Aug 21, 2024
5fd184a
cleanup
michael-radency Aug 22, 2024
c40067c
validation for form fields on FE
michael-radency Aug 22, 2024
16fa4d7
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Aug 22, 2024
065659b
form flashing on reload fix
michael-radency Aug 22, 2024
3d64385
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Aug 22, 2024
a78e4c4
merge fixes
michael-radency Aug 22, 2024
c0c92c6
removed hard coded form types
michael-radency Aug 22, 2024
9eb86e8
Minor copy tweaks.
gandreini Aug 23, 2024
d03e4d4
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Sep 2, 2024
9ffaae8
merge fixes
michael-radency Sep 2, 2024
804bde9
render default form submited if no completion page and no errors on w…
michael-radency Sep 2, 2024
7d593b1
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Sep 2, 2024
33742d1
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Sep 2, 2024
06ab0ce
fix json parse, show add form on side panel if form trigger in workflow
michael-radency Sep 3, 2024
1e23779
get query parameters only on query
michael-radency Sep 3, 2024
5d66e19
query parameters support for form node
michael-radency Sep 3, 2024
2a2f4c4
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Sep 3, 2024
fead448
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Sep 3, 2024
384f542
utils tests
michael-radency Sep 4, 2024
ec5427f
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Sep 4, 2024
a5f0e24
form node's execute and webhook methods test
michael-radency Sep 4, 2024
85b3be0
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Sep 9, 2024
d2a53ec
review update
michael-radency Sep 9, 2024
b0a15c2
Minor copy tweak
gandreini Sep 10, 2024
f8432e6
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Sep 19, 2024
bb925c6
Merge branch 'node-1472-add-form-node-p0' of https://github.com/n8n-i…
michael-radency Sep 19, 2024
e72c751
review updates, wip
michael-radency Sep 19, 2024
2a4fc26
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Sep 19, 2024
de50989
text update
michael-radency Sep 19, 2024
09fb772
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 3, 2024
c291f12
tests update
michael-radency Oct 3, 2024
20a9b4e
lint fix
michael-radency Oct 3, 2024
6760128
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 4, 2024
ff792ab
resolve waiting resolution merge update
michael-radency Oct 4, 2024
f686142
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 4, 2024
2fb4a62
prepare to merge master
michael-radency Oct 9, 2024
8d32eb1
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 9, 2024
0ab06a6
page form popup fix, warnings update
michael-radency Oct 9, 2024
73df14a
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 9, 2024
b6a1721
form duplication fix, path moved to options, review fixes
michael-radency Oct 10, 2024
192f196
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 10, 2024
8f3fea1
update path in options
michael-radency Oct 10, 2024
7c37fa0
reorder options in form trigger
michael-radency Oct 10, 2024
8bb1943
redirection form fix
michael-radency Oct 10, 2024
34d9466
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 10, 2024
7cf1975
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 11, 2024
160f055
reload on sendAndWait, remove suffix and responseWith options from wa…
michael-radency Oct 11, 2024
2b040f4
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 11, 2024
2794bc0
rmoved unused import
michael-radency Oct 11, 2024
4915821
Update packages/cli/src/webhooks/waiting-forms.ts
michael-radency Oct 16, 2024
bb78188
Update packages/cli/src/webhooks/webhook-helpers.ts
michael-radency Oct 16, 2024
3fd0997
Update packages/nodes-base/nodes/Form/Form.node.ts
michael-radency Oct 16, 2024
93f8e7f
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 16, 2024
26e2b0c
review update
michael-radency Oct 16, 2024
b16cee9
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 17, 2024
756f5d6
test fix
michael-radency Oct 17, 2024
170c3ae
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 17, 2024
abefc2f
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1472…
michael-radency Oct 17, 2024
470f149
e2e test fix
michael-radency Oct 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/cli/src/abstract-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import { ExternalHooks } from '@/external-hooks';
import { Logger } from '@/logging/logger.service';
import { rawBodyReader, bodyParser, corsMiddleware } from '@/middlewares';
import { send, sendErrorResponse } from '@/response-helper';
import { WaitingForms } from '@/waiting-forms';
import { LiveWebhooks } from '@/webhooks/live-webhooks';
import { TestWebhooks } from '@/webhooks/test-webhooks';
import { WaitingForms } from '@/webhooks/waiting-forms';
import { WaitingWebhooks } from '@/webhooks/waiting-webhooks';
import { createWebhookHandlerFor } from '@/webhooks/webhook-request-handler';

Expand Down
19 changes: 0 additions & 19 deletions packages/cli/src/waiting-forms.ts

This file was deleted.

143 changes: 143 additions & 0 deletions packages/cli/src/webhooks/waiting-forms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import axios from 'axios';
import type express from 'express';
import { FORM_NODE_TYPE, sleep, Workflow } from 'n8n-workflow';
import { Service } from 'typedi';

import { ConflictError } from '@/errors/response-errors/conflict.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import type { IExecutionResponse } from '@/interfaces';
import { WaitingWebhooks } from '@/webhooks/waiting-webhooks';

import type { IWebhookResponseCallbackData, WaitingWebhookRequest } from './webhook.types';

@Service()
export class WaitingForms extends WaitingWebhooks {
protected override includeForms = true;

protected override logReceivedWebhook(method: string, executionId: string) {
this.logger.debug(`Received waiting-form "${method}" for execution "${executionId}"`);
}

protected disableNode(execution: IExecutionResponse, method?: string) {
if (method === 'POST') {
execution.data.executionData!.nodeExecutionStack[0].node.disabled = true;
}
}

private getWorkflow(execution: IExecutionResponse) {
const { workflowData } = execution;
return new Workflow({
id: workflowData.id,
name: workflowData.name,
nodes: workflowData.nodes,
connections: workflowData.connections,
active: workflowData.active,
nodeTypes: this.nodeTypes,
staticData: workflowData.staticData,
settings: workflowData.settings,
});
}

async executeWebhook(
req: WaitingWebhookRequest,
res: express.Response,
): Promise<IWebhookResponseCallbackData> {
const { path: executionId, suffix } = req.params;

this.logReceivedWebhook(req.method, executionId);

// Reset request parameters
req.params = {} as WaitingWebhookRequest['params'];

const execution = await this.getExecution(executionId);

if (!execution) {
throw new NotFoundError(`The execution "${executionId}" does not exist.`);
}

if (execution.data.resultData.error) {
throw new ConflictError(`The execution "${executionId}" has finished with error.`);
}

if (execution.status === 'running') {
if (this.includeForms && req.method === 'GET') {
await sleep(1000);

const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
const page = await axios({ url });

if (page) {
res.send(`
<script>
setTimeout(function() {
window.location.reload();
}, 1);
</script>
`);

return {
noWebhookResponse: true,
};
} else {
return {
noWebhookResponse: true,
};
}
michael-radency marked this conversation as resolved.
Show resolved Hide resolved
}
throw new ConflictError(`The execution "${executionId}" is running already.`);
}

let completionPage;
if (execution.finished) {
const workflow = this.getWorkflow(execution);

const parentNodes = workflow.getParentNodes(
execution.data.resultData.lastNodeExecuted as string,
);

const lastNodeExecuted = execution.data.resultData.lastNodeExecuted as string;
const lastNode = workflow.nodes[lastNodeExecuted];

if (
!lastNode.disabled &&
lastNode.type === FORM_NODE_TYPE &&
lastNode.parameters.operation === 'completion'
) {
completionPage = lastNodeExecuted;
} else {
completionPage = Object.keys(workflow.nodes).find((nodeName) => {
const node = workflow.nodes[nodeName];
return (
parentNodes.includes(nodeName) &&
!node.disabled &&
node.type === FORM_NODE_TYPE &&
node.parameters.operation === 'completion'
);
});
}

if (!completionPage) {
res.render('form-trigger-completion', {
title: 'Form Submitted',
message: 'Your response has been recorded',
formTitle: 'Form Submitted',
});

return {
noWebhookResponse: true,
};
}
}

const targetNode = completionPage || (execution.data.resultData.lastNodeExecuted as string);

return await this.getWebhookExecutionData({
execution,
req,
res,
lastNodeExecuted: targetNode,
executionId,
suffix,
});
}
}
68 changes: 57 additions & 11 deletions packages/cli/src/webhooks/waiting-webhooks.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type express from 'express';
import {
FORM_NODE_TYPE,
type INodes,
type IWorkflowBase,
NodeHelpers,
SEND_AND_WAIT_OPERATION,
WAIT_NODE_TYPE,
Workflow,
} from 'n8n-workflow';
import { Service } from 'typedi';
Expand Down Expand Up @@ -34,7 +36,7 @@ export class WaitingWebhooks implements IWebhookManager {

constructor(
protected readonly logger: Logger,
private readonly nodeTypes: NodeTypes,
protected readonly nodeTypes: NodeTypes,
private readonly executionRepository: ExecutionRepository,
) {}

Expand All @@ -58,7 +60,7 @@ export class WaitingWebhooks implements IWebhookManager {
);
}

private getWorkflow(workflowData: IWorkflowBase) {
private createWorkflow(workflowData: IWorkflowBase) {
return new Workflow({
id: workflowData.id,
name: workflowData.name,
Expand All @@ -71,6 +73,13 @@ export class WaitingWebhooks implements IWebhookManager {
});
}

protected async getExecution(executionId: string) {
return await this.executionRepository.findSingleExecution(executionId, {
includeData: true,
unflattenData: true,
});
}

async executeWebhook(
req: WaitingWebhookRequest,
res: express.Response,
Expand All @@ -82,17 +91,14 @@ export class WaitingWebhooks implements IWebhookManager {
// Reset request parameters
req.params = {} as WaitingWebhookRequest['params'];

const execution = await this.executionRepository.findSingleExecution(executionId, {
includeData: true,
unflattenData: true,
});
const execution = await this.getExecution(executionId);

if (!execution) {
throw new NotFoundError(`The execution "${executionId} does not exist.`);
throw new NotFoundError(`The execution "${executionId}" does not exist.`);
}

if (execution.status === 'running') {
throw new ConflictError(`The execution "${executionId} is running already.`);
throw new ConflictError(`The execution "${executionId}" is running already.`);
}

if (execution.data?.resultData?.error) {
Expand All @@ -101,7 +107,7 @@ export class WaitingWebhooks implements IWebhookManager {

if (execution.finished) {
const { workflowData } = execution;
const { nodes } = this.getWorkflow(workflowData);
const { nodes } = this.createWorkflow(workflowData);
if (this.isSendAndWaitRequest(nodes, suffix)) {
res.render('send-and-wait-no-action-required', { isTestWebhook: false });
return { noWebhookResponse: true };
Expand All @@ -112,6 +118,31 @@ export class WaitingWebhooks implements IWebhookManager {

const lastNodeExecuted = execution.data.resultData.lastNodeExecuted as string;

return await this.getWebhookExecutionData({
execution,
req,
res,
lastNodeExecuted,
executionId,
suffix,
});
}

protected async getWebhookExecutionData({
execution,
req,
res,
lastNodeExecuted,
executionId,
suffix,
}: {
execution: IExecutionResponse;
req: WaitingWebhookRequest;
res: express.Response;
lastNodeExecuted: string;
executionId: string;
suffix?: string;
}): Promise<IWebhookResponseCallbackData> {
// Set the node as disabled so that the data does not get executed again as it would result
// in starting the wait all over again
this.disableNode(execution, req.method);
Expand All @@ -123,7 +154,7 @@ export class WaitingWebhooks implements IWebhookManager {
execution.data.resultData.runData[lastNodeExecuted].pop();

const { workflowData } = execution;
const workflow = this.getWorkflow(workflowData);
const workflow = this.createWorkflow(workflowData);

const workflowStartNode = workflow.getNode(lastNodeExecuted);
if (workflowStartNode === null) {
Expand All @@ -146,11 +177,26 @@ export class WaitingWebhooks implements IWebhookManager {
if (webhookData === undefined) {
// If no data got found it means that the execution can not be started via a webhook.
// Return 404 because we do not want to give any data if the execution exists or not.
const errorMessage = `The workflow for execution "${executionId}" does not contain a waiting webhook with a matching path/method.`;

if (this.isSendAndWaitRequest(workflow.nodes, suffix)) {
res.render('send-and-wait-no-action-required', { isTestWebhook: false });
return { noWebhookResponse: true };
} else if (!execution.data.resultData.error && execution.status === 'waiting') {
const childNodes = workflow.getChildNodes(
execution.data.resultData.lastNodeExecuted as string,
);
const hasChildForms = childNodes.some(
(node) =>
workflow.nodes[node].type === FORM_NODE_TYPE ||
workflow.nodes[node].type === WAIT_NODE_TYPE,
);
if (hasChildForms) {
return { noWebhookResponse: true };
} else {
throw new NotFoundError(errorMessage);
}
} else {
const errorMessage = `The workflow for execution "${executionId}" does not contain a waiting webhook with a matching path/method.`;
throw new NotFoundError(errorMessage);
}
}
Expand Down
Loading
Loading