OPTIONS request support for Production/Test Webhooks (#787)

* 🐛 Fix naming of events in AffinityTrigger Node

* 🚧 OPTIONS allow header response for production webhooks

*  Implemented Allow header for test webhook OPTIONS response

Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
Rupenieks 2020-07-24 16:24:18 +02:00 committed by GitHub
parent de8e6f2811
commit 82d94873fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 1 deletions

View file

@ -161,6 +161,24 @@ export class ActiveWorkflowRunner {
});
}
/**
* Gets all request methods associated with a single webhook
* @param path webhook path
*/
async getWebhookMethods(path: string) : Promise<string[]> {
const webhooks = await Db.collections.Webhook?.find({ webhookPath: path}) as IWebhookDb[];
// check if something exist
if (webhooks === undefined) {
// The requested webhooks are not registered
throw new ResponseHelper.ResponseError(`The requested webhook "${path}" is not registered.`, 404, 404);
}
// Gather all request methods in string array
let webhookMethods : string[] = webhooks.map(webhook => webhook.method);
return webhookMethods;
}
/**
* Returns the ids of the currently active workflows
*

View file

@ -1567,6 +1567,25 @@ class App {
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
});
// 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;
try {
allowedMethods = await this.activeWorkflowRunner.getWebhookMethods(requestUrl);
// 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
@ -1630,6 +1649,25 @@ class App {
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
});
// 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;
try {
allowedMethods = await this.testWebhooks.getWebhookMethods(requestUrl);
// 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

View file

@ -110,6 +110,21 @@ export class TestWebhooks {
});
}
/**
* Gets all request methods associated with a single test webhook
* @param path webhook path
*/
async getWebhookMethods(path : string) : Promise<string[]> {
const webhookMethods: string[] = this.activeWebhooks!.getWebhookMethods(path);
if (webhookMethods === undefined) {
// The requested webhook is not registered
throw new ResponseHelper.ResponseError(`The requested webhook "${path}" is not registered.`, 404, 404);
}
return webhookMethods;
}
/**
* Checks if it has to wait for webhook data to execute the workflow. If yes it waits

View file

@ -85,6 +85,21 @@ export class ActiveWebhooks {
return this.webhookUrls[webhookKey];
}
/**
* Gets all request methods associated with a single webhook
* @param path
*/
getWebhookMethods(path: string): string[] {
let methods : string[] = [];
Object.keys(this.webhookUrls)
.filter(key => key.includes(path))
.map(key => {
methods.push(key.split('|')[0]);
});
return methods;
}
/**
* Returns the ids of all the workflows which have active webhooks

View file

@ -590,7 +590,7 @@ export interface IWorkflowMetadata {
active: boolean;
}
export type WebhookHttpMethod = 'GET' | 'POST' | 'HEAD';
export type WebhookHttpMethod = 'GET' | 'POST' | 'HEAD' | 'OPTIONS';
export interface IWebhookResponseData {
workflowData?: INodeExecutionData[][];