From 79f8660eef7f213c9923dec68e88a445241adebe Mon Sep 17 00:00:00 2001 From: Hanna Tamoudi Date: Wed, 18 Sep 2024 15:05:13 +0200 Subject: [PATCH] [Automatic import] refactor merge (#193270) (cherry picked from commit af01e511734aba4687bee6ca9122ac1186e31ee1) --- .../server/util/samples.test.ts | 116 ++++++++++++++++++ .../server/util/samples.ts | 72 +++++++---- 2 files changed, 163 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/integration_assistant/server/util/samples.test.ts diff --git a/x-pack/plugins/integration_assistant/server/util/samples.test.ts b/x-pack/plugins/integration_assistant/server/util/samples.test.ts new file mode 100644 index 0000000000000..131135e842334 --- /dev/null +++ b/x-pack/plugins/integration_assistant/server/util/samples.test.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { merge } from './samples'; + +describe('merge', () => { + it('Should return source if target is empty', async () => { + const target = {}; + const source = { target: 'user.name', confidence: 0.9, type: 'string', date_formats: [] }; + + const result = merge(target, source); + + expect(result).toEqual(source); + }); + + it('Should return target if source is empty', async () => { + const target = { hostname: '0.0.0.0', 'teleport.internal/resource-id': '1234' }; + const source = {}; + + const result = merge(target, source); + + expect(result).toEqual(target); + }); + + it('Should return one result', async () => { + const target = { + aaa: { + ei: 0, + event: 'cert.create', + uid: '1234', + cluster_name: 'cluster.com', + identity: { user: 'teleport-admin' }, + server_labels: { hostname: 'some-hostname' }, + }, + }; + const source = { + aaa: { + ei: 0, + event: 'session.start', + uid: '4567', + cluster_name: 'cluster.com', + user: 'teleport-admin', + server_labels: { hostname: 'some-other-hostname', id: '1234' }, + }, + }; + + const result = merge(target, source); + + expect(result).toEqual({ + aaa: { + ei: 0, + event: 'cert.create', + uid: '1234', + cluster_name: 'cluster.com', + identity: { user: 'teleport-admin' }, + server_labels: { hostname: 'some-hostname', id: '1234' }, + user: 'teleport-admin', + }, + }); + }); + + it('Should not merge built-in properties of neither target nor source', async () => { + const target = { + __proto__: 'some properties', + constructor: 'some other properties', + hostname: '0.0.0.0', + 'teleport.internal/resource-id': '1234', + }; + const source = { + __proto__: 'some properties of source', + constructor: 'some other properties of source', + }; + + const result = merge(target, source); + + expect(result).toEqual({ hostname: '0.0.0.0', 'teleport.internal/resource-id': '1234' }); + }); + + it('Should keep source object if it collides with target key that is not an object', async () => { + const target = { + hostname: '', + 'teleport.internal/resource-id': '1234', + date_formats: 'format', + }; + const source = { + target: 'user.name', + confidence: 0.9, + type: 'string', + date_formats: { key: 'value' }, + }; + + const result = merge(target, source); + + expect(result).toEqual({ + 'teleport.internal/resource-id': '1234', + target: 'user.name', + confidence: 0.9, + type: 'string', + hostname: '', + date_formats: { key: 'value' }, + }); + }); + + it('Should copy array into the result', async () => { + const target = { date_formats: ['a', 'b'] }; + const source = { target: 'user.name', confidence: 0.9, type: 'string', date_formats: ['c'] }; + + const result = merge(target, source); + + expect(result).toEqual(source); + }); +}); diff --git a/x-pack/plugins/integration_assistant/server/util/samples.ts b/x-pack/plugins/integration_assistant/server/util/samples.ts index 766856f644a86..745c3d214095f 100644 --- a/x-pack/plugins/integration_assistant/server/util/samples.ts +++ b/x-pack/plugins/integration_assistant/server/util/samples.ts @@ -163,39 +163,61 @@ export function generateFields(mergedDocs: string): string { return yaml.safeDump(fieldsStructure, { sortKeys: false }); } -function isEmptyValue(value: unknown): boolean { - return ( - value === null || - value === undefined || - (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) || - (Array.isArray(value) && value.length === 0) - ); -} - export function merge( target: Record, source: Record ): Record { + const filteredTarget = filterOwnProperties(target); for (const [key, sourceValue] of Object.entries(source)) { - const targetValue = target[key]; - if (Array.isArray(sourceValue)) { - // Directly assign arrays - target[key] = sourceValue; - } else if ( - typeof sourceValue === 'object' && - sourceValue !== null && - !Array.isArray(targetValue) - ) { - if (typeof targetValue !== 'object' || isEmptyValue(targetValue)) { - target[key] = merge({}, sourceValue); - } else { - target[key] = merge(targetValue, sourceValue); + if (!isBuiltInProperties(key, source)) { + const targetValue = filteredTarget[key]; + if (Array.isArray(sourceValue)) { + // Directly assign arrays + filteredTarget[key] = sourceValue; + } else if (isObject(sourceValue) && !Array.isArray(targetValue)) { + if (!isObject(targetValue) || isEmptyValue(targetValue)) { + filteredTarget[key] = merge({}, sourceValue); + } else { + filteredTarget[key] = merge(targetValue, sourceValue); + } + } else if ( + !(key in filteredTarget) || + (isEmptyValue(targetValue) && !isEmptyValue(sourceValue)) + ) { + filteredTarget[key] = sourceValue; } - } else if (!(key in target) || (isEmptyValue(targetValue) && !isEmptyValue(sourceValue))) { - target[key] = sourceValue; } } - return target; + return filteredTarget; +} + +function isEmptyValue(value: unknown): boolean { + if (value == null) return true; + if (isObject(value)) { + if (Array.isArray(value)) return value.length === 0; + return value && Object.keys(value).length === 0; + } + return false; +} + +function isObject(value: any): boolean { + return typeof value === 'object' && value !== null; +} + +function isBuiltInProperties(key: string, obj: Record): boolean { + return key === 'constructor' || !Object.prototype.hasOwnProperty.call(obj, key); +} + +function filterOwnProperties(obj: Record): Record { + const ownProps: Record = {}; + + for (const key of Object.getOwnPropertyNames(obj)) { + if (!isBuiltInProperties(key, obj)) { + ownProps[key] = (obj as any)[key]; + } + } + + return ownProps; } export function mergeSamples(objects: any[]): string {