mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
feat(n8n Form Trigger Node): Improvements (#7571)
Github issue / Community forum post (link here to close automatically): --------- Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in> Co-authored-by: Giulio Andreini <andreini@netseven.it>
This commit is contained in:
@@ -4,6 +4,7 @@ import type {
|
||||
INodeTypeDescription,
|
||||
INodeProperties,
|
||||
IDisplayOptions,
|
||||
IWebhookFunctions,
|
||||
} from 'n8n-workflow';
|
||||
import { WAIT_TIME_UNLIMITED } from 'n8n-workflow';
|
||||
|
||||
@@ -18,14 +19,168 @@ import {
|
||||
responseDataProperty,
|
||||
responseModeProperty,
|
||||
} from '../Webhook/description';
|
||||
|
||||
import {
|
||||
formDescription,
|
||||
formFields,
|
||||
respondWithOptions,
|
||||
formRespondMode,
|
||||
formTitle,
|
||||
} from '../Form/common.descriptions';
|
||||
import { formWebhook } from '../Form/utils';
|
||||
import { updateDisplayOptions } from '../../utils/utilities';
|
||||
|
||||
import { Webhook } from '../Webhook/Webhook.node';
|
||||
|
||||
const waitTimeProperties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Limit Wait Time',
|
||||
name: 'limitWaitTime',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether the workflow will automatically resume execution after the specified limit type',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resume: ['webhook', 'form'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit Type',
|
||||
name: 'limitType',
|
||||
type: 'options',
|
||||
default: 'afterTimeInterval',
|
||||
description:
|
||||
'Sets the condition for the execution to resume. Can be a specified date or after some time.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
limitWaitTime: [true],
|
||||
resume: ['webhook', 'form'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'After Time Interval',
|
||||
description: 'Waits for a certain amount of time',
|
||||
value: 'afterTimeInterval',
|
||||
},
|
||||
{
|
||||
name: 'At Specified Time',
|
||||
description: 'Waits until the set date and time to continue',
|
||||
value: 'atSpecifiedTime',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Amount',
|
||||
name: 'resumeAmount',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
limitType: ['afterTimeInterval'],
|
||||
limitWaitTime: [true],
|
||||
resume: ['webhook', 'form'],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 0,
|
||||
numberPrecision: 2,
|
||||
},
|
||||
default: 1,
|
||||
description: 'The time to wait',
|
||||
},
|
||||
{
|
||||
displayName: 'Unit',
|
||||
name: 'resumeUnit',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
limitType: ['afterTimeInterval'],
|
||||
limitWaitTime: [true],
|
||||
resume: ['webhook', 'form'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Seconds',
|
||||
value: 'seconds',
|
||||
},
|
||||
{
|
||||
name: 'Minutes',
|
||||
value: 'minutes',
|
||||
},
|
||||
{
|
||||
name: 'Hours',
|
||||
value: 'hours',
|
||||
},
|
||||
{
|
||||
name: 'Days',
|
||||
value: 'days',
|
||||
},
|
||||
],
|
||||
default: 'hours',
|
||||
description: 'Unit of the interval value',
|
||||
},
|
||||
{
|
||||
displayName: 'Max Date and Time',
|
||||
name: 'maxDateAndTime',
|
||||
type: 'dateTime',
|
||||
displayOptions: {
|
||||
show: {
|
||||
limitType: ['atSpecifiedTime'],
|
||||
limitWaitTime: [true],
|
||||
resume: ['webhook', 'form'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Continue execution after the specified date and time',
|
||||
},
|
||||
];
|
||||
|
||||
const webhookSuffix: INodeProperties = {
|
||||
displayName: 'Webhook Suffix',
|
||||
name: 'webhookSuffix',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'webhook',
|
||||
noDataExpression: true,
|
||||
description:
|
||||
'This suffix path will be appended to the restart URL. Helpful when using multiple wait nodes.',
|
||||
};
|
||||
|
||||
const displayOnWebhook: IDisplayOptions = {
|
||||
show: {
|
||||
resume: ['webhook'],
|
||||
},
|
||||
};
|
||||
|
||||
const displayOnFormSubmission = {
|
||||
show: {
|
||||
resume: ['form'],
|
||||
},
|
||||
};
|
||||
|
||||
const onFormSubmitProperties = updateDisplayOptions(displayOnFormSubmission, [
|
||||
formTitle,
|
||||
formDescription,
|
||||
formFields,
|
||||
formRespondMode,
|
||||
]);
|
||||
|
||||
const onWebhookCallProperties = updateDisplayOptions(displayOnWebhook, [
|
||||
{
|
||||
...httpMethodsProperty,
|
||||
description: 'The HTTP method of the Webhook call',
|
||||
},
|
||||
responseCodeProperty,
|
||||
responseModeProperty,
|
||||
responseDataProperty,
|
||||
responseBinaryPropertyNameProperty,
|
||||
]);
|
||||
|
||||
const webhookPath = '={{$parameter["options"]["webhookSuffix"] || ""}}';
|
||||
|
||||
export class Wait extends Webhook {
|
||||
authPropertyName = 'incomingAuthentication';
|
||||
|
||||
@@ -47,9 +202,28 @@ export class Wait extends Webhook {
|
||||
{
|
||||
...defaultWebhookDescription,
|
||||
responseData: '={{$parameter["responseData"]}}',
|
||||
path: '={{$parameter["options"]["webhookSuffix"] || ""}}',
|
||||
path: webhookPath,
|
||||
restartWebhook: true,
|
||||
},
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'GET',
|
||||
responseMode: 'onReceived',
|
||||
path: webhookPath,
|
||||
restartWebhook: true,
|
||||
isFullPath: true,
|
||||
isForm: true,
|
||||
},
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMode: '={{$parameter["responseMode"]}}',
|
||||
responseData: '={{$parameter["responseMode"] === "lastNode" ? "noData" : undefined}}',
|
||||
path: webhookPath,
|
||||
restartWebhook: true,
|
||||
isFullPath: true,
|
||||
isForm: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
@@ -72,6 +246,11 @@ export class Wait extends Webhook {
|
||||
value: 'webhook',
|
||||
description: 'Waits for a webhook call before continuing',
|
||||
},
|
||||
{
|
||||
name: 'On Form Submited',
|
||||
value: 'form',
|
||||
description: 'Waits for a form submission before continuing',
|
||||
},
|
||||
],
|
||||
default: 'timeInterval',
|
||||
description: 'Determines the waiting mode to use before the workflow continues',
|
||||
@@ -150,7 +329,7 @@ export class Wait extends Webhook {
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// resume:webhook
|
||||
// resume:webhook & form
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName:
|
||||
@@ -161,160 +340,67 @@ export class Wait extends Webhook {
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
...httpMethodsProperty,
|
||||
displayOptions: displayOnWebhook,
|
||||
description: 'The HTTP method of the Webhook call',
|
||||
},
|
||||
{
|
||||
...responseCodeProperty,
|
||||
displayOptions: displayOnWebhook,
|
||||
},
|
||||
{
|
||||
...responseModeProperty,
|
||||
displayOptions: displayOnWebhook,
|
||||
},
|
||||
{
|
||||
...responseDataProperty,
|
||||
displayOptions: {
|
||||
show: {
|
||||
...responseDataProperty.displayOptions?.show,
|
||||
...displayOnWebhook.show,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
...responseBinaryPropertyNameProperty,
|
||||
displayOptions: {
|
||||
show: {
|
||||
...responseBinaryPropertyNameProperty.displayOptions?.show,
|
||||
...displayOnWebhook.show,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit Wait Time',
|
||||
name: 'limitWaitTime',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
|
||||
description:
|
||||
'If no webhook call is received, the workflow will automatically resume execution after the specified limit type',
|
||||
displayOptions: displayOnWebhook,
|
||||
},
|
||||
{
|
||||
displayName: 'Limit Type',
|
||||
name: 'limitType',
|
||||
type: 'options',
|
||||
default: 'afterTimeInterval',
|
||||
description:
|
||||
'Sets the condition for the execution to resume. Can be a specified date or after some time.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
limitWaitTime: [true],
|
||||
...displayOnWebhook.show,
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'After Time Interval',
|
||||
description: 'Waits for a certain amount of time',
|
||||
value: 'afterTimeInterval',
|
||||
},
|
||||
{
|
||||
name: 'At Specified Time',
|
||||
description: 'Waits until the set date and time to continue',
|
||||
value: 'atSpecifiedTime',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Amount',
|
||||
name: 'resumeAmount',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
limitType: ['afterTimeInterval'],
|
||||
limitWaitTime: [true],
|
||||
...displayOnWebhook.show,
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 0,
|
||||
numberPrecision: 2,
|
||||
},
|
||||
default: 1,
|
||||
description: 'The time to wait',
|
||||
},
|
||||
{
|
||||
displayName: 'Unit',
|
||||
name: 'resumeUnit',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
limitType: ['afterTimeInterval'],
|
||||
limitWaitTime: [true],
|
||||
...displayOnWebhook.show,
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Seconds',
|
||||
value: 'seconds',
|
||||
},
|
||||
{
|
||||
name: 'Minutes',
|
||||
value: 'minutes',
|
||||
},
|
||||
{
|
||||
name: 'Hours',
|
||||
value: 'hours',
|
||||
},
|
||||
{
|
||||
name: 'Days',
|
||||
value: 'days',
|
||||
},
|
||||
],
|
||||
default: 'hours',
|
||||
description: 'Unit of the interval value',
|
||||
},
|
||||
{
|
||||
displayName: 'Max Date and Time',
|
||||
name: 'maxDateAndTime',
|
||||
type: 'dateTime',
|
||||
displayOptions: {
|
||||
show: {
|
||||
limitType: ['atSpecifiedTime'],
|
||||
limitWaitTime: [true],
|
||||
...displayOnWebhook.show,
|
||||
},
|
||||
},
|
||||
displayName:
|
||||
'The form url will be generated at run time. It can be referenced with the <strong>$execution.resumeFormUrl</strong> variable. Send it somewhere before getting to this node. <a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait/?utm_source=n8n_app&utm_medium=node_settings_modal-credential_link&utm_campaign=n8n-nodes-base.wait" target="_blank">More info</a>',
|
||||
name: 'formNotice',
|
||||
type: 'notice',
|
||||
displayOptions: displayOnFormSubmission,
|
||||
default: '',
|
||||
description: 'Continue execution after the specified date and time',
|
||||
},
|
||||
...onFormSubmitProperties,
|
||||
...onWebhookCallProperties,
|
||||
...waitTimeProperties,
|
||||
{
|
||||
...optionsProperty,
|
||||
displayOptions: displayOnWebhook,
|
||||
options: [
|
||||
...(optionsProperty.options as INodeProperties[]),
|
||||
{
|
||||
displayName: 'Webhook Suffix',
|
||||
name: 'webhookSuffix',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'webhook',
|
||||
description:
|
||||
'This suffix path will be appended to the restart URL. Helpful when using multiple wait nodes. Note: Does not support expressions.',
|
||||
options: [...(optionsProperty.options as INodeProperties[]), webhookSuffix],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resume: ['form'],
|
||||
},
|
||||
],
|
||||
hide: {
|
||||
responseMode: ['responseNode'],
|
||||
},
|
||||
},
|
||||
options: [respondWithOptions, webhookSuffix],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resume: ['form'],
|
||||
},
|
||||
hide: {
|
||||
responseMode: ['onReceived', 'lastNode'],
|
||||
},
|
||||
},
|
||||
options: [webhookSuffix],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
async webhook(context: IWebhookFunctions) {
|
||||
const resume = context.getNodeParameter('resume', 0) as string;
|
||||
if (resume === 'form') return formWebhook(context);
|
||||
return super.webhook(context);
|
||||
}
|
||||
|
||||
async execute(context: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const resume = context.getNodeParameter('resume', 0) as string;
|
||||
|
||||
if (resume === 'webhook') {
|
||||
return this.handleWebhookResume(context);
|
||||
if (['webhook', 'form'].includes(resume)) {
|
||||
return this.configureAndPutToWait(context);
|
||||
}
|
||||
|
||||
let waitTill: Date;
|
||||
@@ -357,16 +443,17 @@ export class Wait extends Webhook {
|
||||
return this.putToWait(context, waitTill);
|
||||
}
|
||||
|
||||
private async handleWebhookResume(context: IExecuteFunctions) {
|
||||
private async configureAndPutToWait(context: IExecuteFunctions) {
|
||||
let waitTill = new Date(WAIT_TIME_UNLIMITED);
|
||||
|
||||
const limitWaitTime = context.getNodeParameter('limitWaitTime', 0);
|
||||
|
||||
if (limitWaitTime === true) {
|
||||
const limitType = context.getNodeParameter('limitType', 0);
|
||||
|
||||
if (limitType === 'afterTimeInterval') {
|
||||
let waitAmount = context.getNodeParameter('resumeAmount', 0) as number;
|
||||
const resumeUnit = context.getNodeParameter('resumeUnit', 0);
|
||||
|
||||
if (resumeUnit === 'minutes') {
|
||||
waitAmount *= 60;
|
||||
}
|
||||
@@ -378,7 +465,6 @@ export class Wait extends Webhook {
|
||||
}
|
||||
|
||||
waitAmount *= 1000;
|
||||
|
||||
waitTill = new Date(new Date().getTime() + waitAmount);
|
||||
} else {
|
||||
waitTill = new Date(context.getNodeParameter('maxDateAndTime', 0) as string);
|
||||
|
||||
Reference in New Issue
Block a user