diff --git a/packages/cli/src/WebhookHelpers.ts b/packages/cli/src/WebhookHelpers.ts index 4303be6c89..238c05fc6c 100644 --- a/packages/cli/src/WebhookHelpers.ts +++ b/packages/cli/src/WebhookHelpers.ts @@ -1,4 +1,5 @@ import * as express from 'express'; +import { get } from 'lodash'; import { ActiveExecutions, @@ -275,31 +276,63 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo if (responseData === 'firstEntryJson') { // Return the JSON data of the first entry data = returnData.data!.main[0]![0].json; + + const responsePropertyName = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responsePropertyName'], undefined); + + if (responsePropertyName !== undefined) { + data = get(data, responsePropertyName as string) as IDataObject; + } + + const responseContentType = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseContentType'], undefined); + + if (responseContentType !== undefined) { + // Send the webhook response manually to be able to set the content-type + res.setHeader('Content-Type', responseContentType as string); + + // Returning an object, boolean, number, ... causes problems so make sure to stringify if needed + if (data !== null && data !== undefined && ['Buffer', 'String'].includes(data.constructor.name)) { + res.end(data); + } else { + res.end(JSON.stringify(data)); + } + + responseCallback(null, { + noWebhookResponse: true, + }); + didSendResponse = true; + } + } else if (responseData === 'firstEntryBinary') { // Return the binary data of the first entry data = returnData.data!.main[0]![0]; if (data.binary === undefined) { responseCallback(new Error('No binary data to return got found.'), {}); + didSendResponse = true; } const responseBinaryPropertyName = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseBinaryPropertyName'], 'data'); - if (responseBinaryPropertyName === undefined) { + if (responseBinaryPropertyName === undefined && didSendResponse === false) { responseCallback(new Error('No "responseBinaryPropertyName" is set.'), {}); + didSendResponse = true; } const binaryData = (data.binary as IBinaryKeyData)[responseBinaryPropertyName as string]; - if (binaryData === undefined) { + if (binaryData === undefined && didSendResponse === false) { responseCallback(new Error(`The binary property "${responseBinaryPropertyName}" which should be returned does not exist.`), {}); + didSendResponse = true; } - // Send the webhook response manually - res.setHeader('Content-Type', binaryData.mimeType); - res.end(Buffer.from(binaryData.data, BINARY_ENCODING)); + if (didSendResponse === false) { + // Send the webhook response manually + res.setHeader('Content-Type', binaryData.mimeType); + res.end(Buffer.from(binaryData.data, BINARY_ENCODING)); + + responseCallback(null, { + noWebhookResponse: true, + }); + } - responseCallback(null, { - noWebhookResponse: true, - }); } else { // Return the JSON data of all the entries data = []; @@ -308,10 +341,12 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo } } - responseCallback(null, { - data, - responseCode, - }); + if (didSendResponse === false) { + responseCallback(null, { + data, + responseCode, + }); + } } didSendResponse = true; diff --git a/packages/nodes-base/nodes/Webhook.node.ts b/packages/nodes-base/nodes/Webhook.node.ts index 2797d5e957..8bb09d0c05 100644 --- a/packages/nodes-base/nodes/Webhook.node.ts +++ b/packages/nodes-base/nodes/Webhook.node.ts @@ -75,6 +75,8 @@ export class Webhook implements INodeType { responseMode: '={{$parameter["responseMode"]}}', responseData: '={{$parameter["responseData"]}}', responseBinaryPropertyName: '={{$parameter["responseBinaryPropertyName"]}}', + responseContentType: '={{$parameter["options"]["responseContentType"]}}', + responsePropertyName: '={{$parameter["options"]["responsePropertyName"]}}', path: '={{$parameter["path"]}}', }, ], @@ -203,6 +205,42 @@ export class Webhook implements INodeType { }, description: 'Name of the binary property to return', }, + + { + displayName: 'Options', + name: 'options', + type: 'collection', + displayOptions: { + show: { + responseData: [ + 'firstEntryJson', + ], + responseMode: [ + 'lastNode', + ], + }, + }, + placeholder: 'Add Option', + default: {}, + options: [ + { + displayName: 'Response Content-Type', + name: 'responseContentType', + type: 'string', + default: '', + placeholder: 'application/xml', + description: 'Set a custom content-type to return if another one as the "application/json" should be returned.', + }, + { + displayName: 'Property Name', + name: 'responsePropertyName', + type: 'string', + default: 'data', + description: 'Name of the property to return the data of instead of the whole JSON.', + }, + ], + }, + ], }; diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 79f0a448e2..d4cfd62c79 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -467,6 +467,8 @@ export interface IWebhookDescription { name: string; path: string; responseBinaryPropertyName?: string; + responseContentType?: string; + responsePropertyName?: string; responseMode?: WebhookResponseMode | string; responseData?: WebhookResponseData | string; }