Skip to content

Commit

Permalink
fix(Respond to Webhook Node): Continue on fail and error branch suppo…
Browse files Browse the repository at this point in the history
…rt (#9115)
  • Loading branch information
michael-radency committed Apr 11, 2024
1 parent 064e8f4 commit 86a20f6
Showing 1 changed file with 129 additions and 113 deletions.
242 changes: 129 additions & 113 deletions packages/nodes-base/nodes/RespondToWebhook/RespondToWebhook.node.ts
Expand Up @@ -11,7 +11,7 @@ import type {
import { jsonParse, BINARY_ENCODING, NodeOperationError } from 'n8n-workflow';
import set from 'lodash/set';
import jwt from 'jsonwebtoken';
import { formatPrivateKey } from '../../utils/utilities';
import { formatPrivateKey, generatePairedItemData } from '../../utils/utilities';

export class RespondToWebhook implements INodeType {
description: INodeTypeDescription = {
Expand Down Expand Up @@ -287,141 +287,157 @@ export class RespondToWebhook implements INodeType {
};

async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const nodeVersion = this.getNode().typeVersion;

if (nodeVersion >= 1.1) {
const connectedNodes = this.getParentNodes(this.getNode().name);
if (!connectedNodes.some((node) => node.type === 'n8n-nodes-base.webhook')) {
throw new NodeOperationError(
this.getNode(),
new Error('No Webhook node found in the workflow'),
{
description:
'Insert a Webhook node to your workflow and set the “Respond” parameter to “Using Respond to Webhook Node” ',
},
);
try {
if (nodeVersion >= 1.1) {
const connectedNodes = this.getParentNodes(this.getNode().name);
if (!connectedNodes.some((node) => node.type === 'n8n-nodes-base.webhook')) {
throw new NodeOperationError(
this.getNode(),
new Error('No Webhook node found in the workflow'),
{
description:
'Insert a Webhook node to your workflow and set the “Respond” parameter to “Using Respond to Webhook Node” ',
},
);
}
}
}
const items = this.getInputData();

const respondWith = this.getNodeParameter('respondWith', 0) as string;
const options = this.getNodeParameter('options', 0, {});
const respondWith = this.getNodeParameter('respondWith', 0) as string;
const options = this.getNodeParameter('options', 0, {});

const headers = {} as IDataObject;
if (options.responseHeaders) {
for (const header of (options.responseHeaders as IDataObject).entries as IDataObject[]) {
if (typeof header.name !== 'string') {
header.name = header.name?.toString();
const headers = {} as IDataObject;
if (options.responseHeaders) {
for (const header of (options.responseHeaders as IDataObject).entries as IDataObject[]) {
if (typeof header.name !== 'string') {
header.name = header.name?.toString();
}
headers[header.name?.toLowerCase() as string] = header.value?.toString();
}
headers[header.name?.toLowerCase() as string] = header.value?.toString();
}
}

let statusCode = (options.responseCode as number) || 200;
let responseBody: IN8nHttpResponse | Readable;
if (respondWith === 'json') {
const responseBodyParameter = this.getNodeParameter('responseBody', 0) as string;
if (responseBodyParameter) {
if (typeof responseBodyParameter === 'object') {
responseBody = responseBodyParameter;
} else {
try {
responseBody = jsonParse(responseBodyParameter);
} catch (error) {
throw new NodeOperationError(this.getNode(), error as Error, {
message: "Invalid JSON in 'Response Body' field",
description:
"Check that the syntax of the JSON in the 'Response Body' parameter is valid",
});
let statusCode = (options.responseCode as number) || 200;
let responseBody: IN8nHttpResponse | Readable;
if (respondWith === 'json') {
const responseBodyParameter = this.getNodeParameter('responseBody', 0) as string;
if (responseBodyParameter) {
if (typeof responseBodyParameter === 'object') {
responseBody = responseBodyParameter;
} else {
try {
responseBody = jsonParse(responseBodyParameter);
} catch (error) {
throw new NodeOperationError(this.getNode(), error as Error, {
message: "Invalid JSON in 'Response Body' field",
description:
"Check that the syntax of the JSON in the 'Response Body' parameter is valid",
});
}
}
}
}
} else if (respondWith === 'jwt') {
try {
const { keyType, secret, algorithm, privateKey } = (await this.getCredentials(
'jwtAuth',
)) as {
keyType: 'passphrase' | 'pemKey';
privateKey: string;
secret: string;
algorithm: jwt.Algorithm;
};
} else if (respondWith === 'jwt') {
try {
const { keyType, secret, algorithm, privateKey } = (await this.getCredentials(
'jwtAuth',
)) as {
keyType: 'passphrase' | 'pemKey';
privateKey: string;
secret: string;
algorithm: jwt.Algorithm;
};

let secretOrPrivateKey;
let secretOrPrivateKey;

if (keyType === 'passphrase') {
secretOrPrivateKey = secret;
} else {
secretOrPrivateKey = formatPrivateKey(privateKey);
if (keyType === 'passphrase') {
secretOrPrivateKey = secret;
} else {
secretOrPrivateKey = formatPrivateKey(privateKey);
}
const payload = this.getNodeParameter('payload', 0, {}) as IDataObject;
const token = jwt.sign(payload, secretOrPrivateKey, { algorithm });
responseBody = { token };
} catch (error) {
throw new NodeOperationError(this.getNode(), error as Error, {
message: 'Error signing JWT token',
});
}
const payload = this.getNodeParameter('payload', 0, {}) as IDataObject;
const token = jwt.sign(payload, secretOrPrivateKey, { algorithm });
responseBody = { token };
} catch (error) {
throw new NodeOperationError(this.getNode(), error as Error, {
message: 'Error signing JWT token',
});
}
} else if (respondWith === 'allIncomingItems') {
const respondItems = items.map((item) => item.json);
responseBody = options.responseKey
? set({}, options.responseKey as string, respondItems)
: respondItems;
} else if (respondWith === 'firstIncomingItem') {
responseBody = options.responseKey
? set({}, options.responseKey as string, items[0].json)
: items[0].json;
} else if (respondWith === 'text') {
responseBody = this.getNodeParameter('responseBody', 0) as string;
} else if (respondWith === 'binary') {
const item = items[0];
} else if (respondWith === 'allIncomingItems') {
const respondItems = items.map((item) => item.json);
responseBody = options.responseKey
? set({}, options.responseKey as string, respondItems)
: respondItems;
} else if (respondWith === 'firstIncomingItem') {
responseBody = options.responseKey
? set({}, options.responseKey as string, items[0].json)
: items[0].json;
} else if (respondWith === 'text') {
responseBody = this.getNodeParameter('responseBody', 0) as string;
} else if (respondWith === 'binary') {
const item = items[0];

if (item.binary === undefined) {
throw new NodeOperationError(this.getNode(), 'No binary data exists on the first item!');
}
if (item.binary === undefined) {
throw new NodeOperationError(this.getNode(), 'No binary data exists on the first item!');
}

let responseBinaryPropertyName: string;
let responseBinaryPropertyName: string;

const responseDataSource = this.getNodeParameter('responseDataSource', 0) as string;
const responseDataSource = this.getNodeParameter('responseDataSource', 0) as string;

if (responseDataSource === 'set') {
responseBinaryPropertyName = this.getNodeParameter('inputFieldName', 0) as string;
} else {
const binaryKeys = Object.keys(item.binary);
if (binaryKeys.length === 0) {
throw new NodeOperationError(this.getNode(), 'No binary data exists on the first item!');
if (responseDataSource === 'set') {
responseBinaryPropertyName = this.getNodeParameter('inputFieldName', 0) as string;
} else {
const binaryKeys = Object.keys(item.binary);
if (binaryKeys.length === 0) {
throw new NodeOperationError(
this.getNode(),
'No binary data exists on the first item!',
);
}
responseBinaryPropertyName = binaryKeys[0];
}
responseBinaryPropertyName = binaryKeys[0];
}

const binaryData = this.helpers.assertBinaryData(0, responseBinaryPropertyName);
if (binaryData.id) {
responseBody = { binaryData };
} else {
responseBody = Buffer.from(binaryData.data, BINARY_ENCODING);
headers['content-length'] = (responseBody as Buffer).length;
}
const binaryData = this.helpers.assertBinaryData(0, responseBinaryPropertyName);
if (binaryData.id) {
responseBody = { binaryData };
} else {
responseBody = Buffer.from(binaryData.data, BINARY_ENCODING);
headers['content-length'] = (responseBody as Buffer).length;
}

if (!headers['content-type']) {
headers['content-type'] = binaryData.mimeType;
if (!headers['content-type']) {
headers['content-type'] = binaryData.mimeType;
}
} else if (respondWith === 'redirect') {
headers.location = this.getNodeParameter('redirectURL', 0) as string;
statusCode = (options.responseCode as number) ?? 307;
} else if (respondWith !== 'noData') {
throw new NodeOperationError(
this.getNode(),
`The Response Data option "${respondWith}" is not supported!`,
);
}
} else if (respondWith === 'redirect') {
headers.location = this.getNodeParameter('redirectURL', 0) as string;
statusCode = (options.responseCode as number) ?? 307;
} else if (respondWith !== 'noData') {
throw new NodeOperationError(
this.getNode(),
`The Response Data option "${respondWith}" is not supported!`,
);
}

const response: IN8nHttpFullResponse = {
body: responseBody,
headers,
statusCode,
};
const response: IN8nHttpFullResponse = {
body: responseBody,
headers,
statusCode,
};

this.sendResponse(response);
this.sendResponse(response);
} catch (error) {
if (this.continueOnFail()) {
const itemData = generatePairedItemData(items.length);
const returnData = this.helpers.constructExecutionMetaData(
[{ json: { error: error.message } }],
{ itemData },
);
return [returnData];
}

throw error;
}

return [items];
}
Expand Down

0 comments on commit 86a20f6

Please sign in to comment.