refactor(core): fixes n8n-local-rules/no-json-parse-json-stringify warnings (#4407)

* 🔨 fixes

* 🔨 set rule to error
This commit is contained in:
Michael Kret 2022-10-21 18:24:58 +03:00 committed by GitHub
parent e10128cbea
commit 9d6a2c32d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 75 additions and 50 deletions

View file

@ -323,8 +323,7 @@ const config = (module.exports = {
// TODO: set to `error` and fix offenses // TODO: set to `error` and fix offenses
'n8n-local-rules/no-uncaught-json-parse': 'warn', 'n8n-local-rules/no-uncaught-json-parse': 'warn',
// TODO: set to `error` and fix offenses 'n8n-local-rules/no-json-parse-json-stringify': 'error',
'n8n-local-rules/no-json-parse-json-stringify': 'warn',
// ****************************************************************** // ******************************************************************
// overrides to base ruleset // overrides to base ruleset

View file

@ -2,6 +2,7 @@ import { ContainerOptions, create_container, EventContext, Message, ReceiverOpti
import { ITriggerFunctions } from 'n8n-core'; import { ITriggerFunctions } from 'n8n-core';
import { import {
deepCopy,
IDataObject, IDataObject,
INodeType, INodeType,
INodeTypeDescription, INodeTypeDescription,
@ -172,20 +173,20 @@ export class AmqpTrigger implements INodeType {
if (options.jsonConvertByteArrayToString === true && data.body.content !== undefined) { if (options.jsonConvertByteArrayToString === true && data.body.content !== undefined) {
// The buffer is not ready... Stringify and parse back to load it. // The buffer is not ready... Stringify and parse back to load it.
const cont = JSON.stringify(data.body.content); const cont = deepCopy(data.body.content);
data.body = String.fromCharCode.apply(null, JSON.parse(cont).data); data.body = String.fromCharCode.apply(null, cont.data);
} }
if (options.jsonConvertByteArrayToString === true && data.body.content !== undefined) { if (options.jsonConvertByteArrayToString === true && data.body.content !== undefined) {
// The buffer is not ready... Stringify and parse back to load it. // The buffer is not ready... Stringify and parse back to load it.
const cont = JSON.stringify(data.body.content); const cont = deepCopy(data.body.content);
data.body = String.fromCharCode.apply(null, JSON.parse(cont).data); data.body = String.fromCharCode.apply(null, cont.data);
} }
if (options.jsonConvertByteArrayToString === true && data.body.content !== undefined) { if (options.jsonConvertByteArrayToString === true && data.body.content !== undefined) {
// The buffer is not ready... Stringify and parse back to load it. // The buffer is not ready... Stringify and parse back to load it.
const content = JSON.stringify(data.body.content); const content = deepCopy(data.body.content);
data.body = String.fromCharCode.apply(null, JSON.parse(content).data); data.body = String.fromCharCode.apply(null, content.data);
} }
if (options.jsonParseBody === true) { if (options.jsonParseBody === true) {

View file

@ -6,6 +6,7 @@ import {
} from 'n8n-core'; } from 'n8n-core';
import { import {
deepCopy,
ICredentialDataDecryptedObject, ICredentialDataDecryptedObject,
IDataObject, IDataObject,
IHttpRequestOptions, IHttpRequestOptions,
@ -93,7 +94,7 @@ export function copyInputItem(item: INodeExecutionData, properties: string[]): I
if (item.json[property] === undefined) { if (item.json[property] === undefined) {
newItem[property] = null; newItem[property] = null;
} else { } else {
newItem[property] = JSON.parse(JSON.stringify(item.json[property])); newItem[property] = deepCopy(item.json[property]);
} }
} }
return newItem; return newItem;

View file

@ -1,4 +1,4 @@
import { IDataObject, INodeExecutionData } from 'n8n-workflow'; import { deepCopy, IDataObject, INodeExecutionData } from 'n8n-workflow';
import { import {
AdjustedPutItem, AdjustedPutItem,
@ -103,7 +103,7 @@ export function copyInputItem(item: INodeExecutionData, properties: string[]): I
if (item.json[property] === undefined) { if (item.json[property] === undefined) {
newItem[property] = null; newItem[property] = null;
} else { } else {
newItem[property] = JSON.parse(JSON.stringify(item.json[property])); newItem[property] = deepCopy(item.json[property]);
} }
} }
return newItem; return newItem;

View file

@ -3,6 +3,7 @@ import { set } from 'lodash';
import { IExecuteFunctions } from 'n8n-core'; import { IExecuteFunctions } from 'n8n-core';
import { import {
deepCopy,
ILoadOptionsFunctions, ILoadOptionsFunctions,
INodeExecutionData, INodeExecutionData,
INodePropertyOptions, INodePropertyOptions,
@ -483,7 +484,7 @@ export class Crypto implements INodeType {
if (dataPropertyName.includes('.')) { if (dataPropertyName.includes('.')) {
// Uses dot notation so copy all data // Uses dot notation so copy all data
newItem = { newItem = {
json: JSON.parse(JSON.stringify(item.json)), json: deepCopy(item.json),
pairedItem: { pairedItem: {
item: i, item: i,
}, },

View file

@ -1,6 +1,7 @@
import { IExecuteFunctions } from 'n8n-core'; import { IExecuteFunctions } from 'n8n-core';
import { import {
deepCopy,
IDataObject, IDataObject,
ILoadOptionsFunctions, ILoadOptionsFunctions,
INodeExecutionData, INodeExecutionData,
@ -431,7 +432,7 @@ export class DateTime implements INodeType {
if (dataPropertyName.includes('.')) { if (dataPropertyName.includes('.')) {
// Uses dot notation so copy all data // Uses dot notation so copy all data
newItem = { newItem = {
json: JSON.parse(JSON.stringify(item.json)), json: deepCopy(item.json),
pairedItem: { pairedItem: {
item: i, item: i,
}, },
@ -475,7 +476,7 @@ export class DateTime implements INodeType {
if (dataPropertyName.includes('.')) { if (dataPropertyName.includes('.')) {
// Uses dot notation so copy all data // Uses dot notation so copy all data
newItem = { newItem = {
json: JSON.parse(JSON.stringify(item.json)), json: deepCopy(item.json),
pairedItem: { pairedItem: {
item: i, item: i,
}, },

View file

@ -1,5 +1,6 @@
import { BINARY_ENCODING, IExecuteFunctions } from 'n8n-core'; import { BINARY_ENCODING, IExecuteFunctions } from 'n8n-core';
import { import {
deepCopy,
IDataObject, IDataObject,
ILoadOptionsFunctions, ILoadOptionsFunctions,
INodeExecutionData, INodeExecutionData,
@ -1269,8 +1270,8 @@ export class EditImage implements INodeType {
Object.assign(newItem.binary, item.binary); Object.assign(newItem.binary, item.binary);
// Make a deep copy of the binary data we change // Make a deep copy of the binary data we change
if (newItem.binary![dataPropertyName as string]) { if (newItem.binary![dataPropertyName as string]) {
newItem.binary![dataPropertyName as string] = JSON.parse( newItem.binary![dataPropertyName as string] = deepCopy(
JSON.stringify(newItem.binary![dataPropertyName as string]), newItem.binary![dataPropertyName as string],
); );
} }
} }

View file

@ -1,5 +1,6 @@
import { IExecuteFunctions } from 'n8n-core'; import { IExecuteFunctions } from 'n8n-core';
import { import {
deepCopy,
IBinaryKeyData, IBinaryKeyData,
IDataObject, IDataObject,
INodeExecutionData, INodeExecutionData,
@ -67,7 +68,7 @@ return items;`,
let items = this.getInputData(); let items = this.getInputData();
// Copy the items as they may get changed in the functions // Copy the items as they may get changed in the functions
items = JSON.parse(JSON.stringify(items)); items = deepCopy(items);
// Assign item indexes // Assign item indexes
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
@ -82,7 +83,7 @@ return items;`,
inputData[key] = cleanupData(inputData[key] as IDataObject); inputData[key] = cleanupData(inputData[key] as IDataObject);
} else { } else {
// Is some special object like a Date so stringify // Is some special object like a Date so stringify
inputData[key] = JSON.parse(JSON.stringify(inputData[key])); inputData[key] = deepCopy(inputData[key]);
} }
} }
}); });

View file

@ -1,5 +1,6 @@
import { IExecuteFunctions } from 'n8n-core'; import { IExecuteFunctions } from 'n8n-core';
import { import {
deepCopy,
IBinaryKeyData, IBinaryKeyData,
IDataObject, IDataObject,
INodeExecutionData, INodeExecutionData,
@ -74,7 +75,7 @@ return item;`,
inputData[key] = cleanupData(inputData[key] as IDataObject); inputData[key] = cleanupData(inputData[key] as IDataObject);
} else { } else {
// Is some special object like a Date so stringify // Is some special object like a Date so stringify
inputData[key] = JSON.parse(JSON.stringify(inputData[key])); inputData[key] = deepCopy(inputData[key]);
} }
} }
}); });
@ -89,7 +90,7 @@ return item;`,
item.index = itemIndex; item.index = itemIndex;
// Copy the items as they may get changed in the functions // Copy the items as they may get changed in the functions
item = JSON.parse(JSON.stringify(item)); item = deepCopy(item);
// Define the global objects for the custom function // Define the global objects for the custom function
const sandbox = { const sandbox = {

View file

@ -1,6 +1,7 @@
import { IExecuteFunctions } from 'n8n-core'; import { IExecuteFunctions } from 'n8n-core';
import { import {
deepCopy,
IDataObject, IDataObject,
INodeExecutionData, INodeExecutionData,
INodeType, INodeType,
@ -590,7 +591,7 @@ export class Markdown implements INodeType {
const markdownFromHTML = NodeHtmlMarkdown.translate(html, markdownOptions); const markdownFromHTML = NodeHtmlMarkdown.translate(html, markdownOptions);
const newItem = JSON.parse(JSON.stringify(items[i].json)); const newItem = deepCopy(items[i].json);
set(newItem, destinationKey, markdownFromHTML); set(newItem, destinationKey, markdownFromHTML);
returnData.push(newItem); returnData.push(newItem);
} }
@ -605,7 +606,7 @@ export class Markdown implements INodeType {
Object.keys(options).forEach((key) => converter.setOption(key, options[key])); Object.keys(options).forEach((key) => converter.setOption(key, options[key]));
const htmlFromMarkdown = converter.makeHtml(markdown); const htmlFromMarkdown = converter.makeHtml(markdown);
const newItem = JSON.parse(JSON.stringify(items[i].json)); const newItem = deepCopy(items[i].json);
set(newItem, destinationKey, htmlFromMarkdown); set(newItem, destinationKey, htmlFromMarkdown);
returnData.push(newItem); returnData.push(newItem);

View file

@ -4,6 +4,7 @@ import { get } from 'lodash';
import { IExecuteFunctions } from 'n8n-core'; import { IExecuteFunctions } from 'n8n-core';
import { import {
deepCopy,
GenericValue, GenericValue,
INodeExecutionData, INodeExecutionData,
INodeType, INodeType,
@ -427,7 +428,7 @@ export class MergeV1 implements INodeType {
continue; continue;
} else if (mode === 'mergeByKey') { } else if (mode === 'mergeByKey') {
// Copy the entry as the data gets changed // Copy the entry as the data gets changed
entry = JSON.parse(JSON.stringify(entry)); entry = deepCopy(entry);
for (key of Object.keys(copyData[referenceValue as string].json)) { for (key of Object.keys(copyData[referenceValue as string].json)) {
if (key === propertyName2) { if (key === propertyName2) {

View file

@ -1,4 +1,4 @@
import { IDataObject, INodeExecutionData } from 'n8n-workflow'; import { deepCopy, IDataObject, INodeExecutionData } from 'n8n-workflow';
import { ITables } from './TableInterface'; import { ITables } from './TableInterface';
/** /**
@ -15,7 +15,7 @@ export function copyInputItem(item: INodeExecutionData, properties: string[]): I
if (item.json[property] === undefined) { if (item.json[property] === undefined) {
newItem[property] = null; newItem[property] = null;
} else { } else {
newItem[property] = JSON.parse(JSON.stringify(item.json[property])); newItem[property] = deepCopy(item.json[property]);
} }
} }
return newItem; return newItem;

View file

@ -4,6 +4,7 @@ import { BINARY_ENCODING } from 'n8n-core';
import { IExecuteFunctions } from 'n8n-core'; import { IExecuteFunctions } from 'n8n-core';
import { import {
deepCopy,
IBinaryData, IBinaryData,
IDataObject, IDataObject,
INodeExecutionData, INodeExecutionData,
@ -364,7 +365,7 @@ export class MoveBinaryData implements INodeType {
newItem.json = JSON.parse(convertedValue); newItem.json = JSON.parse(convertedValue);
} else { } else {
// Does get added to existing data so copy it first // Does get added to existing data so copy it first
newItem.json = JSON.parse(JSON.stringify(item.json)); newItem.json = deepCopy(item.json);
if (options.keepAsBase64 !== true) { if (options.keepAsBase64 !== true) {
convertedValue = iconv.decode(buffer, encoding, { convertedValue = iconv.decode(buffer, encoding, {
@ -387,7 +388,7 @@ export class MoveBinaryData implements INodeType {
newItem.binary = item.binary; newItem.binary = item.binary;
} else { } else {
// Binary data will change so copy it // Binary data will change so copy it
newItem.binary = JSON.parse(JSON.stringify(item.binary)); newItem.binary = deepCopy(item.binary);
unset(newItem.binary, sourceKey); unset(newItem.binary, sourceKey);
} }
} else if (mode === 'jsonToBinary') { } else if (mode === 'jsonToBinary') {
@ -408,7 +409,7 @@ export class MoveBinaryData implements INodeType {
if (item.binary !== undefined) { if (item.binary !== undefined) {
// Item already has binary data so copy it // Item already has binary data so copy it
newItem.binary = JSON.parse(JSON.stringify(item.binary)); newItem.binary = deepCopy(item.binary);
} else { } else {
// Item does not have binary data yet so initialize empty // Item does not have binary data yet so initialize empty
newItem.binary = {}; newItem.binary = {};
@ -447,7 +448,7 @@ export class MoveBinaryData implements INodeType {
} else { } else {
// Data should not be kept and only one key has to get removed. So copy all // Data should not be kept and only one key has to get removed. So copy all
// data and then remove the not needed one // data and then remove the not needed one
newItem.json = JSON.parse(JSON.stringify(item.json)); newItem.json = deepCopy(item.json);
const sourceKey = this.getNodeParameter('sourceKey', itemIndex) as string; const sourceKey = this.getNodeParameter('sourceKey', itemIndex) as string;
unset(newItem.json, sourceKey); unset(newItem.json, sourceKey);

View file

@ -1,4 +1,11 @@
import { ICredentialDataDecryptedObject, IDataObject, ILoadOptionsFunctions, INodeExecutionData, INodeListSearchResult } from 'n8n-workflow'; import {
deepCopy,
ICredentialDataDecryptedObject,
IDataObject,
ILoadOptionsFunctions,
INodeExecutionData,
INodeListSearchResult,
} from 'n8n-workflow';
import mysql2 from 'mysql2/promise'; import mysql2 from 'mysql2/promise';
/** /**
@ -17,14 +24,16 @@ export function copyInputItems(items: INodeExecutionData[], properties: string[]
if (item.json[property] === undefined) { if (item.json[property] === undefined) {
newItem[property] = null; newItem[property] = null;
} else { } else {
newItem[property] = JSON.parse(JSON.stringify(item.json[property])); newItem[property] = deepCopy(item.json[property]);
} }
} }
return newItem; return newItem;
}); });
} }
export function createConnection(credentials: ICredentialDataDecryptedObject): Promise<mysql2.Connection> { export function createConnection(
credentials: ICredentialDataDecryptedObject,
): Promise<mysql2.Connection> {
const { ssl, caCertificate, clientCertificate, clientPrivateKey, ...baseCredentials } = const { ssl, caCertificate, clientCertificate, clientPrivateKey, ...baseCredentials } =
credentials; credentials;
@ -57,7 +66,7 @@ export async function searchTables(
ORDER BY table_name ORDER BY table_name
`; `;
const [rows] = await connection.query(sql); const [rows] = await connection.query(sql);
const results = (rows as IDataObject[]).map(r => ({ const results = (rows as IDataObject[]).map((r) => ({
name: r.TABLE_NAME as string, name: r.TABLE_NAME as string,
value: r.TABLE_NAME as string, value: r.TABLE_NAME as string,
})); }));

View file

@ -2,6 +2,7 @@ import { IExecuteFunctions } from 'n8n-core';
import { OptionsWithUri } from 'request'; import { OptionsWithUri } from 'request';
import { import {
deepCopy,
ICredentialsDecrypted, ICredentialsDecrypted,
ICredentialTestFunctions, ICredentialTestFunctions,
IDataObject, IDataObject,
@ -297,7 +298,7 @@ export class Odoo implements INodeType {
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> { async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
let items = this.getInputData(); let items = this.getInputData();
items = JSON.parse(JSON.stringify(items)); items = deepCopy(items);
const returnData: IDataObject[] = []; const returnData: IDataObject[] = [];
let responseData; let responseData;

View file

@ -1,5 +1,11 @@
import { IExecuteFunctions } from 'n8n-core'; import { IExecuteFunctions } from 'n8n-core';
import { IDataObject, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow'; import {
deepCopy,
IDataObject,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import { get, set, unset } from 'lodash'; import { get, set, unset } from 'lodash';
import { options } from 'rhea'; import { options } from 'rhea';
@ -165,7 +171,7 @@ export class RenameKeys implements INodeType {
// Copy the whole JSON data as data on any level can be renamed // Copy the whole JSON data as data on any level can be renamed
newItem = { newItem = {
json: JSON.parse(JSON.stringify(item.json)), json: deepCopy(item.json),
pairedItem: { pairedItem: {
item: itemIndex, item: itemIndex,
}, },

View file

@ -1,5 +1,6 @@
import { IExecuteFunctions } from 'n8n-core'; import { IExecuteFunctions } from 'n8n-core';
import { import {
deepCopy,
IDataObject, IDataObject,
INodeExecutionData, INodeExecutionData,
INodeParameters, INodeParameters,
@ -162,7 +163,7 @@ export class Set implements INodeType {
Object.assign(newItem.binary, item.binary); Object.assign(newItem.binary, item.binary);
} }
newItem.json = JSON.parse(JSON.stringify(item.json)); newItem.json = deepCopy(item.json);
} }
// Add boolean values // Add boolean values

View file

@ -1,4 +1,4 @@
import { IDataObject, INodeExecutionData } from 'n8n-workflow'; import { deepCopy, IDataObject, INodeExecutionData } from 'n8n-workflow';
import snowflake from 'snowflake-sdk'; import snowflake from 'snowflake-sdk';
@ -53,7 +53,7 @@ export function copyInputItems(items: INodeExecutionData[], properties: string[]
if (item.json[property] === undefined) { if (item.json[property] === undefined) {
newItem[property] = null; newItem[property] = null;
} else { } else {
newItem[property] = JSON.parse(JSON.stringify(item.json[property])); newItem[property] = deepCopy(item.json[property]);
} }
} }
return newItem; return newItem;

View file

@ -1,6 +1,7 @@
import { IHookFunctions, IWebhookFunctions } from 'n8n-core'; import { IHookFunctions, IWebhookFunctions } from 'n8n-core';
import { import {
deepCopy,
IDataObject, IDataObject,
ILoadOptionsFunctions, ILoadOptionsFunctions,
INodeExecutionData, INodeExecutionData,
@ -707,7 +708,7 @@ export class SurveyMonkeyTrigger implements INodeType {
responseData.questions = {}; responseData.questions = {};
// Map the "Map" to JSON // Map the "Map" to JSON
const tuples = JSON.parse(JSON.stringify([...responseQuestions])); const tuples = deepCopy([...responseQuestions]);
for (const [key, value] of tuples) { for (const [key, value] of tuples) {
responseData.questions[key] = value; responseData.questions[key] = value;
} }

View file

@ -1,4 +1,4 @@
import { IDataObject, INodeProperties } from 'n8n-workflow'; import { deepCopy, IDataObject, INodeProperties } from 'n8n-workflow';
import { groups } from './Json/Groups'; import { groups } from './Json/Groups';
@ -81,7 +81,7 @@ for (const tool of (tools as IDataObject).processors as IDataObject[]) {
tool: [tool.k], tool: [tool.k],
}, },
}, },
description: JSON.parse(JSON.stringify(description)), description: deepCopy(description),
}; };
let modifiedParam = null; let modifiedParam = null;
@ -109,6 +109,7 @@ for (const tool of (tools as IDataObject).processors as IDataObject[]) {
newParameters.push(currentParam); newParameters.push(currentParam);
} }
} }
// eslint-disable-next-line n8n-local-rules/no-json-parse-json-stringify
parameters = JSON.parse(JSON.stringify(newParameters)); parameters = JSON.parse(JSON.stringify(newParameters));
} else { } else {
parameters.push(parameter); parameters.push(parameter);

View file

@ -38,6 +38,7 @@ import {
NodeParameterValue, NodeParameterValue,
WebhookHttpMethod, WebhookHttpMethod,
} from './Interfaces'; } from './Interfaces';
import { deepCopy } from './utils';
import type { Workflow } from './Workflow'; import type { Workflow } from './Workflow';
@ -660,9 +661,7 @@ export function getNodeParameters(
} else if (returnDefaults) { } else if (returnDefaults) {
// Does not have values defined but defaults should be returned // Does not have values defined but defaults should be returned
if (Array.isArray(nodeProperties.default)) { if (Array.isArray(nodeProperties.default)) {
nodeParameters[nodeProperties.name] = JSON.parse( nodeParameters[nodeProperties.name] = deepCopy(nodeProperties.default);
JSON.stringify(nodeProperties.default),
);
} else { } else {
// As it is probably wrong for many nodes, do we keep on returning an empty array if // As it is probably wrong for many nodes, do we keep on returning an empty array if
// anything else than an array is set as default // anything else than an array is set as default
@ -690,7 +689,7 @@ export function getNodeParameters(
} }
} else if (returnDefaults) { } else if (returnDefaults) {
// Does not have values defined but defaults should be returned // Does not have values defined but defaults should be returned
nodeParameters[nodeProperties.name] = JSON.parse(JSON.stringify(nodeProperties.default)); nodeParameters[nodeProperties.name] = deepCopy(nodeProperties.default);
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name]; nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
} }
} else if (nodeProperties.type === 'fixedCollection') { } else if (nodeProperties.type === 'fixedCollection') {
@ -704,7 +703,7 @@ export function getNodeParameters(
let propertyValues = nodeValues[nodeProperties.name]; let propertyValues = nodeValues[nodeProperties.name];
if (returnDefaults) { if (returnDefaults) {
if (propertyValues === undefined) { if (propertyValues === undefined) {
propertyValues = JSON.parse(JSON.stringify(nodeProperties.default)); propertyValues = deepCopy(nodeProperties.default);
} }
} }
@ -819,9 +818,7 @@ export function getNodeParameters(
if (returnDefaults) { if (returnDefaults) {
// Set also when it has the default value // Set also when it has the default value
if (collectionValues === undefined) { if (collectionValues === undefined) {
nodeParameters[nodeProperties.name] = JSON.parse( nodeParameters[nodeProperties.name] = deepCopy(nodeProperties.default);
JSON.stringify(nodeProperties.default),
);
} else { } else {
nodeParameters[nodeProperties.name] = collectionValues; nodeParameters[nodeProperties.name] = collectionValues;
} }