From 598e63b532dba4e11b21bd6e0c316119247a6a9e Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Fri, 4 Jun 2021 14:05:51 -0600 Subject: [PATCH] [Security Solutions][Detection Engine] Adds e2e tests for constant_keyword data type (#101234) ## Summary Adds e2e tests for the `constant_keyword` regular `keyword` to compare between the two. Bugs found with these is one where we do not copy `constant_keyword` fields into signals which I added `.skip` to the tests now. Tested these rule types: * KQL * EQL * Threshold For the mappings of the `constant_keyword` I use both the `constant_keyword` and the field `alias` like so: ```json { "properties": { "@timestamp": { "type": "date" }, "data_stream": { "properties": { "dataset": { "type": "constant_keyword", "value": "dataset_name_1" }, "module": { "type": "constant_keyword", "value": "module_name_1" } } }, "event": { "properties": { "category": { "type": "keyword" }, "dataset": { "type": "alias", "path": "data_stream.dataset" }, "module": { "type": "alias", "path": "data_stream.module" } } } } } ``` To ensure we can detect against fields. I also mix them with regular const keyword fields in another index to ensure they work also in mixed use cases. ### Checklist - [x] [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 --- .../security_and_spaces/tests/index.ts | 6 + .../tests/keyword_family/README.md | 11 ++ .../tests/keyword_family/const_keyword.ts | 161 +++++++++++++++++ .../tests/keyword_family/index.ts | 21 +++ .../tests/keyword_family/keyword.ts | 131 ++++++++++++++ .../keyword_mixed_with_const.ts | 171 ++++++++++++++++++ .../es_archives/rule_keyword_family/README.md | 20 ++ .../const_keyword/data.json | 47 +++++ .../const_keyword/mappings.json | 48 +++++ .../rule_keyword_family/keyword/data.json | 63 +++++++ .../rule_keyword_family/keyword/mappings.json | 34 ++++ 11 files changed, 713 insertions(+) create mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/README.md create mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/const_keyword.ts create mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/index.ts create mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword.ts create mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword_mixed_with_const.ts create mode 100644 x-pack/test/functional/es_archives/rule_keyword_family/README.md create mode 100644 x-pack/test/functional/es_archives/rule_keyword_family/const_keyword/data.json create mode 100644 x-pack/test/functional/es_archives/rule_keyword_family/const_keyword/mappings.json create mode 100644 x-pack/test/functional/es_archives/rule_keyword_family/keyword/data.json create mode 100644 x-pack/test/functional/es_archives/rule_keyword_family/keyword/mappings.json diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts index 57b24f6de2a4..5756b02c238a 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts @@ -49,5 +49,11 @@ export default ({ loadTestFile }: FtrProviderContext): void => { describe('', function () { loadTestFile(require.resolve('./exception_operators_data_types/index')); }); + + // That split here enable us on using a different ciGroup to run the tests + // listed on ./keyword_family/index + describe('', function () { + loadTestFile(require.resolve('./keyword_family/index')); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/README.md b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/README.md new file mode 100644 index 000000000000..ab7f2ec862b0 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/README.md @@ -0,0 +1,11 @@ +These are tests for the [keyword](https://www.elastic.co/guide/en/elasticsearch/reference/7.12/keyword.html) family where we test +* keyword +* const keyword +* alias fields against each one + +Against mock rules which contain the ECS values of: +* event.module +* even.dataset + +This is to ensure that if you have field aliases we will still correctly have detections occur. This also ensures that if you have +`keyword` mixed with `const keyword` across multiple indexes we will still have detections occur. \ No newline at end of file diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/const_keyword.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/const_keyword.ts new file mode 100644 index 000000000000..43366915f154 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/const_keyword.ts @@ -0,0 +1,161 @@ +/* + * 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 { + EqlCreateSchema, + ThresholdCreateSchema, +} from '../../../../../plugins/security_solution/common/detection_engine/schemas/request'; + +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { + createRule, + createSignalsIndex, + deleteAllAlerts, + deleteSignalsIndex, + getRuleForSignalTesting, + getSignalsById, + waitForRuleSuccessOrStatus, + waitForSignalsToBePresent, +} from '../../../utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + interface EventModule { + module: string; + dataset: string; + } + + describe('Rule detects against a keyword of event.dataset', () => { + beforeEach(async () => { + await createSignalsIndex(supertest); + await esArchiver.load('rule_keyword_family/const_keyword'); + }); + + afterEach(async () => { + await deleteSignalsIndex(supertest); + await deleteAllAlerts(supertest); + await esArchiver.unload('rule_keyword_family/const_keyword'); + }); + + describe('"kql" rule type', () => { + it('should detect the "dataset_name_1" from "event.dataset" and have 4 signals', async () => { + const rule = { + ...getRuleForSignalTesting(['const_keyword']), + query: 'event.dataset: "dataset_name_1"', + }; + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 4, [id]); + const signalsOpen = await getSignalsById(supertest, id); + expect(signalsOpen.hits.hits.length).to.eql(4); + }); + + // TODO: Fix this bug and make this work. We currently do not write out the dataset name when it is not in _source + it.skip('should copy the dataset_name_1 from the index into the signal', async () => { + const rule = { + ...getRuleForSignalTesting(['const_keyword']), + query: 'event.dataset: "dataset_name_1"', + }; + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 4, [id]); + const signalsOpen = await getSignalsById(supertest, id); + const hits = signalsOpen.hits.hits + .map((hit) => (hit._source.event as EventModule).dataset) + .sort(); + expect(hits).to.eql([ + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + ]); + }); + }); + + describe('"eql" rule type', () => { + it('should detect the "dataset_name_1" from "event.dataset" and have 4 signals', async () => { + const rule: EqlCreateSchema = { + ...getRuleForSignalTesting(['const_keyword']), + rule_id: 'eql-rule', + type: 'eql', + language: 'eql', + query: 'any where event.dataset=="dataset_name_1"', + }; + + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 4, [id]); + const signalsOpen = await getSignalsById(supertest, id); + expect(signalsOpen.hits.hits.length).to.eql(4); + }); + + // TODO: Fix this bug and make this work. We currently do not write out the dataset name when it is not in _source + it.skip('should copy the "dataset_name_1" from "event.dataset"', async () => { + const rule: EqlCreateSchema = { + ...getRuleForSignalTesting(['const_keyword']), + rule_id: 'eql-rule', + type: 'eql', + language: 'eql', + query: 'any where event.dataset=="dataset_name_1"', + }; + + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 4, [id]); + const signalsOpen = await getSignalsById(supertest, id); + const hits = signalsOpen.hits.hits + .map((hit) => (hit._source.event as EventModule).dataset) + .sort(); + expect(hits).to.eql([ + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + ]); + }); + }); + + describe('"threshold" rule type', async () => { + it('should detect the "dataset_name_1" from "event.dataset"', async () => { + const rule: ThresholdCreateSchema = { + ...getRuleForSignalTesting(['const_keyword']), + rule_id: 'threshold-rule', + type: 'threshold', + language: 'kuery', + query: '*:*', + threshold: { + field: 'event.dataset', + value: 1, + }, + }; + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signalsOpen = await getSignalsById(supertest, id); + const hits = signalsOpen.hits.hits + .map((hit) => hit._source.signal.threshold_result ?? null) + .sort(); + expect(hits).to.eql([ + { + count: 4, + from: '1900-01-01T00:00:00.000Z', + terms: [ + { + field: 'event.dataset', + value: 'dataset_name_1', + }, + ], + }, + ]); + }); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/index.ts new file mode 100644 index 000000000000..4855524d650e --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/index.ts @@ -0,0 +1,21 @@ +/* + * 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 { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('Detection keyword family data types', function () { + describe('', function () { + this.tags('ciGroup11'); + + loadTestFile(require.resolve('./keyword')); + loadTestFile(require.resolve('./const_keyword')); + loadTestFile(require.resolve('./keyword_mixed_with_const')); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword.ts new file mode 100644 index 000000000000..7ba013184548 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword.ts @@ -0,0 +1,131 @@ +/* + * 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 { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { + createRule, + createSignalsIndex, + deleteAllAlerts, + deleteSignalsIndex, + getRuleForSignalTesting, + getSignalsById, + waitForRuleSuccessOrStatus, + waitForSignalsToBePresent, +} from '../../../utils'; +import { + EqlCreateSchema, + ThresholdCreateSchema, +} from '../../../../../plugins/security_solution/common/detection_engine/schemas/request'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + interface EventModule { + module: string; + dataset: string; + } + + describe('Rule detects against a keyword of event.dataset', () => { + beforeEach(async () => { + await createSignalsIndex(supertest); + await esArchiver.load('rule_keyword_family/keyword'); + }); + + afterEach(async () => { + await deleteSignalsIndex(supertest); + await deleteAllAlerts(supertest); + await esArchiver.unload('rule_keyword_family/keyword'); + }); + + describe('"kql" rule type', () => { + it('should detect the "dataset_name_1" from "event.dataset"', async () => { + const rule = { + ...getRuleForSignalTesting(['keyword']), + query: 'event.dataset: "dataset_name_1"', + }; + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 4, [id]); + const signalsOpen = await getSignalsById(supertest, id); + const hits = signalsOpen.hits.hits + .map((hit) => (hit._source.event as EventModule).dataset) + .sort(); + expect(hits).to.eql([ + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + ]); + }); + }); + + describe('"eql" rule type', () => { + it('should detect the "dataset_name_1" from "event.dataset"', async () => { + const rule: EqlCreateSchema = { + ...getRuleForSignalTesting(['keyword']), + rule_id: 'eql-rule', + type: 'eql', + language: 'eql', + query: 'any where event.dataset=="dataset_name_1"', + }; + + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 4, [id]); + const signalsOpen = await getSignalsById(supertest, id); + const hits = signalsOpen.hits.hits + .map((hit) => (hit._source.event as EventModule).dataset) + .sort(); + expect(hits).to.eql([ + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + ]); + }); + }); + + describe('"threshold" rule type', async () => { + it('should detect the "dataset_name_1" from "event.dataset"', async () => { + const rule: ThresholdCreateSchema = { + ...getRuleForSignalTesting(['keyword']), + rule_id: 'threshold-rule', + type: 'threshold', + language: 'kuery', + query: '*:*', + threshold: { + field: 'event.dataset', + value: 1, + }, + }; + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signalsOpen = await getSignalsById(supertest, id); + const hits = signalsOpen.hits.hits + .map((hit) => hit._source.signal.threshold_result ?? null) + .sort(); + expect(hits).to.eql([ + { + count: 4, + from: '1900-01-01T00:00:00.000Z', + terms: [ + { + field: 'event.dataset', + value: 'dataset_name_1', + }, + ], + }, + ]); + }); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword_mixed_with_const.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword_mixed_with_const.ts new file mode 100644 index 000000000000..a5c69f98c3fe --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword_mixed_with_const.ts @@ -0,0 +1,171 @@ +/* + * 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 { + EqlCreateSchema, + ThresholdCreateSchema, +} from '../../../../../plugins/security_solution/common/detection_engine/schemas/request'; + +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { + createRule, + createSignalsIndex, + deleteAllAlerts, + deleteSignalsIndex, + getRuleForSignalTesting, + getSignalsById, + waitForRuleSuccessOrStatus, + waitForSignalsToBePresent, +} from '../../../utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + interface EventModule { + module: string; + dataset: string; + } + + describe('Rule detects against a keyword and constant_keyword of event.dataset', () => { + beforeEach(async () => { + await createSignalsIndex(supertest); + await esArchiver.load('rule_keyword_family/const_keyword'); + await esArchiver.load('rule_keyword_family/keyword'); + }); + + afterEach(async () => { + await deleteSignalsIndex(supertest); + await deleteAllAlerts(supertest); + await esArchiver.unload('rule_keyword_family/const_keyword'); + await esArchiver.unload('rule_keyword_family/keyword'); + }); + + describe('"kql" rule type', () => { + it('should detect the "dataset_name_1" from "event.dataset" and have 8 signals, 4 from each index', async () => { + const rule = { + ...getRuleForSignalTesting(['keyword', 'const_keyword']), + query: 'event.dataset: "dataset_name_1"', + }; + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 8, [id]); + const signalsOpen = await getSignalsById(supertest, id); + expect(signalsOpen.hits.hits.length).to.eql(8); + }); + + // TODO: Fix this bug and make this work. We currently do not write out the dataset name when it is not in _source + it.skip('should copy the dataset_name_1 from the index into the signal', async () => { + const rule = { + ...getRuleForSignalTesting(['keyword', 'const_keyword']), + query: 'event.dataset: "dataset_name_1"', + }; + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 8, [id]); + const signalsOpen = await getSignalsById(supertest, id); + const hits = signalsOpen.hits.hits + .map((hit) => (hit._source.event as EventModule).dataset) + .sort(); + expect(hits).to.eql([ + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + ]); + }); + }); + + describe('"eql" rule type', () => { + it('should detect the "dataset_name_1" from "event.dataset" and have 8 signals, 4 from each index', async () => { + const rule: EqlCreateSchema = { + ...getRuleForSignalTesting(['keyword', 'const_keyword']), + rule_id: 'eql-rule', + type: 'eql', + language: 'eql', + query: 'any where event.dataset=="dataset_name_1"', + }; + + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 8, [id]); + const signalsOpen = await getSignalsById(supertest, id); + expect(signalsOpen.hits.hits.length).to.eql(8); + }); + + // TODO: Fix this bug and make this work. We currently do not write out the dataset name when it is not in _source + it.skip('should copy the "dataset_name_1" from "event.dataset"', async () => { + const rule: EqlCreateSchema = { + ...getRuleForSignalTesting(['keyword', 'const_keyword']), + rule_id: 'eql-rule', + type: 'eql', + language: 'eql', + query: 'any where event.dataset=="dataset_name_1"', + }; + + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 8, [id]); + const signalsOpen = await getSignalsById(supertest, id); + const hits = signalsOpen.hits.hits + .map((hit) => (hit._source.event as EventModule).dataset) + .sort(); + expect(hits).to.eql([ + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + 'dataset_name_1', + ]); + }); + }); + + describe('"threshold" rule type', async () => { + it('should detect the "dataset_name_1" from "event.dataset"', async () => { + const rule: ThresholdCreateSchema = { + ...getRuleForSignalTesting(['keyword', 'const_keyword']), + rule_id: 'threshold-rule', + type: 'threshold', + language: 'kuery', + query: '*:*', + threshold: { + field: 'event.dataset', + value: 1, + }, + }; + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signalsOpen = await getSignalsById(supertest, id); + const hits = signalsOpen.hits.hits + .map((hit) => hit._source.signal.threshold_result ?? null) + .sort(); + expect(hits).to.eql([ + { + count: 8, + from: '1900-01-01T00:00:00.000Z', + terms: [ + { + field: 'event.dataset', + value: 'dataset_name_1', + }, + ], + }, + ]); + }); + }); + }); +}; diff --git a/x-pack/test/functional/es_archives/rule_keyword_family/README.md b/x-pack/test/functional/es_archives/rule_keyword_family/README.md new file mode 100644 index 000000000000..b6849e7ea591 --- /dev/null +++ b/x-pack/test/functional/es_archives/rule_keyword_family/README.md @@ -0,0 +1,20 @@ +Within this folder is input test data for tests within the folder: + +```ts +x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family +``` + +where these are small ECS compliant input indexes that try to express tests that exercise different parts of +the detection engine around creating and validating that the keyword family and field aliases all will work +with the detection engine. These indexes might contain extra fields or different fields but should not directly +clash with ECS or minimally clash. Nothing is stopping anyone from being ECS strict and not having additional +extra fields but the extra fields and mappings are to just try and keep these tests simple and small. + +Most of these tests center around the two fields of: +* event.module +* event.dataset + +To ensure that if mix and match between `keyword`, `const keyword` and field aliases within them, everything should +still be ok. It is alright if other use cases are added here if they fit within the `keyword` family as described here: +https://www.elastic.co/guide/en/elasticsearch/reference/7.12/keyword.html + diff --git a/x-pack/test/functional/es_archives/rule_keyword_family/const_keyword/data.json b/x-pack/test/functional/es_archives/rule_keyword_family/const_keyword/data.json new file mode 100644 index 000000000000..2d0359c6ff82 --- /dev/null +++ b/x-pack/test/functional/es_archives/rule_keyword_family/const_keyword/data.json @@ -0,0 +1,47 @@ +{ + "type": "doc", + "value": { + "id": "1", + "index": "const_keyword", + "source": { + "@timestamp": "2020-10-27T05:00:53.000Z" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "2", + "index": "const_keyword", + "source": { + "@timestamp": "2020-10-27T05:01:53.000Z" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "3", + "index": "const_keyword", + "source": { + "@timestamp": "2020-10-27T05:02:53.000Z" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "4", + "index": "const_keyword", + "source": { + "@timestamp": "2020-10-27T05:03:53.000Z" + }, + "type": "_doc" + } +} diff --git a/x-pack/test/functional/es_archives/rule_keyword_family/const_keyword/mappings.json b/x-pack/test/functional/es_archives/rule_keyword_family/const_keyword/mappings.json new file mode 100644 index 000000000000..7e3d74f84014 --- /dev/null +++ b/x-pack/test/functional/es_archives/rule_keyword_family/const_keyword/mappings.json @@ -0,0 +1,48 @@ +{ + "type": "index", + "value": { + "index": "const_keyword", + "mappings": { + "dynamic": "strict", + "properties": { + "@timestamp": { + "type": "date" + }, + "data_stream": { + "properties": { + "dataset": { + "type": "constant_keyword", + "value": "dataset_name_1" + }, + "module": { + "type": "constant_keyword", + "value": "module_name_1" + } + } + }, + "event": { + "properties": { + "category": { + "type": "keyword" + }, + "dataset": { + "type": "alias", + "path": "data_stream.dataset" + }, + "module": { + "type": "alias", + "path": "data_stream.module" + } + } + } + } + }, + "settings": { + "index": { + "refresh_interval": "1s", + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} diff --git a/x-pack/test/functional/es_archives/rule_keyword_family/keyword/data.json b/x-pack/test/functional/es_archives/rule_keyword_family/keyword/data.json new file mode 100644 index 000000000000..40118aa0e2ba --- /dev/null +++ b/x-pack/test/functional/es_archives/rule_keyword_family/keyword/data.json @@ -0,0 +1,63 @@ +{ + "type": "doc", + "value": { + "id": "1", + "index": "keyword", + "source": { + "@timestamp": "2020-10-28T05:00:53.000Z", + "event": { + "module": "module_name_1", + "dataset": "dataset_name_1" + } + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "2", + "index": "keyword", + "source": { + "@timestamp": "2020-10-28T05:01:53.000Z", + "event": { + "module": "module_name_1", + "dataset": "dataset_name_1" + } + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "3", + "index": "keyword", + "source": { + "@timestamp": "2020-10-28T05:02:53.000Z", + "event": { + "module": "module_name_1", + "dataset": "dataset_name_1" + } + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "4", + "index": "keyword", + "source": { + "@timestamp": "2020-10-28T05:03:53.000Z", + "event": { + "module": "module_name_1", + "dataset": "dataset_name_1" + } + }, + "type": "_doc" + } +} diff --git a/x-pack/test/functional/es_archives/rule_keyword_family/keyword/mappings.json b/x-pack/test/functional/es_archives/rule_keyword_family/keyword/mappings.json new file mode 100644 index 000000000000..9d5274a00227 --- /dev/null +++ b/x-pack/test/functional/es_archives/rule_keyword_family/keyword/mappings.json @@ -0,0 +1,34 @@ +{ + "type": "index", + "value": { + "index": "keyword", + "mappings": { + "dynamic": "strict", + "properties": { + "@timestamp": { + "type": "date" + }, + "event": { + "properties": { + "category": { + "type": "keyword" + }, + "module": { + "type": "keyword" + }, + "dataset": { + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "refresh_interval": "1s", + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +}