Skip to content

Commit

Permalink
[8.x] [Automatic import] refactor merge (#193270) (#193291)
Browse files Browse the repository at this point in the history
# Backport

This will backport the following commits from `main` to `8.x`:
- [[Automatic import] refactor merge
(#193270)](#193270)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Hanna
Tamoudi","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-09-18T13:05:13Z","message":"[Automatic
import] refactor merge
(#193270)","sha":"af01e511734aba4687bee6ca9122ac1186e31ee1","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["enhancement","release_note:skip","v9.0.0","backport:prev-minor","Team:Security-Scalability","Feature:AutomaticImport"],"title":"[Automatic
import] refactor
merge","number":193270,"url":"https://github.com/elastic/kibana/pull/193270","mergeCommit":{"message":"[Automatic
import] refactor merge
(#193270)","sha":"af01e511734aba4687bee6ca9122ac1186e31ee1"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/193270","number":193270,"mergeCommit":{"message":"[Automatic
import] refactor merge
(#193270)","sha":"af01e511734aba4687bee6ca9122ac1186e31ee1"}}]}]
BACKPORT-->

Co-authored-by: Hanna Tamoudi <[email protected]>
  • Loading branch information
kibanamachine and haetamoudi authored Sep 18, 2024
1 parent e3ae68f commit 5fec6cb
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 25 deletions.
116 changes: 116 additions & 0 deletions x-pack/plugins/integration_assistant/server/util/samples.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
72 changes: 47 additions & 25 deletions x-pack/plugins/integration_assistant/server/util/samples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, any>,
source: Record<string, any>
): Record<string, unknown> {
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<string, any>): boolean {
return key === 'constructor' || !Object.prototype.hasOwnProperty.call(obj, key);
}

function filterOwnProperties(obj: Record<string, any>): Record<string, any> {
const ownProps: Record<string, any> = {};

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 {
Expand Down

0 comments on commit 5fec6cb

Please sign in to comment.