mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-26 13:14:07 -08:00
e77fd5d286
Ensure all errors in `nodes-base` are `ApplicationError` or children of it and contain no variables in the message, to continue normalizing all the backend errors we report to Sentry. Also, skip reporting to Sentry errors from user input and from external APIs. In future we should refine `ApplicationError` to more specific errors. Follow-up to: [#7877](https://github.com/n8n-io/n8n/pull/7877) - [x] Test workflows: https://github.com/n8n-io/n8n/actions/runs/7084627970 - [x] e2e: https://github.com/n8n-io/n8n/actions/runs/7084936861 --------- Co-authored-by: Michael Kret <michael.k@radency.com>
375 lines
9 KiB
TypeScript
375 lines
9 KiB
TypeScript
import type {
|
|
IExecuteFunctions,
|
|
INodeExecutionData,
|
|
INodeType,
|
|
INodeTypeDescription,
|
|
} from 'n8n-workflow';
|
|
import { ApplicationError, NodeApiError, NodeOperationError } from 'n8n-workflow';
|
|
import { setSeed, array as mfArray } from 'minifaker';
|
|
import {
|
|
generateCreditCard,
|
|
generateIPv4,
|
|
generateIPv6,
|
|
generateLocation,
|
|
generateMAC,
|
|
generateNanoid,
|
|
generateRandomAddress,
|
|
generateRandomEmail,
|
|
generateRandomUser,
|
|
generateURL,
|
|
generateUUID,
|
|
generateVersion,
|
|
} from './randomData';
|
|
import { generateGarbageMemory, runGarbageCollector } from './functions';
|
|
|
|
export class DebugHelper implements INodeType {
|
|
description: INodeTypeDescription = {
|
|
displayName: 'DebugHelper',
|
|
name: 'debugHelper',
|
|
icon: 'file:DebugHelper.svg',
|
|
group: ['output'],
|
|
subtitle: '={{$parameter["category"]}}',
|
|
description: 'Causes problems intentionally and generates useful data for debugging',
|
|
version: 1,
|
|
defaults: {
|
|
name: 'DebugHelper',
|
|
},
|
|
inputs: ['main'],
|
|
outputs: ['main'],
|
|
credentials: [],
|
|
properties: [
|
|
{
|
|
displayName: 'Category',
|
|
name: 'category',
|
|
type: 'options',
|
|
noDataExpression: true,
|
|
options: [
|
|
{
|
|
name: 'Do Nothing',
|
|
value: 'doNothing',
|
|
description: 'Does nothing',
|
|
},
|
|
{
|
|
name: 'Throw Error',
|
|
value: 'throwError',
|
|
description: 'Throws an error with the specified type and message',
|
|
},
|
|
{
|
|
name: 'Out Of Memory',
|
|
value: 'oom',
|
|
description: 'Generates a large amount of memory to cause an out of memory error',
|
|
},
|
|
{
|
|
name: 'Generate Random Data',
|
|
value: 'randomData',
|
|
description: 'Generates random data sets',
|
|
},
|
|
],
|
|
default: 'throwError',
|
|
},
|
|
{
|
|
displayName: 'Error Type',
|
|
name: 'throwErrorType',
|
|
type: 'options',
|
|
noDataExpression: true,
|
|
options: [
|
|
{
|
|
name: 'NodeApiError',
|
|
value: 'NodeApiError',
|
|
},
|
|
{
|
|
name: 'NodeOperationError',
|
|
value: 'NodeOperationError',
|
|
},
|
|
{
|
|
name: 'Error',
|
|
value: 'Error',
|
|
},
|
|
],
|
|
default: 'NodeApiError',
|
|
displayOptions: {
|
|
show: {
|
|
category: ['throwError'],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
displayName: 'Error Message',
|
|
name: 'throwErrorMessage',
|
|
type: 'string',
|
|
default: 'Node has thrown an error',
|
|
description: 'The message to send as part of the error',
|
|
displayOptions: {
|
|
show: {
|
|
category: ['throwError'],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
displayName: 'Memory Size to Generate',
|
|
name: 'memorySizeValue',
|
|
type: 'number',
|
|
default: 10,
|
|
description: 'The approximate amount of memory to generate. Be generous...',
|
|
displayOptions: {
|
|
show: {
|
|
category: ['oom'],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
displayName: 'Data Type',
|
|
name: 'randomDataType',
|
|
type: 'options',
|
|
noDataExpression: true,
|
|
options: [
|
|
{
|
|
name: 'Address',
|
|
value: 'address',
|
|
},
|
|
{
|
|
name: 'Coordinates',
|
|
value: 'latLong',
|
|
},
|
|
{
|
|
name: 'Credit Card',
|
|
value: 'creditCard',
|
|
},
|
|
{
|
|
name: 'Email',
|
|
value: 'email',
|
|
},
|
|
{
|
|
name: 'IPv4',
|
|
value: 'ipv4',
|
|
},
|
|
{
|
|
name: 'IPv6',
|
|
value: 'ipv6',
|
|
},
|
|
{
|
|
name: 'MAC',
|
|
value: 'macAddress',
|
|
},
|
|
{
|
|
name: 'NanoIds',
|
|
value: 'nanoid',
|
|
},
|
|
{
|
|
name: 'URL',
|
|
value: 'url',
|
|
},
|
|
{
|
|
name: 'User Data',
|
|
value: 'user',
|
|
},
|
|
{
|
|
name: 'UUID',
|
|
value: 'uuid',
|
|
},
|
|
{
|
|
name: 'Version',
|
|
value: 'semver',
|
|
},
|
|
],
|
|
default: 'user',
|
|
displayOptions: {
|
|
show: {
|
|
category: ['randomData'],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
displayName: 'NanoId Alphabet',
|
|
name: 'nanoidAlphabet',
|
|
type: 'string',
|
|
default: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
|
description: 'The alphabet to use for generating the nanoIds',
|
|
displayOptions: {
|
|
show: {
|
|
category: ['randomData'],
|
|
randomDataType: ['nanoid'],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
displayName: 'NanoId Length',
|
|
name: 'nanoidLength',
|
|
type: 'string',
|
|
default: '16',
|
|
description: 'The length of each nanoIds',
|
|
displayOptions: {
|
|
show: {
|
|
category: ['randomData'],
|
|
randomDataType: ['nanoid'],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
displayName: 'Seed',
|
|
name: 'randomDataSeed',
|
|
type: 'string',
|
|
default: '',
|
|
placeholder: 'Leave empty for random seed',
|
|
description:
|
|
'If set, seed to use for generating the data (same seed will generate the same data)',
|
|
displayOptions: {
|
|
show: {
|
|
category: ['randomData'],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
displayName: 'Number of Items to Generate',
|
|
name: 'randomDataCount',
|
|
type: 'number',
|
|
default: 10,
|
|
description: 'The number of random data items to generate into an array',
|
|
displayOptions: {
|
|
show: {
|
|
category: ['randomData'],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
displayName: 'Output as Single Array',
|
|
name: 'randomDataSingleArray',
|
|
type: 'boolean',
|
|
default: false,
|
|
description: 'Whether to output a single array instead of multiple items',
|
|
displayOptions: {
|
|
show: {
|
|
category: ['randomData'],
|
|
},
|
|
},
|
|
},
|
|
],
|
|
};
|
|
|
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
const items = this.getInputData();
|
|
const returnData: INodeExecutionData[] = [];
|
|
const category = this.getNodeParameter('category', 0) as string;
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
try {
|
|
switch (category) {
|
|
case 'doNothing':
|
|
// as it says on the tin...
|
|
break;
|
|
case 'throwError':
|
|
const throwErrorType = this.getNodeParameter('throwErrorType', 0) as string;
|
|
const throwErrorMessage = this.getNodeParameter('throwErrorMessage', 0) as string;
|
|
switch (throwErrorType) {
|
|
case 'NodeApiError':
|
|
throw new NodeApiError(
|
|
this.getNode(),
|
|
{ message: throwErrorMessage },
|
|
{ message: throwErrorMessage },
|
|
);
|
|
case 'NodeOperationError':
|
|
throw new NodeOperationError(this.getNode(), throwErrorMessage, {
|
|
message: throwErrorMessage,
|
|
});
|
|
case 'Error':
|
|
// eslint-disable-next-line n8n-nodes-base/node-execute-block-wrong-error-thrown
|
|
throw new ApplicationError(throwErrorMessage);
|
|
default:
|
|
break;
|
|
}
|
|
case 'oom':
|
|
const memorySizeValue = this.getNodeParameter('memorySizeValue', 0) as number;
|
|
runGarbageCollector();
|
|
const memUsed = generateGarbageMemory(memorySizeValue);
|
|
items[i].json = memUsed;
|
|
returnData.push(items[i]);
|
|
break;
|
|
case 'randomData':
|
|
const randomDataType = this.getNodeParameter('randomDataType', 0) as string;
|
|
const randomDataCount = this.getNodeParameter('randomDataCount', 0) as number;
|
|
const randomDataSeed = this.getNodeParameter('randomDataSeed', 0) as string;
|
|
const randomDataSingleArray = this.getNodeParameter(
|
|
'randomDataSingleArray',
|
|
0,
|
|
) as boolean;
|
|
const newItem: INodeExecutionData = {
|
|
json: {},
|
|
pairedItem: { item: i },
|
|
};
|
|
if (randomDataSeed !== '') {
|
|
setSeed(randomDataSeed);
|
|
}
|
|
|
|
let randomFn: () => any = generateRandomUser;
|
|
switch (randomDataType) {
|
|
case 'user':
|
|
randomFn = generateRandomUser;
|
|
break;
|
|
case 'email':
|
|
randomFn = generateRandomEmail;
|
|
break;
|
|
case 'address':
|
|
randomFn = generateRandomAddress;
|
|
break;
|
|
case 'creditCard':
|
|
randomFn = generateCreditCard;
|
|
break;
|
|
case 'uuid':
|
|
randomFn = generateUUID;
|
|
break;
|
|
case 'macAddress':
|
|
randomFn = generateMAC;
|
|
break;
|
|
case 'ipv4':
|
|
randomFn = generateIPv4;
|
|
break;
|
|
case 'ipv6':
|
|
randomFn = generateIPv6;
|
|
break;
|
|
case 'latLong':
|
|
randomFn = generateLocation;
|
|
break;
|
|
case 'semver':
|
|
randomFn = generateVersion;
|
|
break;
|
|
case 'url':
|
|
randomFn = generateURL;
|
|
break;
|
|
case 'nanoid':
|
|
const nanoidAlphabet = this.getNodeParameter('nanoidAlphabet', 0) as string;
|
|
const nanoidLength = this.getNodeParameter('nanoidLength', 0) as string;
|
|
randomFn = () => generateNanoid(nanoidAlphabet, nanoidLength);
|
|
break;
|
|
}
|
|
const generatedItems = mfArray(randomDataCount, randomFn);
|
|
if (randomDataSingleArray) {
|
|
newItem.json = { generatedItems };
|
|
returnData.push(newItem);
|
|
} else {
|
|
for (const generatedItem of generatedItems) {
|
|
returnData.push({
|
|
json: generatedItem,
|
|
pairedItem: { item: i },
|
|
});
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} catch (error) {
|
|
if (this.continueOnFail()) {
|
|
const executionErrorData = this.helpers.constructExecutionMetaData(
|
|
this.helpers.returnJsonArray({ error: error.message }),
|
|
{ itemData: { item: i } },
|
|
);
|
|
returnData.push(...executionErrorData);
|
|
continue;
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
return [returnData];
|
|
}
|
|
}
|