diff --git a/packages/cli/src/WebhookHelpers.ts b/packages/cli/src/WebhookHelpers.ts index eb7760d8fc..d39e3f5dee 100644 --- a/packages/cli/src/WebhookHelpers.ts +++ b/packages/cli/src/WebhookHelpers.ts @@ -149,6 +149,21 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo }; } + if (webhookData.webhookDescription['responseHeaders'] !== undefined) { + const responseHeaders = workflow.getComplexParameterValue(workflowStartNode, webhookData.webhookDescription['responseHeaders'], undefined) as { + entries?: Array<{ + name: string; + value: string; + }> | undefined; + }; + + if (responseHeaders !== undefined && responseHeaders['entries'] !== undefined) { + for (const item of responseHeaders['entries']) { + res.setHeader(item['name'], item['value']); + } + } + } + if (webhookResultData.noWebhookResponse === true && didSendResponse === false) { // The response got already send responseCallback(null, { diff --git a/packages/nodes-base/nodes/Webhook.node.ts b/packages/nodes-base/nodes/Webhook.node.ts index 96268eab24..03044ebfe7 100644 --- a/packages/nodes-base/nodes/Webhook.node.ts +++ b/packages/nodes-base/nodes/Webhook.node.ts @@ -85,6 +85,7 @@ export class Webhook implements INodeType { responseBinaryPropertyName: '={{$parameter["responseBinaryPropertyName"]}}', responseContentType: '={{$parameter["options"]["responseContentType"]}}', responsePropertyName: '={{$parameter["options"]["responsePropertyName"]}}', + responseHeaders: '={{$parameter["options"]["responseHeaders"]}}', path: '={{$parameter["path"]}}', }, ], @@ -268,6 +269,39 @@ export class Webhook implements INodeType { placeholder: 'application/xml', description: 'Set a custom content-type to return if another one as the "application/json" should be returned.', }, + { + displayName: 'Response Headers', + name: 'responseHeaders', + placeholder: 'Add Response Header', + description: 'Add headers to the webhook response.', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'entries', + displayName: 'Entries', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'Name of the header.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'Value of the header.', + }, + ] + }, + ], + }, { displayName: 'Property Name', name: 'responsePropertyName', diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index 6c1c48ad56..9a7d3ef1eb 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -734,7 +734,37 @@ export class Workflow { return this.getParameterValue(parameterValue, runData, runIndex, itemIndex, node.name, connectionInputData) as boolean | number | string | undefined; } + /** + * Resolves value of complex parameter. But does not work for workflow-data. + * + * @param {INode} node + * @param {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} parameterValue + * @param {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined)} [defaultValue] + * @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined)} + * @memberof Workflow + */ + getComplexParameterValue(node: INode, parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], defaultValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined = undefined): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined { + if (parameterValue === undefined) { + // Value is not set so return the default + return defaultValue; + } + // Get the value of the node (can be an expression) + const runIndex = 0; + const itemIndex = 0; + const connectionInputData: INodeExecutionData[] = []; + const runData: IRunExecutionData = { + resultData: { + runData: {}, + } + }; + + // Resolve the "outer" main values + const returnData = this.getParameterValue(parameterValue, runData, runIndex, itemIndex, node.name, connectionInputData); + + // Resolve the "inner" values + return this.getParameterValue(returnData, runData, runIndex, itemIndex, node.name, connectionInputData); + } /** * Returns from which of the given nodes the workflow should get started from