mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Drop mergeDeep in favor of lodash merge (#5943)
This commit is contained in:
parent
6cf74e412a
commit
0570514b78
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
import { merge } from 'lodash-es';
|
||||||
import { INodeUi, Schema } from '@/Interface';
|
import { INodeUi, Schema } from '@/Interface';
|
||||||
import RunDataSchemaItem from '@/components/RunDataSchemaItem.vue';
|
import RunDataSchemaItem from '@/components/RunDataSchemaItem.vue';
|
||||||
import Draggable from '@/components/Draggable.vue';
|
import Draggable from '@/components/Draggable.vue';
|
||||||
|
@ -8,7 +9,7 @@ import { useWebhooksStore } from '@/stores/webhooks';
|
||||||
import { runExternalHook } from '@/mixins/externalHooks';
|
import { runExternalHook } from '@/mixins/externalHooks';
|
||||||
import { telemetry } from '@/plugins/telemetry';
|
import { telemetry } from '@/plugins/telemetry';
|
||||||
import { IDataObject } from 'n8n-workflow';
|
import { IDataObject } from 'n8n-workflow';
|
||||||
import { getSchema, isEmpty, mergeDeep } from '@/utils';
|
import { getSchema, isEmpty } from '@/utils';
|
||||||
import { i18n } from '@/plugins/i18n';
|
import { i18n } from '@/plugins/i18n';
|
||||||
import MappingPill from './MappingPill.vue';
|
import MappingPill from './MappingPill.vue';
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ const webhooksStore = useWebhooksStore();
|
||||||
|
|
||||||
const schema = computed<Schema>(() => {
|
const schema = computed<Schema>(() => {
|
||||||
const [head, ...tail] = props.data;
|
const [head, ...tail] = props.data;
|
||||||
return getSchema(mergeDeep([head, ...tail, head]));
|
return getSchema(merge({}, head, ...tail, head));
|
||||||
});
|
});
|
||||||
|
|
||||||
const isDataEmpty = computed(() => {
|
const isDataEmpty = computed(() => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import jp from 'jsonpath';
|
import jp from 'jsonpath';
|
||||||
import { isEmpty, intersection, mergeDeep, getSchema, isValidDate } from '@/utils';
|
import { isEmpty, intersection, getSchema, isValidDate } from '@/utils';
|
||||||
import { Schema } from '@/Interface';
|
import { Schema } from '@/Interface';
|
||||||
|
|
||||||
describe('Types Utils', () => {
|
describe('Types Utils', () => {
|
||||||
|
@ -43,180 +43,6 @@ describe('Types Utils', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('mergeDeep', () => {
|
|
||||||
test.each([
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4],
|
|
||||||
],
|
|
||||||
{},
|
|
||||||
[3, 4],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4],
|
|
||||||
],
|
|
||||||
{ concatArrays: true },
|
|
||||||
[1, 2, 3, 4],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4],
|
|
||||||
],
|
|
||||||
{ overwriteArrays: true },
|
|
||||||
[3, 4],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2, 3],
|
|
||||||
[4, 5],
|
|
||||||
],
|
|
||||||
{},
|
|
||||||
[4, 5, 3],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2, 3],
|
|
||||||
[4, 5],
|
|
||||||
],
|
|
||||||
{ concatArrays: true },
|
|
||||||
[1, 2, 3, 4, 5],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2, 3],
|
|
||||||
[4, 5],
|
|
||||||
],
|
|
||||||
{ overwriteArrays: true },
|
|
||||||
[4, 5],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4, 5],
|
|
||||||
],
|
|
||||||
{},
|
|
||||||
[3, 4, 5],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4, 5],
|
|
||||||
],
|
|
||||||
{ concatArrays: true },
|
|
||||||
[1, 2, 3, 4, 5],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
[1, 2],
|
|
||||||
[3, 4, 5],
|
|
||||||
],
|
|
||||||
{ overwriteArrays: true },
|
|
||||||
[3, 4, 5],
|
|
||||||
],
|
|
||||||
[[{ a: 1, b: [1, 2, { d: 2 }] }, {}], {}, { a: 1, b: [1, 2, { d: 2 }] }],
|
|
||||||
[[{ a: 1, b: [1, 2, { d: 2 }] }, {}], { concatArrays: true }, { a: 1, b: [1, 2, { d: 2 }] }],
|
|
||||||
[
|
|
||||||
[{ a: 1, b: [1, 2, { d: 2 }] }, {}],
|
|
||||||
{ overwriteArrays: true },
|
|
||||||
{ a: 1, b: [1, 2, { d: 2 }] },
|
|
||||||
],
|
|
||||||
[[[{ a: 1, b: [1, 2, { d: 2 }] }], []], {}, [{ a: 1, b: [1, 2, { d: 2 }] }]],
|
|
||||||
[
|
|
||||||
[[{ a: 1, b: [1, 2, { d: 2 }] }], []],
|
|
||||||
{ concatArrays: true },
|
|
||||||
[{ a: 1, b: [1, 2, { d: 2 }] }],
|
|
||||||
],
|
|
||||||
[[[{ a: 1, b: [1, 2, { d: 2 }] }], []], { overwriteArrays: true }, []],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
{ a: 1, b: [1, 2, 3] },
|
|
||||||
{ a: 2, b: [4, 5, 6, 7], c: '2' },
|
|
||||||
{ a: 3, b: [8, 9], d: '3' },
|
|
||||||
],
|
|
||||||
{},
|
|
||||||
{ a: 3, b: [8, 9, 6, 7], c: '2', d: '3' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
{ a: 1, b: [1, 2, 3] },
|
|
||||||
{ a: 2, b: [4, 5, 6, 7], c: '2' },
|
|
||||||
{ a: 3, b: [8, 9], d: '3' },
|
|
||||||
],
|
|
||||||
{ concatArrays: true },
|
|
||||||
{ a: 3, b: [1, 2, 3, 4, 5, 6, 7, 8, 9], c: '2', d: '3' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
{ a: 1, b: [1, 2, 3] },
|
|
||||||
{ a: 2, b: [4, 5, 6, 7], c: '2' },
|
|
||||||
{ a: 3, b: [8, 9], d: '3' },
|
|
||||||
],
|
|
||||||
{ overwriteArrays: true },
|
|
||||||
{ a: 3, b: [8, 9], c: '2', d: '3' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
{ a: 1, b: [{ x: 'a' }] },
|
|
||||||
{ a: 2, b: [{ y: 'b' }], c: '2' },
|
|
||||||
{ a: 3, b: [{ z: 'c' }], d: '3' },
|
|
||||||
],
|
|
||||||
{},
|
|
||||||
{ a: 3, b: [{ x: 'a', y: 'b', z: 'c' }], c: '2', d: '3' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
{ a: 1, b: [{ x: 'a' }] },
|
|
||||||
{ a: 2, b: [{ y: 'b' }], c: '2' },
|
|
||||||
{ a: 3, b: [{ z: 'c' }], d: '3' },
|
|
||||||
],
|
|
||||||
{ concatArrays: true },
|
|
||||||
{ a: 3, b: [{ x: 'a' }, { y: 'b' }, { z: 'c' }], c: '2', d: '3' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
{ a: 1, b: [{ x: 'a' }] },
|
|
||||||
{ a: 2, b: [{ y: 'b' }], c: '2' },
|
|
||||||
{ a: 3, b: [{ z: 'c' }], d: '3' },
|
|
||||||
],
|
|
||||||
{ overwriteArrays: true },
|
|
||||||
{ a: 3, b: [{ z: 'c' }], c: '2', d: '3' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
{ a: 1, b: [{ x: 'a' }, { w: 'd' }] },
|
|
||||||
{ a: 2, b: [{ y: 'b' }], c: '2' },
|
|
||||||
{ a: 3, b: [{ z: 'c' }], d: '3' },
|
|
||||||
],
|
|
||||||
{},
|
|
||||||
{ a: 3, b: [{ z: 'c' }, { w: 'd' }], c: '2', d: '3' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
{ a: 1, b: [{ x: 'a' }, { w: 'd' }] },
|
|
||||||
{ a: 2, b: [{ y: 'b' }], c: '2' },
|
|
||||||
{ a: 3, b: [{ z: 'c' }], d: '3' },
|
|
||||||
],
|
|
||||||
{ concatArrays: true },
|
|
||||||
{ a: 3, b: [{ x: 'a' }, { w: 'd' }, { y: 'b' }, { z: 'c' }], c: '2', d: '3' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
{ a: 1, b: [{ x: 'a' }, { w: 'd' }] },
|
|
||||||
{ a: 2, b: [{ y: 'b' }], c: '2' },
|
|
||||||
{ a: 3, b: [{ z: 'c' }], d: '3' },
|
|
||||||
],
|
|
||||||
{ overwriteArrays: true },
|
|
||||||
{ a: 3, b: [{ z: 'c' }], c: '2', d: '3' },
|
|
||||||
],
|
|
||||||
])('case %#. input %j, options %j should return %j', (sources, options, expected) => {
|
|
||||||
expect(mergeDeep([...sources], options)).toEqual(expected);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getSchema', () => {
|
describe('getSchema', () => {
|
||||||
test.each([
|
test.each([
|
||||||
[, { type: 'undefined', value: 'undefined', path: '' }],
|
[, { type: 'undefined', value: 'undefined', path: '' }],
|
||||||
|
|
|
@ -164,54 +164,6 @@ export const isValidDate = (input: string | number | Date): boolean => {
|
||||||
export const getObjectKeys = <T extends object, K extends keyof T>(o: T): K[] =>
|
export const getObjectKeys = <T extends object, K extends keyof T>(o: T): K[] =>
|
||||||
Object.keys(o) as K[];
|
Object.keys(o) as K[];
|
||||||
|
|
||||||
export const mergeDeep = <T extends object | Primitives>(
|
|
||||||
sources: T[],
|
|
||||||
options?: Partial<Record<'overwriteArrays' | 'concatArrays', boolean>>,
|
|
||||||
): T =>
|
|
||||||
sources.reduce((target, source) => {
|
|
||||||
if (Array.isArray(target) && Array.isArray(source)) {
|
|
||||||
const tLength = target.length;
|
|
||||||
const sLength = source.length;
|
|
||||||
|
|
||||||
if (tLength === 0 || options?.overwriteArrays) {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sLength === 0) {
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options?.concatArrays) {
|
|
||||||
return [...target, ...source];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tLength === sLength) {
|
|
||||||
return target.map((item, index) => mergeDeep([item, source[index]], options));
|
|
||||||
} else if (tLength < sLength) {
|
|
||||||
return source.map((item, index) => mergeDeep([target[index], item], options));
|
|
||||||
} else {
|
|
||||||
return [...source, ...target.slice(sLength)];
|
|
||||||
}
|
|
||||||
} else if (isObj(target) && isObj(source)) {
|
|
||||||
const targetKeys = getObjectKeys(target);
|
|
||||||
const sourceKeys = getObjectKeys(source);
|
|
||||||
const allKeys = [...new Set([...targetKeys, ...sourceKeys])];
|
|
||||||
const mergedObject = Object.create(Object.prototype);
|
|
||||||
for (const key of allKeys) {
|
|
||||||
if (targetKeys.includes(key) && sourceKeys.includes(key)) {
|
|
||||||
mergedObject[key] = mergeDeep([target[key] as T, source[key] as T], options);
|
|
||||||
} else if (targetKeys.includes(key)) {
|
|
||||||
mergedObject[key] = target[key];
|
|
||||||
} else {
|
|
||||||
mergedObject[key] = source[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mergedObject;
|
|
||||||
} else {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
}, (Array.isArray(sources[0]) ? [] : {}) as T);
|
|
||||||
|
|
||||||
export const getSchema = (input: Optional<Primitives | object>, path = ''): Schema => {
|
export const getSchema = (input: Optional<Primitives | object>, path = ''): Schema => {
|
||||||
let schema: Schema = { type: 'undefined', value: 'undefined', path };
|
let schema: Schema = { type: 'undefined', value: 'undefined', path };
|
||||||
switch (typeof input) {
|
switch (typeof input) {
|
||||||
|
|
Loading…
Reference in a new issue