From f1fade2bcf39a401adda770e6ba545a09a570fb6 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatia <117628830+abhishekbhatia1710@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:37:13 +0530 Subject: [PATCH 1/4] [Entity Analytics] API changes for right placement of deleting the old component template (#199734) ## Summary - Delete the old component template after the index template has referenced to the new component template - Test cases for the same flow ```JSON # Let's assume this is 8.15.3 # Create the component template when Risk Score engine is initialised # Create the index template which references the created component template PUT /_component_template/.risk-score-mappings { "template": { "settings": { "number_of_shards": 1 }, "mappings": { "properties": { "timestamp": { "type": "date" }, "user": { "properties": { "id": { "type": "keyword" }, "name": { "type": "text" } } } } } }, "version": 1 } PUT /_index_template/.risk-score.risk-score-default-index-template { "index_patterns": [".risk-score.risk-score-default-index-template"], "template": { "settings": { "number_of_replicas": 1 } }, "composed_of": [".risk-score-mappings"], "priority": 100, "version": 1, "_meta": { "description": "Index template for indices with the pattern my_index-*" } } # The deployment is updated to 8.16 # User tries to enable the Entity store which init's the Risk Score engine (again!!) # Fails, but creates the component template and cannot update the index template to reference the new component template due to the error PUT /_component_template/.risk-score-mappings-default { "template": { "settings": { "number_of_shards": 1 }, "mappings": { "properties": { "timestamp": { "type": "date" }, "user": { "properties": { "id": { "type": "keyword" }, "name": { "type": "text" } } } } } }, "version": 1 } GET /_component_template?filter_path=component_templates.name&name=.risk-score-mappings* DELETE /_component_template/.risk-score-mappings # Fails # changed flow PUT /_index_template/.risk-score.risk-score-default-index-template { "index_patterns": [".risk-score.risk-score-default-index-template"], "template": { "settings": { "number_of_replicas": 1 } }, "composed_of": [".risk-score-mappings-default"], "priority": 100, "version": 1, "_meta": { "description": "Index template for indices with the pattern my_index-*" } } DELETE /_component_template/.risk-score-mappings # Succeeds ######### ``` ### Checklist Delete any items that are not applicable to this PR. - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels) - [ ] This will appear in the **Release Notes** and follow the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) (cherry picked from commit 771c139269ac0eac27274e0858be2fbcf87b31e3) # Conflicts: # x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts # x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts --- .../risk_score/configurations.ts | 3 + .../risk_score/risk_score_data_client.ts | 37 ++++++++- .../init_and_status_apis.ts | 82 ++++++++++++++++++- 3 files changed, 120 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts index 6dbf68c699fd5..610ada87f5159 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts @@ -129,6 +129,9 @@ export const riskScoreFieldMap: FieldMap = { } as const; export const mappingComponentName = '.risk-score-mappings'; +export const nameSpaceAwareMappingsComponentName = (namespace: string): string => { + return `${mappingComponentName}-${namespace}`; +}; export const totalFieldsLimit = 1000; export const getIndexPatternDataStream = (namespace: string): IIndexPatternString => ({ diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts index 0b3c38f3602fd..93ba4e8d4ed46 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts @@ -22,6 +22,7 @@ import { getIndexPatternDataStream, getTransformOptions, mappingComponentName, + nameSpaceAwareMappingsComponentName, riskScoreFieldMap, totalFieldsLimit, } from './configurations'; @@ -51,7 +52,7 @@ interface RiskScoringDataClientOpts { export class RiskScoreDataClient { private writerCache: Map = new Map(); - constructor(private readonly options: RiskScoringDataClientOpts) {} + constructor(private readonly options: RiskScoringDataClientOpts) { } public async getWriter({ namespace }: { namespace: string }): Promise { if (this.writerCache.get(namespace)) { @@ -114,6 +115,16 @@ export class RiskScoreDataClient { namespace, }; + // Check if there are any existing component templates with the namespace in the name + + const oldComponentTemplateExists = await esClient.cluster.existsComponentTemplate({ + name: mappingComponentName, + }); + if (oldComponentTemplateExists) { + await this.updateComponentTemplateNamewithNamespace(namespace); + } + + // Update the new component template with the required data await Promise.all([ createOrUpdateComponentTemplate({ logger: this.options.logger, @@ -156,6 +167,14 @@ export class RiskScoreDataClient { }, }); + // Delete the component template without the namespace in the name + await esClient.cluster.deleteComponentTemplate( + { + name: mappingComponentName, + }, + { ignore: [404] } + ); + await createDataStream({ logger: this.options.logger, esClient, @@ -286,4 +305,20 @@ export class RiskScoreDataClient { { logger: this.options.logger } ); } + + private async updateComponentTemplateNamewithNamespace(namespace: string): Promise { + const esClient = this.options.esClient; + const oldComponentTemplateResponse = await esClient.cluster.getComponentTemplate( + { + name: mappingComponentName, + }, + { ignore: [404] } + ); + const oldComponentTemplate = oldComponentTemplateResponse?.component_templates[0]; + const newComponentTemplateName = nameSpaceAwareMappingsComponentName(namespace); + await esClient.cluster.putComponentTemplate({ + name: newComponentTemplateName, + body: oldComponentTemplate.component_template, + }); + } } diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts index f0b966c1c166e..532b415911cd2 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts @@ -303,7 +303,87 @@ export default ({ getService }: FtrProviderContext) => { ); }); - describe('remove legacy risk score transform', function () { + it('should update the existing component template and index template without any errors', async () => { + const componentTemplateName = '.risk-score-mappings'; + const indexTemplateName = '.risk-score.risk-score-default-index-template'; + const newComponentTemplateName = '.risk-score-mappings-default'; + + // Call API to put the component template and index template + + await es.cluster.putComponentTemplate({ + name: componentTemplateName, + body: { + template: { + settings: { + number_of_shards: 1, + }, + mappings: { + properties: { + timestamp: { + type: 'date', + }, + user: { + properties: { + id: { + type: 'keyword', + }, + name: { + type: 'text', + }, + }, + }, + }, + }, + }, + version: 1, + }, + }); + + // Call an API to put the index template + + await es.indices.putIndexTemplate({ + name: indexTemplateName, + body: { + index_patterns: [indexTemplateName], + composed_of: [componentTemplateName], + template: { + settings: { + number_of_shards: 1, + }, + mappings: { + properties: { + timestamp: { + type: 'date', + }, + user: { + properties: { + id: { + type: 'keyword', + }, + name: { + type: 'text', + }, + }, + }, + }, + }, + }, + }, + }); + + const response = await riskEngineRoutes.init(); + expect(response.status).to.eql(200); + expect(response.body.result.errors).to.eql([]); + + const response2 = await es.cluster.getComponentTemplate({ + name: newComponentTemplateName, + }); + expect(response2.component_templates.length).to.eql(1); + expect(response2.component_templates[0].name).to.eql(newComponentTemplateName); + }); + + // Failing: See https://github.com/elastic/kibana/issues/191637 + describe.skip('remove legacy risk score transform', function () { this.tags('skipFIPS'); it('should remove legacy risk score transform if it exists', async () => { await installLegacyRiskScore({ supertest }); From 3e0b8b97bfbb6d30670be3d7e955d7957fee135c Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 19 Nov 2024 07:37:14 +0000 Subject: [PATCH 2/4] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../lib/entity_analytics/risk_score/risk_score_data_client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts index 93ba4e8d4ed46..572a8deafd9bc 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts @@ -52,7 +52,7 @@ interface RiskScoringDataClientOpts { export class RiskScoreDataClient { private writerCache: Map = new Map(); - constructor(private readonly options: RiskScoringDataClientOpts) { } + constructor(private readonly options: RiskScoringDataClientOpts) {} public async getWriter({ namespace }: { namespace: string }): Promise { if (this.writerCache.get(namespace)) { From 7eb20530bb241df358958405410594ad89ea9d22 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Wed, 27 Nov 2024 19:43:11 +0530 Subject: [PATCH 3/4] Replacing the incorrect reference when creating or updating the index template. --- .../lib/entity_analytics/risk_score/risk_score_data_client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts index 572a8deafd9bc..23c4317de3b8c 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts @@ -130,7 +130,7 @@ export class RiskScoreDataClient { logger: this.options.logger, esClient, template: { - name: mappingComponentName, + name: nameSpaceAwareMappingsComponentName(namespace), _meta: { managed: true, }, @@ -151,7 +151,7 @@ export class RiskScoreDataClient { body: { data_stream: { hidden: true }, index_patterns: [indexPatterns.alias], - composed_of: [mappingComponentName], + composed_of: [nameSpaceAwareMappingsComponentName(namespace)], template: { lifecycle: {}, settings: { From e4094a422898ee35e4b76ec7c0e1245e701b0923 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Wed, 27 Nov 2024 21:27:58 +0530 Subject: [PATCH 4/4] Missing changes for the 8.x --- .../risk_score/risk_score_data_client.test.ts | 674 +++++++++--------- 1 file changed, 352 insertions(+), 322 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.test.ts index 2ddd04a766944..72c0e14f9913b 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.test.ts @@ -40,6 +40,7 @@ jest.spyOn(transforms, 'scheduleTransformNow').mockResolvedValue(Promise.resolve describe('RiskScoreDataClient', () => { let riskScoreDataClient: RiskScoreDataClient; + let riskScoreDataClientWithNameSpace: RiskScoreDataClient; let mockSavedObjectClient: ReturnType; let logger: ReturnType; const esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser; @@ -56,6 +57,8 @@ describe('RiskScoreDataClient', () => { namespace: 'default', }; riskScoreDataClient = new RiskScoreDataClient(options); + const optionsWithNamespace = { ...options, namespace: 'space-1' }; + riskScoreDataClientWithNameSpace = new RiskScoreDataClient(optionsWithNamespace); }); afterEach(() => { @@ -80,369 +83,396 @@ describe('RiskScoreDataClient', () => { }); describe('init success', () => { - it('should initialize risk engine resources', async () => { - await riskScoreDataClient.init(); + it('should initialize risk engine resources in the appropriate space', async () => { + const assertComponentTemplate = (namespace: string) => { + expect(createOrUpdateComponentTemplate).toHaveBeenCalledWith( + expect.objectContaining({ + logger, + esClient, + template: expect.objectContaining({ + name: `.risk-score-mappings-${namespace}`, + _meta: { + managed: true, + }, + }), + totalFieldsLimit: 1000, + }) + ); + }; - expect(createOrUpdateComponentTemplate).toHaveBeenCalledWith( - expect.objectContaining({ + const assertIndexTemplate = (namespace: string) => { + expect(createOrUpdateIndexTemplate).toHaveBeenCalledWith({ logger, esClient, - template: expect.objectContaining({ - name: '.risk-score-mappings', - _meta: { - managed: true, - }, - }), - totalFieldsLimit: 1000, - }) - ); - expect((createOrUpdateComponentTemplate as jest.Mock).mock.lastCall[0].template.template) - .toMatchInlineSnapshot(` - Object { - "mappings": Object { - "dynamic": "strict", - "properties": Object { - "@timestamp": Object { - "ignore_malformed": false, - "type": "date", + template: { + name: `.risk-score.risk-score-${namespace}-index-template`, + body: { + data_stream: { hidden: true }, + index_patterns: [`risk-score.risk-score-${namespace}`], + composed_of: [`.risk-score-mappings-${namespace}`], + template: { + lifecycle: {}, + settings: { + 'index.mapping.total_fields.limit': totalFieldsLimit, + }, + mappings: { + dynamic: false, + _meta: { + kibana: { + version: '8.9.0', + }, + managed: true, + namespace, }, - "host": Object { - "properties": Object { - "name": Object { - "type": "keyword", - }, - "risk": Object { - "properties": Object { - "calculated_level": Object { - "type": "keyword", - }, - "calculated_score": Object { - "type": "float", - }, - "calculated_score_norm": Object { - "type": "float", - }, - "category_1_count": Object { - "type": "long", - }, - "category_1_score": Object { - "type": "float", - }, - "id_field": Object { - "type": "keyword", - }, - "id_value": Object { - "type": "keyword", - }, - "inputs": Object { - "properties": Object { - "category": Object { - "type": "keyword", - }, - "description": Object { - "type": "keyword", - }, - "id": Object { - "type": "keyword", - }, - "index": Object { - "type": "keyword", - }, - "risk_score": Object { - "type": "float", - }, - "timestamp": Object { - "type": "date", - }, + }, + }, + _meta: { + kibana: { + version: '8.9.0', + }, + managed: true, + namespace, + }, + }, + }, + }); + }; + + const assertDataStream = (namespace: string) => { + expect(createDataStream).toHaveBeenCalledWith({ + logger, + esClient, + totalFieldsLimit, + indexPatterns: { + template: `.risk-score.risk-score-${namespace}-index-template`, + alias: `risk-score.risk-score-${namespace}`, + }, + }); + }; + + const assertIndex = (namespace: string) => { + expect(createOrUpdateIndex).toHaveBeenCalledWith({ + logger, + esClient, + options: { + index: `risk-score.risk-score-latest-${namespace}`, + mappings: { + dynamic: false, + properties: { + '@timestamp': { + ignore_malformed: false, + type: 'date', + }, + host: { + properties: { + name: { + type: 'keyword', + }, + risk: { + properties: { + calculated_level: { + type: 'keyword', + }, + calculated_score: { + type: 'float', + }, + calculated_score_norm: { + type: 'float', + }, + category_1_count: { + type: 'long', + }, + category_1_score: { + type: 'float', + }, + id_field: { + type: 'keyword', + }, + id_value: { + type: 'keyword', + }, + inputs: { + properties: { + category: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + index: { + type: 'keyword', + }, + risk_score: { + type: 'float', + }, + timestamp: { + type: 'date', }, - "type": "object", - }, - "notes": Object { - "type": "keyword", }, + type: 'object', + }, + notes: { + type: 'keyword', }, - "type": "object", }, + type: 'object', }, }, - "user": Object { - "properties": Object { - "name": Object { - "type": "keyword", - }, - "risk": Object { - "properties": Object { - "calculated_level": Object { - "type": "keyword", - }, - "calculated_score": Object { - "type": "float", - }, - "calculated_score_norm": Object { - "type": "float", - }, - "category_1_count": Object { - "type": "long", - }, - "category_1_score": Object { - "type": "float", - }, - "id_field": Object { - "type": "keyword", - }, - "id_value": Object { - "type": "keyword", - }, - "inputs": Object { - "properties": Object { - "category": Object { - "type": "keyword", - }, - "description": Object { - "type": "keyword", - }, - "id": Object { - "type": "keyword", - }, - "index": Object { - "type": "keyword", - }, - "risk_score": Object { - "type": "float", - }, - "timestamp": Object { - "type": "date", - }, + }, + user: { + properties: { + name: { + type: 'keyword', + }, + risk: { + properties: { + calculated_level: { + type: 'keyword', + }, + calculated_score: { + type: 'float', + }, + calculated_score_norm: { + type: 'float', + }, + category_1_count: { + type: 'long', + }, + category_1_score: { + type: 'float', + }, + id_field: { + type: 'keyword', + }, + id_value: { + type: 'keyword', + }, + inputs: { + properties: { + category: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + index: { + type: 'keyword', + }, + risk_score: { + type: 'float', + }, + timestamp: { + type: 'date', }, - "type": "object", - }, - "notes": Object { - "type": "keyword", }, + type: 'object', + }, + notes: { + type: 'keyword', }, - "type": "object", }, + type: 'object', }, }, }, }, - "settings": Object {}, - } - `); + }, + }, + }); + }; - expect(createOrUpdateIndexTemplate).toHaveBeenCalledWith({ - logger, - esClient, - template: { - name: '.risk-score.risk-score-default-index-template', - body: { - data_stream: { hidden: true }, - index_patterns: ['risk-score.risk-score-default'], - composed_of: ['.risk-score-mappings'], - template: { - lifecycle: {}, - settings: { - 'index.mapping.total_fields.limit': totalFieldsLimit, - }, - mappings: { - dynamic: false, - _meta: { - kibana: { - version: '8.9.0', - }, - managed: true, - namespace: 'default', - }, + const assertTransform = (namespace: string) => { + expect(transforms.createTransform).toHaveBeenCalledWith({ + logger, + esClient, + transform: { + dest: { + index: `risk-score.risk-score-latest-${namespace}`, + }, + frequency: '1h', + latest: { + sort: '@timestamp', + unique_key: ['host.name', 'user.name'], + }, + source: { + index: [`risk-score.risk-score-${namespace}`], + }, + sync: { + time: { + delay: '0s', + field: '@timestamp', }, }, + transform_id: `risk_score_latest_transform_${namespace}`, + settings: { + unattended: true, + }, _meta: { - kibana: { - version: '8.9.0', - }, + version: 2, managed: true, - namespace: 'default', + managed_by: 'security-entity-analytics', }, }, - }, - }); + }); + }; - expect(createDataStream).toHaveBeenCalledWith({ - logger, - esClient, - totalFieldsLimit, - indexPatterns: { - template: `.risk-score.risk-score-default-index-template`, - alias: `risk-score.risk-score-default`, - }, - }); + // Default namespace + esClient.cluster.existsComponentTemplate.mockResolvedValue(false); + await riskScoreDataClient.init(); + assertComponentTemplate('default'); + assertIndexTemplate('default'); + assertDataStream('default'); + assertIndex('default'); + assertTransform('default'); - expect(createOrUpdateIndex).toHaveBeenCalledWith({ - logger, - esClient, - options: { - index: `risk-score.risk-score-latest-default`, - mappings: { - dynamic: false, - properties: { - '@timestamp': { - ignore_malformed: false, - type: 'date', - }, - host: { - properties: { - name: { - type: 'keyword', - }, - risk: { - properties: { - calculated_level: { - type: 'keyword', - }, - calculated_score: { - type: 'float', - }, - calculated_score_norm: { - type: 'float', - }, - category_1_count: { - type: 'long', - }, - category_1_score: { - type: 'float', - }, - id_field: { - type: 'keyword', - }, - id_value: { - type: 'keyword', - }, - inputs: { - properties: { - category: { - type: 'keyword', - }, - description: { - type: 'keyword', - }, - id: { - type: 'keyword', - }, - index: { - type: 'keyword', - }, - risk_score: { - type: 'float', - }, - timestamp: { - type: 'date', + // Space-1 namespace + esClient.cluster.existsComponentTemplate.mockResolvedValue(false); + await riskScoreDataClientWithNameSpace.init(); + assertComponentTemplate('space-1'); + assertIndexTemplate('space-1'); + assertDataStream('space-1'); + assertIndex('space-1'); + assertTransform('space-1'); + + expect((createOrUpdateComponentTemplate as jest.Mock).mock.lastCall[0].template.template) + .toMatchInlineSnapshot(` + Object { + "mappings": Object { + "dynamic": "strict", + "properties": Object { + "@timestamp": Object { + "ignore_malformed": false, + "type": "date", + }, + "host": Object { + "properties": Object { + "name": Object { + "type": "keyword", + }, + "risk": Object { + "properties": Object { + "calculated_level": Object { + "type": "keyword", + }, + "calculated_score": Object { + "type": "float", + }, + "calculated_score_norm": Object { + "type": "float", + }, + "category_1_count": Object { + "type": "long", + }, + "category_1_score": Object { + "type": "float", + }, + "id_field": Object { + "type": "keyword", + }, + "id_value": Object { + "type": "keyword", + }, + "inputs": Object { + "properties": Object { + "category": Object { + "type": "keyword", + }, + "description": Object { + "type": "keyword", + }, + "id": Object { + "type": "keyword", + }, + "index": Object { + "type": "keyword", + }, + "risk_score": Object { + "type": "float", + }, + "timestamp": Object { + "type": "date", + }, }, + "type": "object", + }, + "notes": Object { + "type": "keyword", }, - type: 'object', - }, - notes: { - type: 'keyword', }, + "type": "object", }, - type: 'object', }, }, - }, - user: { - properties: { - name: { - type: 'keyword', - }, - risk: { - properties: { - calculated_level: { - type: 'keyword', - }, - calculated_score: { - type: 'float', - }, - calculated_score_norm: { - type: 'float', - }, - category_1_count: { - type: 'long', - }, - category_1_score: { - type: 'float', - }, - id_field: { - type: 'keyword', - }, - id_value: { - type: 'keyword', - }, - inputs: { - properties: { - category: { - type: 'keyword', - }, - description: { - type: 'keyword', - }, - id: { - type: 'keyword', - }, - index: { - type: 'keyword', - }, - risk_score: { - type: 'float', - }, - timestamp: { - type: 'date', + "user": Object { + "properties": Object { + "name": Object { + "type": "keyword", + }, + "risk": Object { + "properties": Object { + "calculated_level": Object { + "type": "keyword", + }, + "calculated_score": Object { + "type": "float", + }, + "calculated_score_norm": Object { + "type": "float", + }, + "category_1_count": Object { + "type": "long", + }, + "category_1_score": Object { + "type": "float", + }, + "id_field": Object { + "type": "keyword", + }, + "id_value": Object { + "type": "keyword", + }, + "inputs": Object { + "properties": Object { + "category": Object { + "type": "keyword", + }, + "description": Object { + "type": "keyword", + }, + "id": Object { + "type": "keyword", + }, + "index": Object { + "type": "keyword", + }, + "risk_score": Object { + "type": "float", + }, + "timestamp": Object { + "type": "date", + }, }, + "type": "object", + }, + "notes": Object { + "type": "keyword", }, - type: 'object', - }, - notes: { - type: 'keyword', }, + "type": "object", }, - type: 'object', }, }, }, }, - }, - }, - }); - - expect(transforms.createTransform).toHaveBeenCalledWith({ - logger, - esClient, - transform: { - dest: { - index: 'risk-score.risk-score-latest-default', - }, - frequency: '1h', - latest: { - sort: '@timestamp', - unique_key: ['host.name', 'user.name'], - }, - source: { - index: ['risk-score.risk-score-default'], - }, - sync: { - time: { - delay: '0s', - field: '@timestamp', - }, - }, - transform_id: 'risk_score_latest_transform_default', - settings: { - unattended: true, - }, - _meta: { - version: 2, - managed: true, - managed_by: 'security-entity-analytics', - }, - }, - }); + "settings": Object {}, + } + `); }); });