mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
perf: update deepCopy (#4364)
* perf: update deepCopy * fix: using deepCopy in core and cli packages * fix: using deepCopy in editor * chore: formatting * fix: some micro optimisation in deepCopy
This commit is contained in:
parent
638d6f60d3
commit
1aa21ed3df
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
import { ICredentialDataDecryptedObject } from 'n8n-workflow';
|
||||
import { deepCopy, ICredentialDataDecryptedObject } from 'n8n-workflow';
|
||||
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import { CredentialTypes, GenericHelpers, ICredentialsOverwrite } from '.';
|
||||
|
@ -19,7 +19,7 @@ class CredentialsOverwritesClass {
|
|||
if (overwriteData !== undefined) {
|
||||
// If data is already given it can directly be set instead of
|
||||
// loaded from environment
|
||||
this.__setData(JSON.parse(JSON.stringify(overwriteData)));
|
||||
this.__setData(deepCopy(overwriteData));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -57,14 +57,11 @@ class CredentialsOverwritesClass {
|
|||
return data;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const returnData = JSON.parse(JSON.stringify(data));
|
||||
const returnData = deepCopy(data);
|
||||
// Overwrite only if there is currently no data set
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const key of Object.keys(overwrites)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
// @ts-ignore
|
||||
if ([null, undefined, ''].includes(returnData[key])) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
returnData[key] = overwrites[key];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ import {
|
|||
NodeParameterValueType,
|
||||
NodeExecutionWithMetadata,
|
||||
IPairedItemData,
|
||||
deepCopy,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { Agent } from 'https';
|
||||
|
@ -1641,7 +1642,7 @@ export async function getCredentials(
|
|||
*
|
||||
*/
|
||||
export function getNode(node: INode): INode {
|
||||
return JSON.parse(JSON.stringify(node));
|
||||
return deepCopy(node);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
USER_SETTINGS_FILE_NAME,
|
||||
USER_SETTINGS_SUBFOLDER,
|
||||
} from '.';
|
||||
import { deepCopy } from 'n8n-workflow';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { promisify } = require('util');
|
||||
|
@ -173,7 +174,7 @@ export async function writeUserSettings(
|
|||
}
|
||||
|
||||
await fsWriteFile(settingsPath, JSON.stringify(settingsToWrite, null, '\t'));
|
||||
settingsCache = JSON.parse(JSON.stringify(userSettings));
|
||||
settingsCache = deepCopy(userSettings);
|
||||
|
||||
return userSettings;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import {
|
|||
} from '@/Interface';
|
||||
|
||||
import {
|
||||
deepCopy,
|
||||
INodeProperties,
|
||||
INodePropertyOptions,
|
||||
} from 'n8n-workflow';
|
||||
|
@ -161,7 +162,7 @@ export default mixins(
|
|||
} else {
|
||||
// Everything else saves them directly as an array.
|
||||
newValue = get(this.nodeValues, `${this.path}.${optionName}`, []);
|
||||
newValue.push(JSON.parse(JSON.stringify(option.default)));
|
||||
newValue.push(deepCopy(option.default));
|
||||
}
|
||||
|
||||
parameterData = {
|
||||
|
@ -172,7 +173,7 @@ export default mixins(
|
|||
// Add a new option
|
||||
parameterData = {
|
||||
name,
|
||||
value: JSON.parse(JSON.stringify(option.default)),
|
||||
value: deepCopy(option.default),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -113,8 +113,10 @@ import {
|
|||
} from '@/Interface';
|
||||
|
||||
import {
|
||||
deepCopy,
|
||||
INodeParameters,
|
||||
INodePropertyCollection,
|
||||
NodeParameterValue,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { get } from 'lodash';
|
||||
|
@ -249,13 +251,13 @@ export default mixins(genericHelpers)
|
|||
// Multiple values are allowed so append option to array
|
||||
newParameterValue[optionParameter.name] = get(this.nodeValues, `${this.path}.${optionParameter.name}`, []);
|
||||
if (Array.isArray(optionParameter.default)) {
|
||||
(newParameterValue[optionParameter.name] as INodeParameters[]).push(...JSON.parse(JSON.stringify(optionParameter.default)));
|
||||
(newParameterValue[optionParameter.name] as INodeParameters[]).push(...deepCopy(optionParameter.default as INodeParameters[]));
|
||||
} else if (optionParameter.default !== '' && typeof optionParameter.default !== 'object') {
|
||||
(newParameterValue[optionParameter.name] as INodeParameters[]).push(JSON.parse(JSON.stringify(optionParameter.default)));
|
||||
(newParameterValue[optionParameter.name] as NodeParameterValue[]).push(deepCopy(optionParameter.default));
|
||||
}
|
||||
} else {
|
||||
// Add a new option
|
||||
newParameterValue[optionParameter.name] = JSON.parse(JSON.stringify(optionParameter.default));
|
||||
newParameterValue[optionParameter.name] = deepCopy(optionParameter.default);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ import { get } from 'lodash';
|
|||
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
||||
|
||||
import mixins from 'vue-typed-mixins';
|
||||
import { deepCopy } from "n8n-workflow";
|
||||
|
||||
export default mixins(genericHelpers)
|
||||
.extend({
|
||||
|
@ -87,7 +88,7 @@ export default mixins(genericHelpers)
|
|||
currentValue = [];
|
||||
}
|
||||
|
||||
currentValue.push(JSON.parse(JSON.stringify(this.parameter.default)));
|
||||
currentValue.push(deepCopy(this.parameter.default));
|
||||
|
||||
const parameterData = {
|
||||
name,
|
||||
|
|
|
@ -122,6 +122,7 @@ import {
|
|||
INodeProperties,
|
||||
NodeHelpers,
|
||||
NodeParameterValue,
|
||||
deepCopy,
|
||||
} from 'n8n-workflow';
|
||||
import { INodeUi, INodeUpdatePropertiesInformation, IUpdateInformation } from '@/Interface';
|
||||
|
||||
|
@ -427,7 +428,7 @@ export default mixins(externalHooks, genericHelpers, nodeHelpers).extend({
|
|||
// Value should be set
|
||||
if (typeof value === 'object') {
|
||||
// @ts-ignore
|
||||
Vue.set(get(this.nodeValues, nameParts.join('.')), lastNamePart, JSON.parse(JSON.stringify(value)));
|
||||
Vue.set(get(this.nodeValues, nameParts.join('.')), lastNamePart, deepCopy(value));
|
||||
} else {
|
||||
// @ts-ignore
|
||||
Vue.set(get(this.nodeValues, nameParts.join('.')), lastNamePart, value);
|
||||
|
@ -500,7 +501,7 @@ export default mixins(externalHooks, genericHelpers, nodeHelpers).extend({
|
|||
|
||||
// Copy the data because it is the data of vuex so make sure that
|
||||
// we do not edit it directly
|
||||
nodeParameters = JSON.parse(JSON.stringify(nodeParameters));
|
||||
nodeParameters = deepCopy(nodeParameters);
|
||||
|
||||
for (const parameterName of Object.keys(parameterData.value)) {
|
||||
//@ts-ignore
|
||||
|
@ -591,7 +592,7 @@ export default mixins(externalHooks, genericHelpers, nodeHelpers).extend({
|
|||
|
||||
// Copy the data because it is the data of vuex so make sure that
|
||||
// we do not edit it directly
|
||||
nodeParameters = JSON.parse(JSON.stringify(nodeParameters));
|
||||
nodeParameters = deepCopy(nodeParameters);
|
||||
|
||||
// Remove the 'parameters.' from the beginning to just have the
|
||||
// actual parameter name
|
||||
|
@ -734,7 +735,7 @@ export default mixins(externalHooks, genericHelpers, nodeHelpers).extend({
|
|||
}
|
||||
}
|
||||
|
||||
Vue.set(this.nodeValues, 'parameters', JSON.parse(JSON.stringify(this.node.parameters)));
|
||||
Vue.set(this.nodeValues, 'parameters', deepCopy(this.node.parameters));
|
||||
} else {
|
||||
this.nodeValid = false;
|
||||
}
|
||||
|
|
|
@ -97,6 +97,7 @@
|
|||
<script lang="ts">
|
||||
|
||||
import {
|
||||
deepCopy,
|
||||
INodeParameters,
|
||||
INodeProperties,
|
||||
INodeTypeDescription,
|
||||
|
@ -288,7 +289,7 @@ export default mixins(
|
|||
|
||||
if (parameterGotResolved === true) {
|
||||
if (this.path) {
|
||||
rawValues = JSON.parse(JSON.stringify(this.nodeValues));
|
||||
rawValues = deepCopy(this.nodeValues);
|
||||
set(rawValues, this.path, nodeValues);
|
||||
return this.displayParameter(rawValues, parameter, this.path, this.node);
|
||||
} else {
|
||||
|
|
|
@ -192,6 +192,7 @@ import { WORKFLOW_SETTINGS_MODAL_KEY } from '../constants';
|
|||
import mixins from 'vue-typed-mixins';
|
||||
|
||||
import { mapGetters } from "vuex";
|
||||
import { deepCopy } from "n8n-workflow";
|
||||
|
||||
export default mixins(
|
||||
externalHooks,
|
||||
|
@ -274,7 +275,7 @@ export default mixins(
|
|||
this.$showError(error, 'Problem loading settings', 'The following error occurred loading the data:');
|
||||
}
|
||||
|
||||
const workflowSettings = JSON.parse(JSON.stringify(this.$store.getters.workflowSettings));
|
||||
const workflowSettings = deepCopy(this.$store.getters.workflowSettings);
|
||||
|
||||
if (workflowSettings.timezone === undefined) {
|
||||
workflowSettings.timezone = 'DEFAULT';
|
||||
|
@ -536,7 +537,7 @@ export default mixins(
|
|||
}
|
||||
}
|
||||
|
||||
const oldSettings = JSON.parse(JSON.stringify(this.$store.getters.workflowSettings));
|
||||
const oldSettings = deepCopy(this.$store.getters.workflowSettings);
|
||||
|
||||
this.$store.commit('setWorkflowSettings', localWorkflowSettings);
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import {
|
|||
IExecuteData,
|
||||
INodeConnection,
|
||||
IWebhookDescription,
|
||||
deepCopy,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
|
@ -361,8 +362,8 @@ export const workflowHelpers = mixins(
|
|||
cachedWorkflow = new Workflow({
|
||||
id: workflowId,
|
||||
name: workflowName,
|
||||
nodes: copyData ? JSON.parse(JSON.stringify(nodes)) : nodes,
|
||||
connections: copyData? JSON.parse(JSON.stringify(connections)): connections,
|
||||
nodes: copyData ? deepCopy(nodes) : nodes,
|
||||
connections: copyData? deepCopy(connections): connections,
|
||||
active: false,
|
||||
nodeTypes,
|
||||
settings: this.$store.getters.workflowSettings,
|
||||
|
|
|
@ -99,6 +99,7 @@ import * as CanvasHelpers from './canvasHelpers';
|
|||
import mixins from 'vue-typed-mixins';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import {
|
||||
deepCopy,
|
||||
IConnection,
|
||||
IConnections,
|
||||
IDataObject,
|
||||
|
@ -459,7 +460,7 @@ export default mixins(
|
|||
this.$store.commit('setWorkflowExecutionData', data);
|
||||
this.$store.commit('setWorkflowPinData', data.workflowData.pinData);
|
||||
|
||||
await this.addNodes(JSON.parse(JSON.stringify(data.workflowData.nodes)), JSON.parse(JSON.stringify(data.workflowData.connections)));
|
||||
await this.addNodes(deepCopy(data.workflowData.nodes), deepCopy(data.workflowData.connections));
|
||||
this.$nextTick(() => {
|
||||
this.zoomToFit();
|
||||
this.$store.commit('setStateDirty', false);
|
||||
|
@ -2193,7 +2194,7 @@ export default mixins(
|
|||
|
||||
// Deep copy the data so that data on lower levels of the node-properties do
|
||||
// not share objects
|
||||
const newNodeData = JSON.parse(JSON.stringify(this.getNodeDataToSave(node)));
|
||||
const newNodeData = deepCopy(this.getNodeDataToSave(node));
|
||||
newNodeData.id = uuid();
|
||||
|
||||
// Check if node-name is unique else find one that is
|
||||
|
|
|
@ -16,3 +16,4 @@ export * from './WorkflowDataProxy';
|
|||
export * from './WorkflowErrors';
|
||||
export * from './WorkflowHooks';
|
||||
export { LoggerProxy, NodeHelpers, ObservableObject, TelemetryHelpers };
|
||||
export { deepCopy } from './utils';
|
||||
|
|
34
packages/workflow/src/utils.test.ts
Normal file
34
packages/workflow/src/utils.test.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { deepCopy } from './utils';
|
||||
|
||||
describe('deepCopy', () => {
|
||||
it('should deep copy an object', () => {
|
||||
const object = {
|
||||
deep: {
|
||||
props: {
|
||||
list: [{ a: 1 }, { b: 2 }, { c: 3 }],
|
||||
},
|
||||
arr: [1, 2, 3],
|
||||
},
|
||||
arr: [
|
||||
{
|
||||
prop: {
|
||||
list: ['a', 'b', 'c'],
|
||||
},
|
||||
},
|
||||
],
|
||||
func: () => {},
|
||||
date: new Date(),
|
||||
undef: undefined,
|
||||
nil: null,
|
||||
bool: true,
|
||||
num: 1,
|
||||
};
|
||||
const copy = deepCopy(object);
|
||||
expect(copy).toEqual(object);
|
||||
expect(copy).not.toBe(object);
|
||||
expect(copy.arr).toEqual(object.arr);
|
||||
expect(copy.arr).not.toBe(object.arr);
|
||||
expect(copy.deep.props).toEqual(object.deep.props);
|
||||
expect(copy.deep.props).not.toBe(object.deep.props);
|
||||
});
|
||||
});
|
|
@ -1 +1,32 @@
|
|||
export const deepCopy = <T>(toCopy: T) => JSON.parse(JSON.stringify(toCopy)) as T;
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */
|
||||
export const deepCopy = <T>(source: T): T => {
|
||||
let clone: any;
|
||||
let i: any;
|
||||
const hasOwnProp = Object.prototype.hasOwnProperty.bind(source);
|
||||
// Primitives & Null
|
||||
if (typeof source !== 'object' || source === null) {
|
||||
return source;
|
||||
}
|
||||
// Date
|
||||
if (source instanceof Date) {
|
||||
return new Date(source.getTime()) as T;
|
||||
}
|
||||
// Array
|
||||
if (Array.isArray(source)) {
|
||||
clone = [];
|
||||
const len = source.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
clone[i] = deepCopy(source[i]);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
// Object
|
||||
clone = {};
|
||||
for (i in source) {
|
||||
if (hasOwnProp(i)) {
|
||||
clone[i] = deepCopy((source as any)[i]);
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
};
|
||||
// eslint-enable
|
||||
|
|
Loading…
Reference in a new issue