mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -08:00
✨ Add DELETE, PATCH and PUT request support to Webhooks
This commit is contained in:
parent
ec5bfaf895
commit
9a06d0fffc
|
@ -72,6 +72,7 @@ import {
|
||||||
IWorkflowBase,
|
IWorkflowBase,
|
||||||
LoggerProxy,
|
LoggerProxy,
|
||||||
NodeHelpers,
|
NodeHelpers,
|
||||||
|
WebhookHttpMethod,
|
||||||
Workflow,
|
Workflow,
|
||||||
WorkflowExecuteMode,
|
WorkflowExecuteMode,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
@ -202,6 +203,8 @@ class App {
|
||||||
|
|
||||||
presetCredentialsLoaded: boolean;
|
presetCredentialsLoaded: boolean;
|
||||||
|
|
||||||
|
webhookMethods: WebhookHttpMethod[];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.app = express();
|
this.app = express();
|
||||||
|
|
||||||
|
@ -237,6 +240,8 @@ class App {
|
||||||
this.presetCredentialsLoaded = false;
|
this.presetCredentialsLoaded = false;
|
||||||
this.endpointPresetCredentials = config.get('credentials.overwrite.endpoint') as string;
|
this.endpointPresetCredentials = config.get('credentials.overwrite.endpoint') as string;
|
||||||
|
|
||||||
|
this.webhookMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT'];
|
||||||
|
|
||||||
const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl();
|
const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl();
|
||||||
|
|
||||||
const telemetrySettings: ITelemetrySettings = {
|
const telemetrySettings: ITelemetrySettings = {
|
||||||
|
@ -2704,8 +2709,8 @@ class App {
|
||||||
WebhookServer.registerProductionWebhooks.apply(this);
|
WebhookServer.registerProductionWebhooks.apply(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// HEAD webhook requests (test for UI)
|
// Register all webhook requests (test for UI)
|
||||||
this.app.head(
|
this.app.all(
|
||||||
`/${this.endpointWebhookTest}/*`,
|
`/${this.endpointWebhookTest}/*`,
|
||||||
async (req: express.Request, res: express.Response) => {
|
async (req: express.Request, res: express.Response) => {
|
||||||
// Cut away the "/webhook-test/" to get the registred part of the url
|
// Cut away the "/webhook-test/" to get the registred part of the url
|
||||||
|
@ -2713,98 +2718,36 @@ class App {
|
||||||
this.endpointWebhookTest.length + 2,
|
this.endpointWebhookTest.length + 2,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const method = req.method.toUpperCase() as WebhookHttpMethod;
|
||||||
|
|
||||||
|
if (method === 'OPTIONS') {
|
||||||
|
let allowedMethods: string[];
|
||||||
|
try {
|
||||||
|
allowedMethods = await this.testWebhooks.getWebhookMethods(requestUrl);
|
||||||
|
allowedMethods.push('OPTIONS');
|
||||||
|
|
||||||
|
// Add custom "Allow" header to satisfy OPTIONS response.
|
||||||
|
res.append('Allow', allowedMethods);
|
||||||
|
} catch (error) {
|
||||||
|
ResponseHelper.sendErrorResponse(res, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseHelper.sendSuccessResponse(res, {}, true, 204);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.webhookMethods.includes(method)) {
|
||||||
|
ResponseHelper.sendErrorResponse(
|
||||||
|
res,
|
||||||
|
new Error(`The method ${method} is not supported.`),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let response;
|
let response;
|
||||||
try {
|
try {
|
||||||
response = await this.testWebhooks.callTestWebhook('HEAD', requestUrl, req, res);
|
response = await this.testWebhooks.callTestWebhook(method, requestUrl, req, res);
|
||||||
} catch (error) {
|
|
||||||
ResponseHelper.sendErrorResponse(res, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.noWebhookResponse === true) {
|
|
||||||
// Nothing else to do as the response got already sent
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponseHelper.sendSuccessResponse(
|
|
||||||
res,
|
|
||||||
response.data,
|
|
||||||
true,
|
|
||||||
response.responseCode,
|
|
||||||
response.headers,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// HEAD webhook requests (test for UI)
|
|
||||||
this.app.options(
|
|
||||||
`/${this.endpointWebhookTest}/*`,
|
|
||||||
async (req: express.Request, res: express.Response) => {
|
|
||||||
// Cut away the "/webhook-test/" to get the registred part of the url
|
|
||||||
const requestUrl = (req as ICustomRequest).parsedUrl!.pathname!.slice(
|
|
||||||
this.endpointWebhookTest.length + 2,
|
|
||||||
);
|
|
||||||
|
|
||||||
let allowedMethods: string[];
|
|
||||||
try {
|
|
||||||
allowedMethods = await this.testWebhooks.getWebhookMethods(requestUrl);
|
|
||||||
allowedMethods.push('OPTIONS');
|
|
||||||
|
|
||||||
// Add custom "Allow" header to satisfy OPTIONS response.
|
|
||||||
res.append('Allow', allowedMethods);
|
|
||||||
} catch (error) {
|
|
||||||
ResponseHelper.sendErrorResponse(res, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponseHelper.sendSuccessResponse(res, {}, true, 204);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// GET webhook requests (test for UI)
|
|
||||||
this.app.get(
|
|
||||||
`/${this.endpointWebhookTest}/*`,
|
|
||||||
async (req: express.Request, res: express.Response) => {
|
|
||||||
// Cut away the "/webhook-test/" to get the registred part of the url
|
|
||||||
const requestUrl = (req as ICustomRequest).parsedUrl!.pathname!.slice(
|
|
||||||
this.endpointWebhookTest.length + 2,
|
|
||||||
);
|
|
||||||
|
|
||||||
let response;
|
|
||||||
try {
|
|
||||||
response = await this.testWebhooks.callTestWebhook('GET', requestUrl, req, res);
|
|
||||||
} catch (error) {
|
|
||||||
ResponseHelper.sendErrorResponse(res, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.noWebhookResponse === true) {
|
|
||||||
// Nothing else to do as the response got already sent
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponseHelper.sendSuccessResponse(
|
|
||||||
res,
|
|
||||||
response.data,
|
|
||||||
true,
|
|
||||||
response.responseCode,
|
|
||||||
response.headers,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// POST webhook requests (test for UI)
|
|
||||||
this.app.post(
|
|
||||||
`/${this.endpointWebhookTest}/*`,
|
|
||||||
async (req: express.Request, res: express.Response) => {
|
|
||||||
// Cut away the "/webhook-test/" to get the registred part of the url
|
|
||||||
const requestUrl = (req as ICustomRequest).parsedUrl!.pathname!.slice(
|
|
||||||
this.endpointWebhookTest.length + 2,
|
|
||||||
);
|
|
||||||
|
|
||||||
let response;
|
|
||||||
try {
|
|
||||||
response = await this.testWebhooks.callTestWebhook('POST', requestUrl, req, res);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ResponseHelper.sendErrorResponse(res, error);
|
ResponseHelper.sendErrorResponse(res, error);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -16,6 +16,7 @@ import * as _ from 'lodash';
|
||||||
import * as compression from 'compression';
|
import * as compression from 'compression';
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import * as parseUrl from 'parseurl';
|
import * as parseUrl from 'parseurl';
|
||||||
|
import { WebhookHttpMethod } from 'n8n-workflow';
|
||||||
// eslint-disable-next-line import/no-cycle
|
// eslint-disable-next-line import/no-cycle
|
||||||
import {
|
import {
|
||||||
ActiveExecutions,
|
ActiveExecutions,
|
||||||
|
@ -41,8 +42,8 @@ export function registerProductionWebhooks() {
|
||||||
// Regular Webhooks
|
// Regular Webhooks
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
// HEAD webhook requests
|
// Register all webhook requests
|
||||||
this.app.head(
|
this.app.all(
|
||||||
`/${this.endpointWebhook}/*`,
|
`/${this.endpointWebhook}/*`,
|
||||||
async (req: express.Request, res: express.Response) => {
|
async (req: express.Request, res: express.Response) => {
|
||||||
// Cut away the "/webhook/" to get the registred part of the url
|
// Cut away the "/webhook/" to get the registred part of the url
|
||||||
|
@ -50,99 +51,34 @@ export function registerProductionWebhooks() {
|
||||||
this.endpointWebhook.length + 2,
|
this.endpointWebhook.length + 2,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const method = req.method.toUpperCase() as WebhookHttpMethod;
|
||||||
|
|
||||||
|
if (method === 'OPTIONS') {
|
||||||
|
let allowedMethods: string[];
|
||||||
|
try {
|
||||||
|
allowedMethods = await this.activeWorkflowRunner.getWebhookMethods(requestUrl);
|
||||||
|
allowedMethods.push('OPTIONS');
|
||||||
|
|
||||||
|
// Add custom "Allow" header to satisfy OPTIONS response.
|
||||||
|
res.append('Allow', allowedMethods);
|
||||||
|
} catch (error) {
|
||||||
|
ResponseHelper.sendErrorResponse(res, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseHelper.sendSuccessResponse(res, {}, true, 204);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.webhookMethods.includes(method)) {
|
||||||
|
ResponseHelper.sendErrorResponse(res, new Error(`The method ${method} is not supported.`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let response;
|
let response;
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
response = await this.activeWorkflowRunner.executeWebhook('HEAD', requestUrl, req, res);
|
response = await this.activeWorkflowRunner.executeWebhook(method, requestUrl, req, res);
|
||||||
} catch (error) {
|
|
||||||
ResponseHelper.sendErrorResponse(res, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.noWebhookResponse === true) {
|
|
||||||
// Nothing else to do as the response got already sent
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponseHelper.sendSuccessResponse(
|
|
||||||
res,
|
|
||||||
response.data,
|
|
||||||
true,
|
|
||||||
response.responseCode,
|
|
||||||
response.headers,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// OPTIONS webhook requests
|
|
||||||
this.app.options(
|
|
||||||
`/${this.endpointWebhook}/*`,
|
|
||||||
async (req: express.Request, res: express.Response) => {
|
|
||||||
// Cut away the "/webhook/" to get the registred part of the url
|
|
||||||
const requestUrl = (req as ICustomRequest).parsedUrl!.pathname!.slice(
|
|
||||||
this.endpointWebhook.length + 2,
|
|
||||||
);
|
|
||||||
|
|
||||||
let allowedMethods: string[];
|
|
||||||
try {
|
|
||||||
allowedMethods = await this.activeWorkflowRunner.getWebhookMethods(requestUrl);
|
|
||||||
allowedMethods.push('OPTIONS');
|
|
||||||
|
|
||||||
// Add custom "Allow" header to satisfy OPTIONS response.
|
|
||||||
res.append('Allow', allowedMethods);
|
|
||||||
} catch (error) {
|
|
||||||
ResponseHelper.sendErrorResponse(res, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponseHelper.sendSuccessResponse(res, {}, true, 204);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// GET webhook requests
|
|
||||||
this.app.get(
|
|
||||||
`/${this.endpointWebhook}/*`,
|
|
||||||
async (req: express.Request, res: express.Response) => {
|
|
||||||
// Cut away the "/webhook/" to get the registred part of the url
|
|
||||||
const requestUrl = (req as ICustomRequest).parsedUrl!.pathname!.slice(
|
|
||||||
this.endpointWebhook.length + 2,
|
|
||||||
);
|
|
||||||
|
|
||||||
let response;
|
|
||||||
try {
|
|
||||||
response = await this.activeWorkflowRunner.executeWebhook('GET', requestUrl, req, res);
|
|
||||||
} catch (error) {
|
|
||||||
ResponseHelper.sendErrorResponse(res, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.noWebhookResponse === true) {
|
|
||||||
// Nothing else to do as the response got already sent
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponseHelper.sendSuccessResponse(
|
|
||||||
res,
|
|
||||||
response.data,
|
|
||||||
true,
|
|
||||||
response.responseCode,
|
|
||||||
response.headers,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// POST webhook requests
|
|
||||||
this.app.post(
|
|
||||||
`/${this.endpointWebhook}/*`,
|
|
||||||
async (req: express.Request, res: express.Response) => {
|
|
||||||
// Cut away the "/webhook/" to get the registred part of the url
|
|
||||||
const requestUrl = (req as ICustomRequest).parsedUrl!.pathname!.slice(
|
|
||||||
this.endpointWebhook.length + 2,
|
|
||||||
);
|
|
||||||
|
|
||||||
let response;
|
|
||||||
try {
|
|
||||||
response = await this.activeWorkflowRunner.executeWebhook('POST', requestUrl, req, res);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ResponseHelper.sendErrorResponse(res, error);
|
ResponseHelper.sendErrorResponse(res, error);
|
||||||
return;
|
return;
|
||||||
|
@ -169,8 +105,8 @@ export function registerProductionWebhooks() {
|
||||||
|
|
||||||
const waitingWebhooks = new WaitingWebhooks();
|
const waitingWebhooks = new WaitingWebhooks();
|
||||||
|
|
||||||
// HEAD webhook-waiting requests
|
// Register all webhook-waiting requests
|
||||||
this.app.head(
|
this.app.all(
|
||||||
`/${this.endpointWebhookWaiting}/*`,
|
`/${this.endpointWebhookWaiting}/*`,
|
||||||
async (req: express.Request, res: express.Response) => {
|
async (req: express.Request, res: express.Response) => {
|
||||||
// Cut away the "/webhook-waiting/" to get the registred part of the url
|
// Cut away the "/webhook-waiting/" to get the registred part of the url
|
||||||
|
@ -178,73 +114,20 @@ export function registerProductionWebhooks() {
|
||||||
this.endpointWebhookWaiting.length + 2,
|
this.endpointWebhookWaiting.length + 2,
|
||||||
);
|
);
|
||||||
|
|
||||||
let response;
|
const method = req.method.toUpperCase() as WebhookHttpMethod;
|
||||||
try {
|
|
||||||
response = await waitingWebhooks.executeWebhook('HEAD', requestUrl, req, res);
|
// TOOD: Add support for OPTIONS in the future
|
||||||
} catch (error) {
|
// if (method === 'OPTIONS') {
|
||||||
ResponseHelper.sendErrorResponse(res, error);
|
// }
|
||||||
|
|
||||||
|
if (!this.webhookMethods.includes(method)) {
|
||||||
|
ResponseHelper.sendErrorResponse(res, new Error(`The method ${method} is not supported.`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.noWebhookResponse === true) {
|
|
||||||
// Nothing else to do as the response got already sent
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponseHelper.sendSuccessResponse(
|
|
||||||
res,
|
|
||||||
response.data,
|
|
||||||
true,
|
|
||||||
response.responseCode,
|
|
||||||
response.headers,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// GET webhook-waiting requests
|
|
||||||
this.app.get(
|
|
||||||
`/${this.endpointWebhookWaiting}/*`,
|
|
||||||
async (req: express.Request, res: express.Response) => {
|
|
||||||
// Cut away the "/webhook-waiting/" to get the registred part of the url
|
|
||||||
const requestUrl = (req as ICustomRequest).parsedUrl!.pathname!.slice(
|
|
||||||
this.endpointWebhookWaiting.length + 2,
|
|
||||||
);
|
|
||||||
|
|
||||||
let response;
|
let response;
|
||||||
try {
|
try {
|
||||||
response = await waitingWebhooks.executeWebhook('GET', requestUrl, req, res);
|
response = await waitingWebhooks.executeWebhook(method, requestUrl, req, res);
|
||||||
} catch (error) {
|
|
||||||
ResponseHelper.sendErrorResponse(res, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.noWebhookResponse === true) {
|
|
||||||
// Nothing else to do as the response got already sent
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponseHelper.sendSuccessResponse(
|
|
||||||
res,
|
|
||||||
response.data,
|
|
||||||
true,
|
|
||||||
response.responseCode,
|
|
||||||
response.headers,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// POST webhook-waiting requests
|
|
||||||
this.app.post(
|
|
||||||
`/${this.endpointWebhookWaiting}/*`,
|
|
||||||
async (req: express.Request, res: express.Response) => {
|
|
||||||
// Cut away the "/webhook-waiting/" to get the registred part of the url
|
|
||||||
const requestUrl = (req as ICustomRequest).parsedUrl!.pathname!.slice(
|
|
||||||
this.endpointWebhookWaiting.length + 2,
|
|
||||||
);
|
|
||||||
|
|
||||||
let response;
|
|
||||||
try {
|
|
||||||
response = await waitingWebhooks.executeWebhook('POST', requestUrl, req, res);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ResponseHelper.sendErrorResponse(res, error);
|
ResponseHelper.sendErrorResponse(res, error);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -250,6 +250,10 @@ export class Wait implements INodeType {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
name: 'DELETE',
|
||||||
|
value: 'DELETE',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'GET',
|
name: 'GET',
|
||||||
value: 'GET',
|
value: 'GET',
|
||||||
|
@ -258,10 +262,18 @@ export class Wait implements INodeType {
|
||||||
name: 'HEAD',
|
name: 'HEAD',
|
||||||
value: 'HEAD',
|
value: 'HEAD',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'PATCH',
|
||||||
|
value: 'PATCH',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'POST',
|
name: 'POST',
|
||||||
value: 'POST',
|
value: 'POST',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'PUT',
|
||||||
|
value: 'PUT',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
default: 'GET',
|
default: 'GET',
|
||||||
description: 'The HTTP method of the Webhook call',
|
description: 'The HTTP method of the Webhook call',
|
||||||
|
@ -514,6 +526,8 @@ export class Wait implements INodeType {
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
'/httpMethod': [
|
'/httpMethod': [
|
||||||
|
'PATCH',
|
||||||
|
'PUT',
|
||||||
'POST',
|
'POST',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -120,6 +120,10 @@ export class Webhook implements INodeType {
|
||||||
name: 'httpMethod',
|
name: 'httpMethod',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
name: 'DELETE',
|
||||||
|
value: 'DELETE',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'GET',
|
name: 'GET',
|
||||||
value: 'GET',
|
value: 'GET',
|
||||||
|
@ -128,10 +132,18 @@ export class Webhook implements INodeType {
|
||||||
name: 'HEAD',
|
name: 'HEAD',
|
||||||
value: 'HEAD',
|
value: 'HEAD',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'PATCH',
|
||||||
|
value: 'PATCH',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'POST',
|
name: 'POST',
|
||||||
value: 'POST',
|
value: 'POST',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'PUT',
|
||||||
|
value: 'PUT',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
default: 'GET',
|
default: 'GET',
|
||||||
description: 'The HTTP method to listen to.',
|
description: 'The HTTP method to listen to.',
|
||||||
|
@ -265,6 +277,8 @@ export class Webhook implements INodeType {
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
'/httpMethod': [
|
'/httpMethod': [
|
||||||
|
'PATCH',
|
||||||
|
'PUT',
|
||||||
'POST',
|
'POST',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1178,7 +1178,7 @@ export interface IWorkflowMetadata {
|
||||||
active: boolean;
|
active: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WebhookHttpMethod = 'GET' | 'POST' | 'HEAD' | 'OPTIONS';
|
export type WebhookHttpMethod = 'DELETE' | 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'OPTIONS';
|
||||||
|
|
||||||
export interface IWebhookResponseData {
|
export interface IWebhookResponseData {
|
||||||
workflowData?: INodeExecutionData[][];
|
workflowData?: INodeExecutionData[][];
|
||||||
|
|
Loading…
Reference in a new issue