[8.x] Deprecate universal entity (#211771)

# Backport

This will backport the following commits from `main` to `8.x`:
- [Deprecate universal
entity](https://github.com/elastic/kibana/pull/210978)

<!--- Backport version: 9.6.6 -->

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

<!--BACKPORT [{"author":{"name":"Ido
Cohen","email":"90558359+CohenIdo@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-02-18T14:49:32Z","message":"Deprecate
universal
entity","sha":"f5c9529e37ea3dddca4db748bfa7e2391303f87e","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Cloud
Security","backport:version","v8.18.0","v9.1.0","v8.19.0"],"title":"Deprecate
universal
entity","number":210978,"url":"https://github.com/elastic/kibana/pull/210978","mergeCommit":{"message":"Deprecate
universal
entity","sha":"f5c9529e37ea3dddca4db748bfa7e2391303f87e"}},"sourceBranch":"main","suggestedTargetBranches":["9.0","8.18","8.x"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.18","label":"v8.18.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/210978","number":210978,"mergeCommit":{"message":"Deprecate
universal
entity","sha":"f5c9529e37ea3dddca4db748bfa7e2391303f87e"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Ido Cohen 2025-02-20 15:28:29 +02:00 committed by GitHub
parent 1a0565234b
commit 834e54f158
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 45 additions and 355 deletions

View file

@ -104,4 +104,4 @@ enabled:
- x-pack/test/cloud_security_posture_functional/config.agentless.ts
- x-pack/test/cloud_security_posture_functional/data_views/config.ts
- x-pack/test/automatic_import_api_integration/apis/config_basic.ts
- x-pack/test/automatic_import_api_integration/apis/config_graphs.ts
- x-pack/test/automatic_import_api_integration/apis/config_graphs.ts

37
.github/CODEOWNERS vendored
View file

@ -1961,6 +1961,7 @@ x-pack/plugins/osquery @elastic/security-defend-workflows
# Cloud Security Posture
x-pack/packages/kbn-cloud-security-posture @elastic/kibana-cloud-security-posture
<<<<<<< HEAD
/x-pack/test_serverless/functional/test_suites/security/config.cloud_security_posture.* @elastic/kibana-cloud-security-posture
/x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture @elastic/kibana-cloud-security-posture
/x-pack/test/api_integration/apis/cloud_security_posture/ @elastic/kibana-cloud-security-posture
@ -1977,6 +1978,42 @@ x-pack/packages/kbn-cloud-security-posture @elastic/kibana-cloud-security-postur
/x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture @elastic/kibana-cloud-security-posture
/x-pack/test/security_solution_cypress/cypress/e2e/cloud_security_posture/misconfiguration_contextual_flyout.cy.ts @elastic/kibana-cloud-security-posture
/x-pack/test/security_solution_cypress/cypress/e2e/cloud_security_posture/vulnerabilities_contextual_flyout.cy.ts @elastic/kibana-cloud-security-posture
=======
## Plugins
x-pack/plugins/cloud_defend @elastic/kibana-cloud-security-posture
x-pack/plugins/cloud_security_posture @elastic/kibana-cloud-security-posture
x-pack/plugins/kubernetes_security @elastic/kibana-cloud-security-posture
## Security Solution sub teams
x-pack/solutions/security/plugins/security_solution/public/common/components/sessions_viewer @elastic/kibana-cloud-security-posture
x-pack/solutions/security/plugins/security_solution/public/cloud_defend @elastic/kibana-cloud-security-posture
x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture @elastic/kibana-cloud-security-posture
x-pack/solutions/security/plugins/security_solution/public/kubernetes @elastic/kibana-cloud-security-posture
x-pack/solutions/security/plugins/security_solution/server/lib/asset_inventory @elastic/kibana-cloud-security-posture
## Fleet plugin (co-owned with Fleet team)
x-pack/platform/plugins/shared/fleet/public/components/cloud_security_posture @elastic/fleet @elastic/kibana-cloud-security-posture
x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/cloud_security_posture @elastic/fleet @elastic/kibana-cloud-security-posture
x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.* @elastic/fleet @elastic/kibana-cloud-security-posture
x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.* @elastic/fleet @elastic/kibana-cloud-security-posture
## Kubernetes Security tests
x-pack/test/functional/es_archives/kubernetes_security @elastic/kibana-cloud-security-posture
x-pack/test/kubernetes_security @elastic/kibana-cloud-security-posture
## SessionView tests
x-pack/test/functional/es_archives/session_view @elastic/kibana-cloud-security-posture
x-pack/test/session_view @elastic/kibana-cloud-security-posture # Assigned per https://github.com/elastic/kibana/blob/main/api_docs/session_view.mdx#L18
## CSP tests
x-pack/test/api_integration/apis/cloud_security_posture/ @elastic/kibana-cloud-security-posture
x-pack/test/cloud_security_posture_functional/ @elastic/kibana-cloud-security-posture
x-pack/test/cloud_security_posture_api/ @elastic/kibana-cloud-security-posture
## CSP Serverless tests
x-pack/test_serverless/functional/test_suites/security/config.cloud_security_posture.* @elastic/kibana-cloud-security-posture
x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/ @elastic/kibana-cloud-security-posture
x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/ @elastic/kibana-cloud-security-posture
## CSP e2e tests
x-pack/test/security_solution_cypress/cypress/e2e/cloud_security_posture/misconfiguration_contextual_flyout.cy.ts @elastic/kibana-cloud-security-posture
x-pack/test/security_solution_cypress/cypress/e2e/cloud_security_posture/vulnerabilities_contextual_flyout.cy.ts @elastic/kibana-cloud-security-posture
x-pack/test/security_solution_cypress/cypress/e2e/asset_inventory @elastic/kibana-cloud-security-posture
>>>>>>> f5c9529e37e (Deprecate universal entity)
# Security Solution onboarding tour
/x-pack/solutions/security/plugins/security_solution/public/common/components/guided_onboarding @elastic/security-threat-hunting-explore

View file

@ -47985,7 +47985,6 @@ components:
- user
- host
- service
- universal
type: string
Security_Entity_Analytics_API_HostEntity:
type: object
@ -48061,7 +48060,6 @@ components:
- host.name
- user.name
- service.name
- related.entity
type: string
Security_Entity_Analytics_API_IndexPattern:
type: string

View file

@ -36375,7 +36375,6 @@ components:
- user
- host
- service
- universal
type: string
Security_Entity_Analytics_API_HostEntity:
type: object
@ -36451,7 +36450,6 @@ components:
- host.name
- user.name
- service.name
- related.entity
type: string
Security_Entity_Analytics_API_IndexPattern:
type: string

View file

@ -17,7 +17,7 @@
import { z } from '@kbn/zod';
export type IdField = z.infer<typeof IdField>;
export const IdField = z.enum(['host.name', 'user.name', 'service.name', 'related.entity']);
export const IdField = z.enum(['host.name', 'user.name', 'service.name']);
export type IdFieldEnum = typeof IdField.enum;
export const IdFieldEnum = IdField.enum;

View file

@ -29,7 +29,6 @@ components:
- 'host.name'
- 'user.name'
- 'service.name'
- 'related.entity'
AssetCriticalityRecordIdParts:
type: object
properties:

View file

@ -17,7 +17,7 @@
import { z } from '@kbn/zod';
export type EntityType = z.infer<typeof EntityType>;
export const EntityType = z.enum(['user', 'host', 'service', 'universal']);
export const EntityType = z.enum(['user', 'host', 'service']);
export type EntityTypeEnum = typeof EntityType.enum;
export const EntityTypeEnum = EntityType.enum;

View file

@ -12,7 +12,6 @@ components:
- user
- host
- service
- universal
EngineDescriptor:
type: object

View file

@ -7,17 +7,11 @@
import type { ExperimentalFeatures } from '../../experimental_features';
import { getAllEntityTypes, getDisabledEntityTypes } from '../utils';
import { EntityType } from '../types';
const ASSET_CRITICALITY_UNAVAILABLE_TYPES = [EntityType.universal];
// TODO delete this function when the universal entity support is added
export const getAssetCriticalityEntityTypes = (experimentalFeatures: ExperimentalFeatures) => {
const allEntityTypes = getAllEntityTypes();
const disabledEntityTypes = getDisabledEntityTypes(experimentalFeatures);
return allEntityTypes.filter(
(value) =>
!disabledEntityTypes.includes(value) && !ASSET_CRITICALITY_UNAVAILABLE_TYPES.includes(value)
);
return allEntityTypes.filter((value) => !disabledEntityTypes.includes(value));
};

View file

@ -6,18 +6,12 @@
*/
import type { ExperimentalFeatures } from '../../experimental_features';
import { EntityType } from '../types';
import { getAllEntityTypes, getDisabledEntityTypes } from '../utils';
const ENTITY_STORE_UNAVAILABLE_TYPES = [EntityType.universal];
// TODO delete this function when the universal entity support is added
export const getEnabledStoreEntityTypes = (experimentalFeatures: ExperimentalFeatures) => {
const allEntityTypes = getAllEntityTypes();
const disabledEntityTypes = getDisabledEntityTypes(experimentalFeatures);
return allEntityTypes.filter(
(value) =>
!disabledEntityTypes.includes(value) && !ENTITY_STORE_UNAVAILABLE_TYPES.includes(value)
);
return allEntityTypes.filter((value) => !disabledEntityTypes.includes(value));
};

View file

@ -6,7 +6,6 @@
*/
import * as t from 'io-ts';
import { EntityType } from '../types';
import { getAllEntityTypes, getDisabledEntityTypes } from '../utils';
import type { ExperimentalFeatures } from '../../experimental_features';
@ -28,15 +27,10 @@ export function fromEnum<EnumType extends string>(
);
}
const RISK_ENGINE_UNAVAILABLE_TYPES = [EntityType.universal];
// TODO delete this function when the universal entity support is added
export const getRiskEngineEntityTypes = (experimentalFeatures: ExperimentalFeatures) => {
const allEntityTypes = getAllEntityTypes();
const disabledEntityTypes = getDisabledEntityTypes(experimentalFeatures);
return allEntityTypes.filter(
(value) =>
!disabledEntityTypes.includes(value) && !RISK_ENGINE_UNAVAILABLE_TYPES.includes(value)
);
return allEntityTypes.filter((value) => !disabledEntityTypes.includes(value));
};

View file

@ -15,19 +15,16 @@ export enum EntityType {
user = 'user',
host = 'host',
service = 'service',
universal = 'universal',
}
export enum EntityIdentifierFields {
hostName = 'host.name',
userName = 'user.name',
serviceName = 'service.name',
universal = 'related.entity',
}
export const EntityTypeToIdentifierField: Record<EntityType, EntityIdentifierFields> = {
[EntityType.host]: EntityIdentifierFields.hostName,
[EntityType.user]: EntityIdentifierFields.userName,
[EntityType.service]: EntityIdentifierFields.serviceName,
[EntityType.universal]: EntityIdentifierFields.universal,
};

View file

@ -4,13 +4,8 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { getAllEntityTypes, getDisabledEntityTypes } from './utils';
import { getAllEntityTypes } from './utils';
import { EntityType } from './types';
import type { ExperimentalFeatures } from '../experimental_features';
import { mockGlobalState } from '../../public/common/mock';
const mockedExperimentalFeatures = mockGlobalState.app.enableExperimental;
describe('utils', () => {
describe('getAllEntityTypes', () => {
@ -19,24 +14,4 @@ describe('utils', () => {
expect(entityTypes).toEqual(Object.values(EntityType));
});
});
describe('getDisabledEntityTypes', () => {
it('should return disabled entity types when assetInventoryStoreEnabled is false', () => {
const experimentalFeatures: ExperimentalFeatures = {
...mockedExperimentalFeatures,
assetInventoryStoreEnabled: false,
};
const disabledEntityTypes = getDisabledEntityTypes(experimentalFeatures);
expect(disabledEntityTypes).toEqual([EntityType.universal]);
});
it('should return no disabled entity types when both features are true', () => {
const experimentalFeatures: ExperimentalFeatures = {
...mockedExperimentalFeatures,
assetInventoryStoreEnabled: true,
};
const disabledEntityTypes = getDisabledEntityTypes(experimentalFeatures);
expect(disabledEntityTypes).toEqual([]);
});
});
});

View file

@ -13,11 +13,6 @@ export const getDisabledEntityTypes = (
experimentalFeatures: ExperimentalFeatures
): EntityType[] => {
const disabledEntityTypes: EntityType[] = [];
const isUniversalEntityStoreEnabled = experimentalFeatures.assetInventoryStoreEnabled;
if (!isUniversalEntityStoreEnabled) {
disabledEntityTypes.push(EntityType.universal);
}
return disabledEntityTypes;
};

View file

@ -262,12 +262,6 @@ export const allowedExperimentalValues = Object.freeze({
*/
crowdstrikeRunScriptEnabled: true,
/**
* Enables the Asset Inventory Entity Store feature.
* Allows initializing the Universal Entity Store via the API.
*/
assetInventoryStoreEnabled: false,
/**
* Enables the Asset Inventory feature
*/

View file

@ -93,12 +93,10 @@ export const EntityTypeToLevelField: Record<EntityType, RiskScoreFields> = {
[EntityType.host]: RiskScoreFields.hostRisk,
[EntityType.user]: RiskScoreFields.userRisk,
[EntityType.service]: RiskScoreFields.serviceRisk,
[EntityType.universal]: RiskScoreFields.unsupported, // We don't calculate risk for the universal entity
};
export const EntityTypeToScoreField: Record<EntityType, RiskScoreFields> = {
[EntityType.host]: RiskScoreFields.hostRiskScore,
[EntityType.user]: RiskScoreFields.userRiskScore,
[EntityType.service]: RiskScoreFields.serviceRiskScore,
[EntityType.universal]: RiskScoreFields.unsupported, // We don't calculate risk for the universal entity
};

View file

@ -1204,7 +1204,6 @@ components:
- user
- host
- service
- universal
type: string
HostEntity:
type: object
@ -1280,7 +1279,6 @@ components:
- host.name
- user.name
- service.name
- related.entity
type: string
IndexPattern:
type: string

View file

@ -1204,7 +1204,6 @@ components:
- user
- host
- service
- universal
type: string
HostEntity:
type: object
@ -1280,7 +1279,6 @@ components:
- host.name
- user.name
- service.name
- related.entity
type: string
IndexPattern:
type: string

View file

@ -35,7 +35,6 @@ export const EntityIconByType: Record<EntityType, IconType> = {
[EntityType.user]: 'user',
[EntityType.host]: 'storage',
[EntityType.service]: 'node',
[EntityType.universal]: 'globe', // random value since we don't support universal entity type
};
export const sourceFieldToText = (source: string) => {

View file

@ -32,7 +32,6 @@ export const EntityPanelKeyByType: Record<EntityType, string | undefined> = {
[EntityType.host]: HostPanelKey,
[EntityType.user]: UserPanelKey,
[EntityType.service]: ServicePanelKey,
[EntityType.universal]: undefined, // TODO create universal flyout?
};
// TODO rename all params and merged them as 'entityName'
@ -40,5 +39,4 @@ export const EntityPanelParamByType: Record<EntityType, string | undefined> = {
[EntityType.host]: 'hostName',
[EntityType.user]: 'userName',
[EntityType.service]: 'serviceName',
[EntityType.universal]: undefined, // TODO create universal flyout?
};

View file

@ -82,7 +82,6 @@ const entityTypeByIdField = {
'host.name': 'host',
'user.name': 'user',
'service.name': 'service',
'related.entity': 'universal',
} as const;
export const getImplicitEntityFields = (record: AssetCriticalityUpsertWithDeleted) => {

View file

@ -8,7 +8,6 @@
import type { EntityType } from '../../../../../common/api/entity_analytics/entity_store';
import {
HOST_DEFINITION_VERSION,
UNIVERSAL_DEFINITION_VERSION,
USER_DEFINITION_VERSION,
SERVICE_DEFINITION_VERSION,
} from './entity_descriptions';
@ -16,7 +15,6 @@ import {
export const VERSIONS_BY_ENTITY_TYPE: Record<EntityType, string> = {
host: HOST_DEFINITION_VERSION,
user: USER_DEFINITION_VERSION,
universal: UNIVERSAL_DEFINITION_VERSION,
service: SERVICE_DEFINITION_VERSION,
};

View file

@ -8,5 +8,4 @@
export * from './host';
export * from './user';
export * from './service';
export * from './universal';
export { getCommonFieldDescriptions } from './common';

View file

@ -1,96 +0,0 @@
/*
* 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 type { IngestProcessorContainer } from '@elastic/elasticsearch/lib/api/types';
import type { EntityDescription } from '../types';
import { collectValues } from './field_utils';
export const UNIVERSAL_DEFINITION_VERSION = '1.0.0';
export const UNIVERSAL_IDENTITY_FIELD = 'related.entity';
export const entityMetadataExtractorProcessor = {
script: {
tag: 'entity_metadata_extractor',
on_failure: [
{
set: {
field: 'error.message',
value:
'Processor {{ _ingest.on_failure_processor_type }} with tag {{ _ingest.on_failure_processor_tag }} in pipeline {{ _ingest.on_failure_pipeline }} failed with message {{ _ingest.on_failure_message }}',
},
},
],
lang: 'painless',
source: /* java */ `
// Array, boolean, integer, ip, bytes, anything that is not a map, is a leaf field
void overwriteLeafFields(Object toBeOverwritten, Object toOverwrite) {
if (!(toBeOverwritten instanceof Map)) {
// We can't override anything that isn't a map
return;
}
if (toOverwrite instanceof Map) {
Map mapToBeOverwritten = (Map) toBeOverwritten;
for (entryToOverwrite in ((Map) toOverwrite).entrySet()) {
String keyToOverwrite = entryToOverwrite.getKey();
Object valueToOverwrite = entryToOverwrite.getValue();
if (valueToOverwrite instanceof Map) {
// If no initial value, we just put everything we have to overwrite
if (mapToBeOverwritten.get(keyToOverwrite) == null) {
mapToBeOverwritten.put(keyToOverwrite, valueToOverwrite)
} else {
overwriteLeafFields(mapToBeOverwritten.get(keyToOverwrite), valueToOverwrite);
}
} else {
mapToBeOverwritten.put(keyToOverwrite, valueToOverwrite)
}
}
}
}
def id = ctx.entity.id;
Map merged = ctx;
for (meta in ctx.collected.metadata) {
Object json = Processors.json(meta);
if (((Map)json)[id] == null) {
continue;
}
if (((Map)json)[id] != null) {
overwriteLeafFields(merged, ((Map)json)[id]);
}
}
merged.entity.id = id;
ctx = merged;
`,
},
};
export const universalEntityEngineDescription: EntityDescription = {
version: UNIVERSAL_DEFINITION_VERSION,
entityType: 'universal',
identityField: UNIVERSAL_IDENTITY_FIELD,
fields: [collectValues({ source: 'entities.keyword', destination: 'collected.metadata' })],
settings: {
timestampField: 'event.ingested',
},
pipeline: (processors: IngestProcessorContainer[]) => {
const index = processors.findIndex((p) => Boolean(p.enrich));
if (index === -1) {
throw new Error('Enrich processor not found');
}
const init = processors.slice(0, index);
const tail = processors.slice(index);
const pipe = [...init, entityMetadataExtractorProcessor, ...tail];
return pipe;
},
dynamic: true,
};

View file

@ -363,14 +363,5 @@ describe('EntityStoreDataClient', () => {
expect(spyInit).toHaveBeenCalledWith(EntityType.host, expect.anything(), expect.anything());
});
it('does not enable engine when the given entity type is disabled', async () => {
await dataClient.enable({
...defaultOptions,
entityTypes: [EntityType.universal],
});
expect(spyInit).not.toHaveBeenCalled();
});
});
});

View file

@ -306,12 +306,6 @@ export class EntityStoreDataClient {
requestBody: InitEntityEngineRequestBody,
{ pipelineDebugMode = false }: { pipelineDebugMode?: boolean } = {}
): Promise<InitEntityEngineResponse> {
const { experimentalFeatures } = this.options;
if (entityType === EntityType.universal && !experimentalFeatures.assetInventoryStoreEnabled) {
throw new Error('Universal entity store is not enabled');
}
if (!this.options.taskManager) {
throw new Error('Task Manager is not available');
}

View file

@ -14,7 +14,6 @@ import { generateIndexMappings } from '../elasticsearch_assets';
import {
hostEntityEngineDescription,
userEntityEngineDescription,
universalEntityEngineDescription,
serviceEntityEngineDescription,
} from '../entity_definitions/entity_descriptions';
import type { EntityStoreConfig } from '../types';
@ -27,7 +26,6 @@ import { defaultOptions } from '../constants';
const engineDescriptionRegistry: Record<EntityType, EntityDescription> = {
host: hostEntityEngineDescription,
user: userEntityEngineDescription,
universal: universalEntityEngineDescription,
service: serviceEntityEngineDescription,
};

View file

@ -1,157 +0,0 @@
/*
* 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 expect from '@kbn/expect';
import { entityMetadataExtractorProcessor } from '@kbn/security-solution-plugin/server/lib/entity_analytics/entity_store/entity_definitions/entity_descriptions/universal';
import { dynamicNewestRetentionSteps } from '@kbn/security-solution-plugin/server/lib/entity_analytics/entity_store/field_retention/dynamic_retention';
import { FtrProviderContext } from '../../../../ftr_provider_context';
import { applyIngestProcessorToDoc } from '../utils/ingest';
export default ({ getService }: FtrProviderContext) => {
const es = getService('es');
const log = getService('log');
describe('@ess @serverless @skipInServerlessMKI Asset Inventory - universal entity engine pipeline ', () => {
describe('Entity metadata extractor processor step', () => {
it('should extract metadata from "collected.metadata" and add it to the document', async () => {
const metadata = {
test: {
cloud: { super: 123 },
okta: { foo: { baz: { qux: 1 } } },
},
};
const doc = {
collected: {
metadata: [JSON.stringify(metadata)],
},
entity: {
id: 'test',
},
};
const result = await applyIngestProcessorToDoc(
[entityMetadataExtractorProcessor],
doc,
es,
log
);
const processed = {
...doc,
...metadata.test,
};
return expect(result).to.eql(processed);
});
});
describe('prefer newest value for dynamic entities', () => {
it('should return latest value if no history value', async () => {
const metadata = {
cloud: { super: 123 },
};
const doc = metadata;
const processor = dynamicNewestRetentionSteps([]);
const result = await applyIngestProcessorToDoc([processor], doc, es, log);
return expect(result).to.eql(doc);
});
it('should return history value if no latest value is found', async () => {
const metadata = {
cloud: { super: 123 },
};
const doc = {
historical: metadata,
};
const processor = dynamicNewestRetentionSteps([]);
const result = await applyIngestProcessorToDoc([processor], doc, es, log);
return expect(result).to.eql({
...doc,
...metadata,
});
});
it('should return latest value if both historical and latest values exist', async () => {
const metadata = {
cloud: { super: 123 },
};
const historical = {
cloud: { super: 456 },
};
const doc = {
historical,
...metadata,
};
const processor = dynamicNewestRetentionSteps([]);
const result = await applyIngestProcessorToDoc([processor], doc, es, log);
return expect(result).to.eql(doc);
});
it('should merge nested object preserving historical values not found in latest', async () => {
const metadata = {
cloud: { host: 'test' },
okta: { foo: { bar: { baz: 1 } } },
};
const historical = {
cloud: { user: 'agent' },
okta: { foo: { bar: { qux: 11 } } },
};
const doc = {
historical,
...metadata,
};
const processor = dynamicNewestRetentionSteps([]);
const result = await applyIngestProcessorToDoc([processor], doc, es, log);
return expect(result).to.eql({
historical,
cloud: { host: 'test', user: 'agent' },
okta: { foo: { bar: { baz: 1, qux: 11 } } },
});
});
it('should ignore historical static fields', async () => {
const metadata = {
cloud: { host: 'test' },
};
const historical = {
static: 'static',
cloud: { user: 'agent' },
okta: { foo: { bar: { qux: 1 } } },
};
const doc = {
historical,
...metadata,
};
const staticFields = ['static'];
const processor = dynamicNewestRetentionSteps(staticFields);
const result = await applyIngestProcessorToDoc([processor], doc, es, log);
return expect(result).to.eql({
historical,
cloud: { host: 'test', user: 'agent' },
okta: { foo: { bar: { qux: 1 } } },
});
});
});
});
};

View file

@ -13,6 +13,5 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./entity_store'));
loadTestFile(require.resolve('./field_retention_operators'));
loadTestFile(require.resolve('./entity_store_nondefault_spaces'));
loadTestFile(require.resolve('./asset_inventory_pipeline'));
});
}