mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
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>
171 lines
4.0 KiB
TypeScript
171 lines
4.0 KiB
TypeScript
import { NodeVM } from '@n8n/vm2';
|
|
import type {
|
|
IDataObject,
|
|
IExecuteFunctions,
|
|
IBinaryData,
|
|
INode,
|
|
INodeExecutionData,
|
|
GenericValue,
|
|
} from 'n8n-workflow';
|
|
import { ApplicationError, NodeOperationError } from 'n8n-workflow';
|
|
|
|
import get from 'lodash/get';
|
|
import isEqual from 'lodash/isEqual';
|
|
import isObject from 'lodash/isObject';
|
|
import merge from 'lodash/merge';
|
|
import reduce from 'lodash/reduce';
|
|
|
|
export const compareItems = (
|
|
obj: INodeExecutionData,
|
|
obj2: INodeExecutionData,
|
|
keys: string[],
|
|
disableDotNotation: boolean,
|
|
_node: INode,
|
|
) => {
|
|
let result = true;
|
|
for (const key of keys) {
|
|
if (!disableDotNotation) {
|
|
if (!isEqual(get(obj.json, key), get(obj2.json, key))) {
|
|
result = false;
|
|
break;
|
|
}
|
|
} else {
|
|
if (!isEqual(obj.json[key], obj2.json[key])) {
|
|
result = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
|
|
export const flattenKeys = (obj: IDataObject, path: string[] = []): IDataObject => {
|
|
return !isObject(obj)
|
|
? { [path.join('.')]: obj }
|
|
: reduce(obj, (cum, next, key) => merge(cum, flattenKeys(next as IDataObject, [...path, key])), {}); //prettier-ignore
|
|
};
|
|
|
|
export const shuffleArray = (array: any[]) => {
|
|
for (let i = array.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[array[i], array[j]] = [array[j], array[i]];
|
|
}
|
|
};
|
|
|
|
export const prepareFieldsArray = (fields: string | string[], fieldName = 'Fields') => {
|
|
if (typeof fields === 'string') {
|
|
return fields
|
|
.split(',')
|
|
.map((entry) => entry.trim())
|
|
.filter((entry) => entry !== '');
|
|
}
|
|
if (Array.isArray(fields)) {
|
|
return fields;
|
|
}
|
|
throw new ApplicationError(
|
|
`The \'${fieldName}\' parameter must be a string of fields separated by commas or an array of strings.`,
|
|
{ level: 'warning' },
|
|
);
|
|
};
|
|
|
|
const returnRegExp = /\breturn\b/g;
|
|
|
|
export function sortByCode(
|
|
this: IExecuteFunctions,
|
|
items: INodeExecutionData[],
|
|
): INodeExecutionData[] {
|
|
const code = this.getNodeParameter('code', 0) as string;
|
|
if (!returnRegExp.test(code)) {
|
|
throw new NodeOperationError(
|
|
this.getNode(),
|
|
"Sort code doesn't return. Please add a 'return' statement to your code",
|
|
);
|
|
}
|
|
|
|
const mode = this.getMode();
|
|
const vm = new NodeVM({
|
|
console: mode === 'manual' ? 'redirect' : 'inherit',
|
|
sandbox: { items },
|
|
});
|
|
|
|
return vm.run(`module.exports = items.sort((a, b) => { ${code} })`);
|
|
}
|
|
|
|
type PartialBinaryData = Omit<IBinaryData, 'data'>;
|
|
const isBinaryUniqueSetup = () => {
|
|
const binaries: PartialBinaryData[] = [];
|
|
return (binary: IBinaryData) => {
|
|
for (const existingBinary of binaries) {
|
|
if (
|
|
existingBinary.mimeType === binary.mimeType &&
|
|
existingBinary.fileType === binary.fileType &&
|
|
existingBinary.fileSize === binary.fileSize &&
|
|
existingBinary.fileExtension === binary.fileExtension
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
binaries.push({
|
|
mimeType: binary.mimeType,
|
|
fileType: binary.fileType,
|
|
fileSize: binary.fileSize,
|
|
fileExtension: binary.fileExtension,
|
|
});
|
|
|
|
return true;
|
|
};
|
|
};
|
|
|
|
export function addBinariesToItem(
|
|
newItem: INodeExecutionData,
|
|
items: INodeExecutionData[],
|
|
uniqueOnly?: boolean,
|
|
) {
|
|
const isBinaryUnique = uniqueOnly ? isBinaryUniqueSetup() : undefined;
|
|
|
|
for (const item of items) {
|
|
if (item.binary === undefined) continue;
|
|
|
|
for (const key of Object.keys(item.binary)) {
|
|
if (!newItem.binary) newItem.binary = {};
|
|
let binaryKey = key;
|
|
const binary = item.binary[key];
|
|
|
|
if (isBinaryUnique && !isBinaryUnique(binary)) {
|
|
continue;
|
|
}
|
|
|
|
// If the binary key already exists add a suffix to it
|
|
let i = 1;
|
|
while (newItem.binary[binaryKey] !== undefined) {
|
|
binaryKey = `${key}_${i}`;
|
|
i++;
|
|
}
|
|
|
|
newItem.binary[binaryKey] = binary;
|
|
}
|
|
}
|
|
|
|
return newItem;
|
|
}
|
|
|
|
export function typeToNumber(value: GenericValue): number {
|
|
if (typeof value === 'object') {
|
|
if (Array.isArray(value)) return 9;
|
|
if (value === null) return 10;
|
|
if (value instanceof Date) return 11;
|
|
}
|
|
const types = {
|
|
_string: 1,
|
|
_number: 2,
|
|
_bigint: 3,
|
|
_boolean: 4,
|
|
_symbol: 5,
|
|
_undefined: 6,
|
|
_object: 7,
|
|
_function: 8,
|
|
};
|
|
return types[`_${typeof value}`];
|
|
}
|