From 3bff7fbf24576ab2620d20114e633a479e922d15 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 2 Oct 2023 14:05:01 -0400 Subject: [PATCH] [Response Ops][Alerting] Update framework alerts client to write flattened alerts docs (#167691) Resolves https://github.com/elastic/kibana/issues/166946 ## PRs to this feature branch * https://github.com/elastic/kibana/pull/167439 * https://github.com/elastic/kibana/pull/167583 ## Summary The rule registry has traditionally written out AAD docs with flattened keys, like ``` { "kibana.alert.rule.name": "test" } ``` The framework alerts client has been writing out AAD docs as objects, like ``` { "kibana": { "alert": { "rule": { "name": "test" } } } } ``` We've identified a few places where we're updating the docs where having this divergence makes things more difficult, so this is to switch the framework to writing flattened alert docs before onboarding more rule types. This PR is targeted for 8.11, which is also when we onboarded the index threshold rule type and the ML anomaly detection rule type to FAAD. For the ES query rule, which started writing unflattened AaD docs in 8.10, this PR adds special handling to ensure that those unflattened docs are correctly updated with flattened fields. ## To Verify ### ES Query and Index Threshold AaD Create these rules that trigger alerts and verify that their AaD docs are written out as flattened. For the ES Query rule type, select a Metrics/Logs consumer and verify that they appear on the O11y alerts table. ### ML alerts ML alerts added in https://github.com/elastic/kibana/pull/166349 looked like:
Unflattened ``` { "kibana": { "alert": { "url": "/app/ml/explorer/?_g=(ml%3A(jobIds%3A!(rt-anomaly-mean-value))%2Ctime%3A(from%3A'2023-09-28T14%3A57%3A00.000Z'%2Cmode%3Aabsolute%2Cto%3A'2023-09-28T15%3A17%3A00.000Z'))&_a=(explorer%3A(mlExplorerFilter%3A(filterActive%3A!t%2CfilteredFields%3A!(key%2Cthird-key)%2CinfluencersFilterQuery%3A(bool%3A(minimum_should_match%3A1%2Cshould%3A!((match_phrase%3A(key%3Athird-key)))))%2CqueryString%3A'key%3A%22third-key%22')%2CmlExplorerSwimlane%3A()))", "reason": "Alerts are raised based on real-time scores. Remember that scores may be adjusted over time as data continues to be analyzed.", "job_id": "rt-anomaly-mean-value", "anomaly_score": 73.63508175828011, "is_interim": false, "anomaly_timestamp": 1695913620000, "top_records": [{ "job_id": "rt-anomaly-mean-value", "record_score": 73.63516446528412, "initial_record_score": 73.63516446528412, "detector_index": 0, "is_interim": false, "timestamp": 1695913620000, "partition_field_name": "key", "partition_field_value": "third-key", "function": "mean", "actual": [ 3 ], "typical": [ 4.187715468532429 ] }], "top_influencers": [{ "job_id": "rt-anomaly-mean-value", "influencer_field_name": "key", "influencer_field_value": "third-key", "influencer_score": 73.63508175828011, "initial_influencer_score": 73.63508175828011, "is_interim": false, "timestamp": 1695913620000 }], "action_group": "anomaly_score_match", "flapping": false, "flapping_history": [ true, false, false, false ], "instance": { "id": "rt-anomaly-mean-value" }, "maintenance_window_ids": [], "rule": { "category": "Anomaly detection alert", "consumer": "alerts", "execution": { "uuid": "e9e681d4-c8e4-43eb-82e5-a58bdf7ffe12" }, "name": "rt-ad-alert-influencer", "parameters": { "severity": 5, "resultType": "influencer", "includeInterim": false, "jobSelection": { "jobIds": [ "rt-anomaly-mean-value" ], "groupIds": [] }, "lookbackInterval": null, "topNBuckets": null }, "producer": "ml", "revision": 0, "rule_type_id": "xpack.ml.anomaly_detection_alert", "tags": [], "uuid": "9e1d6bc0-5e10-11ee-8416-3bf48cca0922" }, "status": "active", "uuid": "c9c1f075-9985-4c55-8ff8-22349cb30269", "workflow_status": "open", "duration": { "us": "99021000000" }, "start": "2023-09-28T15:07:12.868Z", "time_range": { "gte": "2023-09-28T15:07:12.868Z" } }, "space_ids": [ "default" ], "version": "8.11.0" }, "@timestamp": "2023-09-28T15:08:51.889Z", "event": { "action": "active", "kind": "signal" }, "tags": [] } ```
Now they look like:
Flattened ``` { "kibana.alert.url": "/app/ml/explorer/?_g=(ml%3A(jobIds%3A!(rt-anomaly-mean-value))%2Ctime%3A(from%3A'2023-09-28T15%3A03%3A00.000Z'%2Cmode%3Aabsolute%2Cto%3A'2023-09-28T15%3A23%3A00.000Z'))&_a=(explorer%3A(mlExplorerFilter%3A(filterActive%3A!t%2CfilteredFields%3A!(key%2Cthird-key)%2CinfluencersFilterQuery%3A(bool%3A(minimum_should_match%3A1%2Cshould%3A!((match_phrase%3A(key%3Athird-key)))))%2CqueryString%3A'key%3A%22third-key%22')%2CmlExplorerSwimlane%3A()))", "kibana.alert.reason": "Alerts are raised based on real-time scores. Remember that scores may be adjusted over time as data continues to be analyzed.", "kibana.alert.job_id": "rt-anomaly-mean-value", "kibana.alert.anomaly_score": 72.75515452061356, "kibana.alert.is_interim": false, "kibana.alert.anomaly_timestamp": 1695913980000, "kibana.alert.top_records": [{ "job_id": "rt-anomaly-mean-value", "record_score": 72.75515452061356, "initial_record_score": 72.75515452061356, "detector_index": 0, "is_interim": false, "timestamp": 1695913980000, "partition_field_name": "key", "partition_field_value": "third-key", "function": "mean", "actual": [ 0.5 ], "typical": [ 4.138745343296527 ] }], "kibana.alert.top_influencers": [{ "job_id": "rt-anomaly-mean-value", "influencer_field_name": "key", "influencer_field_value": "third-key", "influencer_score": 72.75515452061356, "initial_influencer_score": 72.75515452061356, "is_interim": false, "timestamp": 1695913980000 }], "kibana.alert.rule.category": "Anomaly detection alert", "kibana.alert.rule.consumer": "alerts", "kibana.alert.rule.execution.uuid": "17fef3d3-d595-4362-837e-b2a73650169e", "kibana.alert.rule.name": "rt-ad-alert-influencer", "kibana.alert.rule.parameters": { "severity": 5, "resultType": "influencer", "includeInterim": false, "jobSelection": { "jobIds": [ "rt-anomaly-mean-value" ], "groupIds": [] }, "lookbackInterval": null, "topNBuckets": null }, "kibana.alert.rule.producer": "ml", "kibana.alert.rule.revision": 0, "kibana.alert.rule.rule_type_id": "xpack.ml.anomaly_detection_alert", "kibana.alert.rule.tags": [], "kibana.alert.rule.uuid": "757c7610-5e11-11ee-8bc6-a95c3ced4757", "kibana.space_ids": [ "default" ], "@timestamp": "2023-09-28T15:14:52.057Z", "event.action": "active", "event.kind": "signal", "kibana.alert.action_group": "anomaly_score_match", "kibana.alert.flapping": false, "kibana.alert.flapping_history": [ true, false, false, false ], "kibana.alert.instance.id": "rt-anomaly-mean-value", "kibana.alert.maintenance_window_ids": [], "kibana.alert.status": "active", "kibana.alert.uuid": "ac1f0d7c-461b-4fc6-b4c3-04416ac876d3", "kibana.alert.workflow_status": "open", "kibana.alert.duration.us": "99028000000", "kibana.alert.start": "2023-09-28T15:13:13.028Z", "kibana.alert.time_range": { "gte": "2023-09-28T15:13:13.028Z" }, "kibana.version": "8.11.0", "tags": [] } ```
--------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../schemas/create_schema_from_field_map.ts | 16 +- .../src/schemas/generated/alert_schema.ts | 96 +- .../src/schemas/generated/ecs_schema.ts | 2994 +++++++---------- .../schemas/generated/legacy_alert_schema.ts | 82 +- .../generated/ml_anomaly_detection_schema.ts | 90 +- .../generated/observability_apm_schema.ts | 68 +- .../generated/observability_logs_schema.ts | 38 +- .../generated/observability_metrics_schema.ts | 38 +- .../generated/observability_slo_schema.ts | 46 +- .../generated/observability_uptime_schema.ts | 94 +- .../src/schemas/generated/security_schema.ts | 406 +-- .../src/schemas/generated/stack_schema.ts | 26 +- .../alerting/server/alert/alert.test.ts | 1 + x-pack/plugins/alerting/server/alert/alert.ts | 11 +- .../alerts_client/alerts_client.test.ts | 1900 ++++------- .../server/alerts_client/alerts_client.ts | 39 +- .../lib/alert_conflict_resolver.test.ts | 21 +- .../lib/alert_conflict_resolver.ts | 7 +- .../alerts_client/lib/build_new_alert.test.ts | 351 +- .../alerts_client/lib/build_new_alert.ts | 84 +- .../lib/build_ongoing_alert.test.ts | 1107 +++--- .../alerts_client/lib/build_ongoing_alert.ts | 137 +- .../lib/build_recovered_alert.test.ts | 943 +++--- .../lib/build_recovered_alert.ts | 147 +- .../lib/build_updated_recovered_alert.test.ts | 154 +- .../lib/build_updated_recovered_alert.ts | 59 +- .../alerts_client/lib/format_alert.test.ts | 257 ++ .../server/alerts_client/lib/format_alert.ts | 96 + .../alerts_client/lib/format_rule.test.ts | 45 +- .../server/alerts_client/lib/format_rule.ts | 43 +- .../lib/get_summarized_alerts_query.ts | 23 +- .../server/alerts_client/lib/index.ts | 2 +- .../lib/strip_framework_fields.test.ts | 56 + .../server/alerts_client/lib/test_fixtures.ts | 118 + .../alerting/server/alerts_client/types.ts | 30 +- .../task_runner_alerts_client.test.ts | 101 +- .../register_anomaly_detection_alert_type.ts | 9 +- .../rule_types/es_query/executor.test.ts | 167 +- .../server/rule_types/es_query/executor.ts | 13 +- .../rule_types/es_query/rule_type.test.ts | 33 +- .../index_threshold/rule_type.test.ts | 11 +- .../rule_types/index_threshold/rule_type.ts | 13 +- .../ml_rule_types/anomaly_detection/alert.ts | 5 +- .../builtin_alert_types/es_query/esql_only.ts | 14 +- .../builtin_alert_types/es_query/rule.ts | 14 +- .../index_threshold/alert.ts | 16 +- .../group4/alerts_as_data/alerts_as_data.ts | 247 +- .../alerts_as_data_conflicts.ts | 26 +- .../alerts_as_data/alerts_as_data_flapping.ts | 43 +- .../common/alerting/alert_documents.ts | 137 +- .../common/alerting/summary_actions.ts | 247 +- 51 files changed, 5051 insertions(+), 5670 deletions(-) create mode 100644 x-pack/plugins/alerting/server/alerts_client/lib/format_alert.test.ts create mode 100644 x-pack/plugins/alerting/server/alerts_client/lib/format_alert.ts create mode 100644 x-pack/plugins/alerting/server/alerts_client/lib/test_fixtures.ts diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/create_schema_from_field_map.ts b/packages/kbn-alerts-as-data-utils/src/schemas/create_schema_from_field_map.ts index a0599d85fab33..b06bda3a36278 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/create_schema_from_field_map.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/create_schema_from_field_map.ts @@ -31,7 +31,7 @@ export const createSchemaFromFieldMap = ({ useAlert = false, useEcs = false, useLegacyAlerts = false, - flattened = false, + flattened = true, }: CreateSchemaFromFieldMapOpts) => { const lineWriters = { IMPORTS: createLineWriter(), @@ -280,19 +280,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/alert_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/alert_schema.ts index 4978d8b1fa1e4..ac143adf5f5d5 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/alert_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/alert_schema.ts @@ -25,19 +25,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; @@ -68,59 +68,39 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const AlertRequired = rt.type({ '@timestamp': schemaDate, - kibana: rt.type({ - alert: rt.type({ - instance: rt.type({ - id: schemaString, - }), - rule: rt.type({ - category: schemaString, - consumer: schemaString, - name: schemaString, - producer: schemaString, - revision: schemaStringOrNumber, - rule_type_id: schemaString, - uuid: schemaString, - }), - status: schemaString, - uuid: schemaString, - }), - space_ids: schemaStringArray, - }), + 'kibana.alert.instance.id': schemaString, + 'kibana.alert.rule.category': schemaString, + 'kibana.alert.rule.consumer': schemaString, + 'kibana.alert.rule.name': schemaString, + 'kibana.alert.rule.producer': schemaString, + 'kibana.alert.rule.revision': schemaStringOrNumber, + 'kibana.alert.rule.rule_type_id': schemaString, + 'kibana.alert.rule.uuid': schemaString, + 'kibana.alert.status': schemaString, + 'kibana.alert.uuid': schemaString, + 'kibana.space_ids': schemaStringArray, }); const AlertOptional = rt.partial({ - event: rt.partial({ - action: schemaString, - kind: schemaString, - }), - kibana: rt.partial({ - alert: rt.partial({ - action_group: schemaString, - case_ids: schemaStringArray, - duration: rt.partial({ - us: schemaStringOrNumber, - }), - end: schemaDate, - flapping: schemaBoolean, - flapping_history: schemaBooleanArray, - last_detected: schemaDate, - maintenance_window_ids: schemaStringArray, - reason: schemaString, - rule: rt.partial({ - execution: rt.partial({ - uuid: schemaString, - }), - parameters: schemaUnknown, - tags: schemaStringArray, - }), - start: schemaDate, - time_range: schemaDateRange, - url: schemaString, - workflow_status: schemaString, - workflow_tags: schemaStringArray, - }), - version: schemaString, - }), + 'event.action': schemaString, + 'event.kind': schemaString, + 'kibana.alert.action_group': schemaString, + 'kibana.alert.case_ids': schemaStringArray, + 'kibana.alert.duration.us': schemaStringOrNumber, + 'kibana.alert.end': schemaDate, + 'kibana.alert.flapping': schemaBoolean, + 'kibana.alert.flapping_history': schemaBooleanArray, + 'kibana.alert.last_detected': schemaDate, + 'kibana.alert.maintenance_window_ids': schemaStringArray, + 'kibana.alert.reason': schemaString, + 'kibana.alert.rule.execution.uuid': schemaString, + 'kibana.alert.rule.parameters': schemaUnknown, + 'kibana.alert.rule.tags': schemaStringArray, + 'kibana.alert.start': schemaDate, + 'kibana.alert.time_range': schemaDateRange, + 'kibana.alert.url': schemaString, + 'kibana.alert.workflow_status': schemaString, + 'kibana.alert.workflow_tags': schemaStringArray, + 'kibana.version': schemaString, tags: schemaStringArray, }); diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts index 8565f6838fc63..840fc2187321c 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts @@ -25,19 +25,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; @@ -68,1766 +68,1262 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const EcsRequired = rt.type({ '@timestamp': schemaDate, - ecs: rt.type({ - version: schemaString, - }), + 'ecs.version': schemaString, }); const EcsOptional = rt.partial({ - agent: rt.partial({ - build: rt.partial({ - original: schemaString, - }), - ephemeral_id: schemaString, - id: schemaString, - name: schemaString, - type: schemaString, - version: schemaString, - }), - client: rt.partial({ - address: schemaString, - as: rt.partial({ - number: schemaStringOrNumber, - organization: rt.partial({ - name: schemaString, - }), - }), - bytes: schemaStringOrNumber, - domain: schemaString, - geo: rt.partial({ - city_name: schemaString, - continent_code: schemaString, - continent_name: schemaString, - country_iso_code: schemaString, - country_name: schemaString, - location: schemaGeoPoint, - name: schemaString, - postal_code: schemaString, - region_iso_code: schemaString, - region_name: schemaString, - timezone: schemaString, - }), - ip: schemaString, - mac: schemaString, - nat: rt.partial({ - ip: schemaString, - port: schemaStringOrNumber, - }), - packets: schemaStringOrNumber, - port: schemaStringOrNumber, - registered_domain: schemaString, - subdomain: schemaString, - top_level_domain: schemaString, - user: rt.partial({ - domain: schemaString, - email: schemaString, - full_name: schemaString, - group: rt.partial({ - domain: schemaString, - id: schemaString, - name: schemaString, - }), - hash: schemaString, - id: schemaString, - name: schemaString, - roles: schemaStringArray, - }), - }), - cloud: rt.partial({ - account: rt.partial({ - id: schemaString, - name: schemaString, - }), - availability_zone: schemaString, - instance: rt.partial({ - id: schemaString, - name: schemaString, - }), - machine: rt.partial({ - type: schemaString, - }), - origin: rt.partial({ - account: rt.partial({ - id: schemaString, - name: schemaString, - }), - availability_zone: schemaString, - instance: rt.partial({ - id: schemaString, - name: schemaString, - }), - machine: rt.partial({ - type: schemaString, - }), - project: rt.partial({ - id: schemaString, - name: schemaString, - }), - provider: schemaString, - region: schemaString, - service: rt.partial({ - name: schemaString, - }), - }), - project: rt.partial({ - id: schemaString, - name: schemaString, - }), - provider: schemaString, - region: schemaString, - service: rt.partial({ - name: schemaString, - }), - target: rt.partial({ - account: rt.partial({ - id: schemaString, - name: schemaString, - }), - availability_zone: schemaString, - instance: rt.partial({ - id: schemaString, - name: schemaString, - }), - machine: rt.partial({ - type: schemaString, - }), - project: rt.partial({ - id: schemaString, - name: schemaString, - }), - provider: schemaString, - region: schemaString, - service: rt.partial({ - name: schemaString, - }), - }), - }), - container: rt.partial({ - cpu: rt.partial({ - usage: schemaStringOrNumber, - }), - disk: rt.partial({ - read: rt.partial({ - bytes: schemaStringOrNumber, - }), - write: rt.partial({ - bytes: schemaStringOrNumber, - }), - }), - id: schemaString, - image: rt.partial({ - hash: rt.partial({ - all: schemaStringArray, - }), - name: schemaString, - tag: schemaStringArray, - }), - memory: rt.partial({ - usage: schemaStringOrNumber, - }), - name: schemaString, - network: rt.partial({ - egress: rt.partial({ - bytes: schemaStringOrNumber, - }), - ingress: rt.partial({ - bytes: schemaStringOrNumber, - }), - }), - runtime: schemaString, - }), - destination: rt.partial({ - address: schemaString, - as: rt.partial({ - number: schemaStringOrNumber, - organization: rt.partial({ - name: schemaString, - }), - }), - bytes: schemaStringOrNumber, - domain: schemaString, - geo: rt.partial({ - city_name: schemaString, - continent_code: schemaString, - continent_name: schemaString, - country_iso_code: schemaString, - country_name: schemaString, - location: schemaGeoPoint, - name: schemaString, - postal_code: schemaString, - region_iso_code: schemaString, - region_name: schemaString, - timezone: schemaString, - }), - ip: schemaString, - mac: schemaString, - nat: rt.partial({ - ip: schemaString, - port: schemaStringOrNumber, - }), - packets: schemaStringOrNumber, - port: schemaStringOrNumber, - registered_domain: schemaString, - subdomain: schemaString, - top_level_domain: schemaString, - user: rt.partial({ - domain: schemaString, - email: schemaString, - full_name: schemaString, - group: rt.partial({ - domain: schemaString, - id: schemaString, - name: schemaString, - }), - hash: schemaString, - id: schemaString, - name: schemaString, - roles: schemaStringArray, - }), - }), - device: rt.partial({ - id: schemaString, - manufacturer: schemaString, - model: rt.partial({ - identifier: schemaString, - name: schemaString, - }), - }), - dll: rt.partial({ - code_signature: rt.partial({ - digest_algorithm: schemaString, - exists: schemaBoolean, - signing_id: schemaString, - status: schemaString, - subject_name: schemaString, - team_id: schemaString, - timestamp: schemaDate, - trusted: schemaBoolean, - valid: schemaBoolean, - }), - hash: rt.partial({ - md5: schemaString, - sha1: schemaString, - sha256: schemaString, - sha384: schemaString, - sha512: schemaString, - ssdeep: schemaString, - tlsh: schemaString, - }), - name: schemaString, - path: schemaString, - pe: rt.partial({ - architecture: schemaString, - company: schemaString, - description: schemaString, - file_version: schemaString, - imphash: schemaString, - original_file_name: schemaString, - pehash: schemaString, - product: schemaString, - }), - }), - dns: rt.partial({ - answers: rt.array( - rt.partial({ - class: schemaString, - data: schemaString, - name: schemaString, - ttl: schemaStringOrNumber, - type: schemaString, - }) - ), - header_flags: schemaStringArray, - id: schemaString, - op_code: schemaString, - question: rt.partial({ + 'agent.build.original': schemaString, + 'agent.ephemeral_id': schemaString, + 'agent.id': schemaString, + 'agent.name': schemaString, + 'agent.type': schemaString, + 'agent.version': schemaString, + 'client.address': schemaString, + 'client.as.number': schemaStringOrNumber, + 'client.as.organization.name': schemaString, + 'client.bytes': schemaStringOrNumber, + 'client.domain': schemaString, + 'client.geo.city_name': schemaString, + 'client.geo.continent_code': schemaString, + 'client.geo.continent_name': schemaString, + 'client.geo.country_iso_code': schemaString, + 'client.geo.country_name': schemaString, + 'client.geo.location': schemaGeoPoint, + 'client.geo.name': schemaString, + 'client.geo.postal_code': schemaString, + 'client.geo.region_iso_code': schemaString, + 'client.geo.region_name': schemaString, + 'client.geo.timezone': schemaString, + 'client.ip': schemaString, + 'client.mac': schemaString, + 'client.nat.ip': schemaString, + 'client.nat.port': schemaStringOrNumber, + 'client.packets': schemaStringOrNumber, + 'client.port': schemaStringOrNumber, + 'client.registered_domain': schemaString, + 'client.subdomain': schemaString, + 'client.top_level_domain': schemaString, + 'client.user.domain': schemaString, + 'client.user.email': schemaString, + 'client.user.full_name': schemaString, + 'client.user.group.domain': schemaString, + 'client.user.group.id': schemaString, + 'client.user.group.name': schemaString, + 'client.user.hash': schemaString, + 'client.user.id': schemaString, + 'client.user.name': schemaString, + 'client.user.roles': schemaStringArray, + 'cloud.account.id': schemaString, + 'cloud.account.name': schemaString, + 'cloud.availability_zone': schemaString, + 'cloud.instance.id': schemaString, + 'cloud.instance.name': schemaString, + 'cloud.machine.type': schemaString, + 'cloud.origin.account.id': schemaString, + 'cloud.origin.account.name': schemaString, + 'cloud.origin.availability_zone': schemaString, + 'cloud.origin.instance.id': schemaString, + 'cloud.origin.instance.name': schemaString, + 'cloud.origin.machine.type': schemaString, + 'cloud.origin.project.id': schemaString, + 'cloud.origin.project.name': schemaString, + 'cloud.origin.provider': schemaString, + 'cloud.origin.region': schemaString, + 'cloud.origin.service.name': schemaString, + 'cloud.project.id': schemaString, + 'cloud.project.name': schemaString, + 'cloud.provider': schemaString, + 'cloud.region': schemaString, + 'cloud.service.name': schemaString, + 'cloud.target.account.id': schemaString, + 'cloud.target.account.name': schemaString, + 'cloud.target.availability_zone': schemaString, + 'cloud.target.instance.id': schemaString, + 'cloud.target.instance.name': schemaString, + 'cloud.target.machine.type': schemaString, + 'cloud.target.project.id': schemaString, + 'cloud.target.project.name': schemaString, + 'cloud.target.provider': schemaString, + 'cloud.target.region': schemaString, + 'cloud.target.service.name': schemaString, + 'container.cpu.usage': schemaStringOrNumber, + 'container.disk.read.bytes': schemaStringOrNumber, + 'container.disk.write.bytes': schemaStringOrNumber, + 'container.id': schemaString, + 'container.image.hash.all': schemaStringArray, + 'container.image.name': schemaString, + 'container.image.tag': schemaStringArray, + 'container.memory.usage': schemaStringOrNumber, + 'container.name': schemaString, + 'container.network.egress.bytes': schemaStringOrNumber, + 'container.network.ingress.bytes': schemaStringOrNumber, + 'container.runtime': schemaString, + 'destination.address': schemaString, + 'destination.as.number': schemaStringOrNumber, + 'destination.as.organization.name': schemaString, + 'destination.bytes': schemaStringOrNumber, + 'destination.domain': schemaString, + 'destination.geo.city_name': schemaString, + 'destination.geo.continent_code': schemaString, + 'destination.geo.continent_name': schemaString, + 'destination.geo.country_iso_code': schemaString, + 'destination.geo.country_name': schemaString, + 'destination.geo.location': schemaGeoPoint, + 'destination.geo.name': schemaString, + 'destination.geo.postal_code': schemaString, + 'destination.geo.region_iso_code': schemaString, + 'destination.geo.region_name': schemaString, + 'destination.geo.timezone': schemaString, + 'destination.ip': schemaString, + 'destination.mac': schemaString, + 'destination.nat.ip': schemaString, + 'destination.nat.port': schemaStringOrNumber, + 'destination.packets': schemaStringOrNumber, + 'destination.port': schemaStringOrNumber, + 'destination.registered_domain': schemaString, + 'destination.subdomain': schemaString, + 'destination.top_level_domain': schemaString, + 'destination.user.domain': schemaString, + 'destination.user.email': schemaString, + 'destination.user.full_name': schemaString, + 'destination.user.group.domain': schemaString, + 'destination.user.group.id': schemaString, + 'destination.user.group.name': schemaString, + 'destination.user.hash': schemaString, + 'destination.user.id': schemaString, + 'destination.user.name': schemaString, + 'destination.user.roles': schemaStringArray, + 'device.id': schemaString, + 'device.manufacturer': schemaString, + 'device.model.identifier': schemaString, + 'device.model.name': schemaString, + 'dll.code_signature.digest_algorithm': schemaString, + 'dll.code_signature.exists': schemaBoolean, + 'dll.code_signature.signing_id': schemaString, + 'dll.code_signature.status': schemaString, + 'dll.code_signature.subject_name': schemaString, + 'dll.code_signature.team_id': schemaString, + 'dll.code_signature.timestamp': schemaDate, + 'dll.code_signature.trusted': schemaBoolean, + 'dll.code_signature.valid': schemaBoolean, + 'dll.hash.md5': schemaString, + 'dll.hash.sha1': schemaString, + 'dll.hash.sha256': schemaString, + 'dll.hash.sha384': schemaString, + 'dll.hash.sha512': schemaString, + 'dll.hash.ssdeep': schemaString, + 'dll.hash.tlsh': schemaString, + 'dll.name': schemaString, + 'dll.path': schemaString, + 'dll.pe.architecture': schemaString, + 'dll.pe.company': schemaString, + 'dll.pe.description': schemaString, + 'dll.pe.file_version': schemaString, + 'dll.pe.imphash': schemaString, + 'dll.pe.original_file_name': schemaString, + 'dll.pe.pehash': schemaString, + 'dll.pe.product': schemaString, + 'dns.answers': rt.array( + rt.partial({ class: schemaString, + data: schemaString, name: schemaString, - registered_domain: schemaString, - subdomain: schemaString, - top_level_domain: schemaString, + ttl: schemaStringOrNumber, type: schemaString, - }), - resolved_ip: schemaStringArray, - response_code: schemaString, - type: schemaString, - }), - email: rt.partial({ - attachments: rt.array( - rt.partial({ - file: rt.partial({ - extension: schemaString, - hash: rt.partial({ - md5: schemaString, - sha1: schemaString, - sha256: schemaString, - sha384: schemaString, - sha512: schemaString, - ssdeep: schemaString, - tlsh: schemaString, - }), - mime_type: schemaString, - name: schemaString, - size: schemaStringOrNumber, - }), - }) - ), - bcc: rt.partial({ - address: schemaStringArray, - }), - cc: rt.partial({ - address: schemaStringArray, - }), - content_type: schemaString, - delivery_timestamp: schemaDate, - direction: schemaString, - from: rt.partial({ - address: schemaStringArray, - }), - local_id: schemaString, - message_id: schemaString, - origination_timestamp: schemaDate, - reply_to: rt.partial({ - address: schemaStringArray, - }), - sender: rt.partial({ - address: schemaString, - }), - subject: schemaString, - to: rt.partial({ - address: schemaStringArray, - }), - x_mailer: schemaString, - }), - error: rt.partial({ - code: schemaString, - id: schemaString, - message: schemaString, - stack_trace: schemaString, - type: schemaString, - }), - event: rt.partial({ - action: schemaString, - agent_id_status: schemaString, - category: schemaStringArray, - code: schemaString, - created: schemaDate, - dataset: schemaString, - duration: schemaStringOrNumber, - end: schemaDate, - hash: schemaString, - id: schemaString, - ingested: schemaDate, - kind: schemaString, - module: schemaString, - original: schemaString, - outcome: schemaString, - provider: schemaString, - reason: schemaString, - reference: schemaString, - risk_score: schemaNumber, - risk_score_norm: schemaNumber, - sequence: schemaStringOrNumber, - severity: schemaStringOrNumber, - start: schemaDate, - timezone: schemaString, - type: schemaStringArray, - url: schemaString, - }), - faas: rt.partial({ - coldstart: schemaBoolean, - execution: schemaString, - id: schemaString, - name: schemaString, - version: schemaString, - }), - file: rt.partial({ - accessed: schemaDate, - attributes: schemaStringArray, - code_signature: rt.partial({ - digest_algorithm: schemaString, - exists: schemaBoolean, - signing_id: schemaString, - status: schemaString, - subject_name: schemaString, - team_id: schemaString, - timestamp: schemaDate, - trusted: schemaBoolean, - valid: schemaBoolean, - }), - created: schemaDate, - ctime: schemaDate, - device: schemaString, - directory: schemaString, - drive_letter: schemaString, - elf: rt.partial({ - architecture: schemaString, - byte_order: schemaString, - cpu_type: schemaString, - creation_date: schemaDate, - exports: schemaUnknownArray, - header: rt.partial({ - abi_version: schemaString, - class: schemaString, - data: schemaString, - entrypoint: schemaStringOrNumber, - object_version: schemaString, - os_abi: schemaString, - type: schemaString, - version: schemaString, - }), - imports: schemaUnknownArray, - sections: rt.array( - rt.partial({ - chi2: schemaStringOrNumber, - entropy: schemaStringOrNumber, - flags: schemaString, - name: schemaString, - physical_offset: schemaString, - physical_size: schemaStringOrNumber, - type: schemaString, - virtual_address: schemaStringOrNumber, - virtual_size: schemaStringOrNumber, - }) - ), - segments: rt.array( - rt.partial({ - sections: schemaString, - type: schemaString, - }) - ), - shared_libraries: schemaStringArray, - telfhash: schemaString, - }), - extension: schemaString, - fork_name: schemaString, - gid: schemaString, - group: schemaString, - hash: rt.partial({ - md5: schemaString, - sha1: schemaString, - sha256: schemaString, - sha384: schemaString, - sha512: schemaString, - ssdeep: schemaString, - tlsh: schemaString, - }), - inode: schemaString, - mime_type: schemaString, - mode: schemaString, - mtime: schemaDate, - name: schemaString, - owner: schemaString, - path: schemaString, - pe: rt.partial({ - architecture: schemaString, - company: schemaString, - description: schemaString, - file_version: schemaString, - imphash: schemaString, - original_file_name: schemaString, - pehash: schemaString, - product: schemaString, - }), - size: schemaStringOrNumber, - target_path: schemaString, - type: schemaString, - uid: schemaString, - x509: rt.partial({ - alternative_names: schemaStringArray, - issuer: rt.partial({ - common_name: schemaStringArray, - country: schemaStringArray, - distinguished_name: schemaString, - locality: schemaStringArray, - organization: schemaStringArray, - organizational_unit: schemaStringArray, - state_or_province: schemaStringArray, - }), - not_after: schemaDate, - not_before: schemaDate, - public_key_algorithm: schemaString, - public_key_curve: schemaString, - public_key_exponent: schemaStringOrNumber, - public_key_size: schemaStringOrNumber, - serial_number: schemaString, - signature_algorithm: schemaString, - subject: rt.partial({ - common_name: schemaStringArray, - country: schemaStringArray, - distinguished_name: schemaString, - locality: schemaStringArray, - organization: schemaStringArray, - organizational_unit: schemaStringArray, - state_or_province: schemaStringArray, - }), - version_number: schemaString, - }), - }), - group: rt.partial({ - domain: schemaString, - id: schemaString, - name: schemaString, - }), - host: rt.partial({ - architecture: schemaString, - boot: rt.partial({ - id: schemaString, - }), - cpu: rt.partial({ - usage: schemaStringOrNumber, - }), - disk: rt.partial({ - read: rt.partial({ - bytes: schemaStringOrNumber, - }), - write: rt.partial({ - bytes: schemaStringOrNumber, - }), - }), - domain: schemaString, - geo: rt.partial({ - city_name: schemaString, - continent_code: schemaString, - continent_name: schemaString, - country_iso_code: schemaString, - country_name: schemaString, - location: schemaGeoPoint, + }) + ), + 'dns.header_flags': schemaStringArray, + 'dns.id': schemaString, + 'dns.op_code': schemaString, + 'dns.question.class': schemaString, + 'dns.question.name': schemaString, + 'dns.question.registered_domain': schemaString, + 'dns.question.subdomain': schemaString, + 'dns.question.top_level_domain': schemaString, + 'dns.question.type': schemaString, + 'dns.resolved_ip': schemaStringArray, + 'dns.response_code': schemaString, + 'dns.type': schemaString, + 'email.attachments': rt.array( + rt.partial({ + 'file.extension': schemaString, + 'file.hash.md5': schemaString, + 'file.hash.sha1': schemaString, + 'file.hash.sha256': schemaString, + 'file.hash.sha384': schemaString, + 'file.hash.sha512': schemaString, + 'file.hash.ssdeep': schemaString, + 'file.hash.tlsh': schemaString, + 'file.mime_type': schemaString, + 'file.name': schemaString, + 'file.size': schemaStringOrNumber, + }) + ), + 'email.bcc.address': schemaStringArray, + 'email.cc.address': schemaStringArray, + 'email.content_type': schemaString, + 'email.delivery_timestamp': schemaDate, + 'email.direction': schemaString, + 'email.from.address': schemaStringArray, + 'email.local_id': schemaString, + 'email.message_id': schemaString, + 'email.origination_timestamp': schemaDate, + 'email.reply_to.address': schemaStringArray, + 'email.sender.address': schemaString, + 'email.subject': schemaString, + 'email.to.address': schemaStringArray, + 'email.x_mailer': schemaString, + 'error.code': schemaString, + 'error.id': schemaString, + 'error.message': schemaString, + 'error.stack_trace': schemaString, + 'error.type': schemaString, + 'event.action': schemaString, + 'event.agent_id_status': schemaString, + 'event.category': schemaStringArray, + 'event.code': schemaString, + 'event.created': schemaDate, + 'event.dataset': schemaString, + 'event.duration': schemaStringOrNumber, + 'event.end': schemaDate, + 'event.hash': schemaString, + 'event.id': schemaString, + 'event.ingested': schemaDate, + 'event.kind': schemaString, + 'event.module': schemaString, + 'event.original': schemaString, + 'event.outcome': schemaString, + 'event.provider': schemaString, + 'event.reason': schemaString, + 'event.reference': schemaString, + 'event.risk_score': schemaNumber, + 'event.risk_score_norm': schemaNumber, + 'event.sequence': schemaStringOrNumber, + 'event.severity': schemaStringOrNumber, + 'event.start': schemaDate, + 'event.timezone': schemaString, + 'event.type': schemaStringArray, + 'event.url': schemaString, + 'faas.coldstart': schemaBoolean, + 'faas.execution': schemaString, + 'faas.id': schemaString, + 'faas.name': schemaString, + 'faas.version': schemaString, + 'file.accessed': schemaDate, + 'file.attributes': schemaStringArray, + 'file.code_signature.digest_algorithm': schemaString, + 'file.code_signature.exists': schemaBoolean, + 'file.code_signature.signing_id': schemaString, + 'file.code_signature.status': schemaString, + 'file.code_signature.subject_name': schemaString, + 'file.code_signature.team_id': schemaString, + 'file.code_signature.timestamp': schemaDate, + 'file.code_signature.trusted': schemaBoolean, + 'file.code_signature.valid': schemaBoolean, + 'file.created': schemaDate, + 'file.ctime': schemaDate, + 'file.device': schemaString, + 'file.directory': schemaString, + 'file.drive_letter': schemaString, + 'file.elf.architecture': schemaString, + 'file.elf.byte_order': schemaString, + 'file.elf.cpu_type': schemaString, + 'file.elf.creation_date': schemaDate, + 'file.elf.exports': schemaUnknownArray, + 'file.elf.header.abi_version': schemaString, + 'file.elf.header.class': schemaString, + 'file.elf.header.data': schemaString, + 'file.elf.header.entrypoint': schemaStringOrNumber, + 'file.elf.header.object_version': schemaString, + 'file.elf.header.os_abi': schemaString, + 'file.elf.header.type': schemaString, + 'file.elf.header.version': schemaString, + 'file.elf.imports': schemaUnknownArray, + 'file.elf.sections': rt.array( + rt.partial({ + chi2: schemaStringOrNumber, + entropy: schemaStringOrNumber, + flags: schemaString, name: schemaString, - postal_code: schemaString, - region_iso_code: schemaString, - region_name: schemaString, - timezone: schemaString, - }), - hostname: schemaString, - id: schemaString, - ip: schemaStringArray, - mac: schemaStringArray, - name: schemaString, - network: rt.partial({ - egress: rt.partial({ - bytes: schemaStringOrNumber, - packets: schemaStringOrNumber, - }), - ingress: rt.partial({ - bytes: schemaStringOrNumber, - packets: schemaStringOrNumber, - }), - }), - os: rt.partial({ - family: schemaString, - full: schemaString, - kernel: schemaString, - name: schemaString, - platform: schemaString, + physical_offset: schemaString, + physical_size: schemaStringOrNumber, type: schemaString, - version: schemaString, - }), - pid_ns_ino: schemaString, - risk: rt.partial({ - calculated_level: schemaString, - calculated_score: schemaNumber, - calculated_score_norm: schemaNumber, - static_level: schemaString, - static_score: schemaNumber, - static_score_norm: schemaNumber, - }), - type: schemaString, - uptime: schemaStringOrNumber, - }), - http: rt.partial({ - request: rt.partial({ - body: rt.partial({ - bytes: schemaStringOrNumber, - content: schemaString, - }), - bytes: schemaStringOrNumber, - id: schemaString, - method: schemaString, - mime_type: schemaString, - referrer: schemaString, - }), - response: rt.partial({ - body: rt.partial({ - bytes: schemaStringOrNumber, - content: schemaString, - }), - bytes: schemaStringOrNumber, - mime_type: schemaString, - status_code: schemaStringOrNumber, - }), - version: schemaString, - }), - log: rt.partial({ - file: rt.partial({ - path: schemaString, - }), - level: schemaString, - logger: schemaString, - origin: rt.partial({ - file: rt.partial({ - line: schemaStringOrNumber, - name: schemaString, - }), - function: schemaString, - }), - }), - message: schemaString, - network: rt.partial({ - application: schemaString, - bytes: schemaStringOrNumber, - community_id: schemaString, - direction: schemaString, - forwarded_ip: schemaString, - iana_number: schemaString, - name: schemaString, - packets: schemaStringOrNumber, - protocol: schemaString, - transport: schemaString, - type: schemaString, - vlan: rt.partial({ - id: schemaString, - name: schemaString, - }), - }), - observer: rt.partial({ - geo: rt.partial({ - city_name: schemaString, - continent_code: schemaString, - continent_name: schemaString, - country_iso_code: schemaString, - country_name: schemaString, - location: schemaGeoPoint, - name: schemaString, - postal_code: schemaString, - region_iso_code: schemaString, - region_name: schemaString, - timezone: schemaString, - }), - hostname: schemaString, - ip: schemaStringArray, - mac: schemaStringArray, - name: schemaString, - os: rt.partial({ - family: schemaString, - full: schemaString, - kernel: schemaString, - name: schemaString, - platform: schemaString, + virtual_address: schemaStringOrNumber, + virtual_size: schemaStringOrNumber, + }) + ), + 'file.elf.segments': rt.array( + rt.partial({ + sections: schemaString, type: schemaString, - version: schemaString, - }), - product: schemaString, - serial_number: schemaString, - type: schemaString, - vendor: schemaString, - version: schemaString, - }), - orchestrator: rt.partial({ - api_version: schemaString, - cluster: rt.partial({ - id: schemaString, - name: schemaString, - url: schemaString, - version: schemaString, - }), - namespace: schemaString, - organization: schemaString, - resource: rt.partial({ - id: schemaString, - ip: schemaStringArray, + }) + ), + 'file.elf.shared_libraries': schemaStringArray, + 'file.elf.telfhash': schemaString, + 'file.extension': schemaString, + 'file.fork_name': schemaString, + 'file.gid': schemaString, + 'file.group': schemaString, + 'file.hash.md5': schemaString, + 'file.hash.sha1': schemaString, + 'file.hash.sha256': schemaString, + 'file.hash.sha384': schemaString, + 'file.hash.sha512': schemaString, + 'file.hash.ssdeep': schemaString, + 'file.hash.tlsh': schemaString, + 'file.inode': schemaString, + 'file.mime_type': schemaString, + 'file.mode': schemaString, + 'file.mtime': schemaDate, + 'file.name': schemaString, + 'file.owner': schemaString, + 'file.path': schemaString, + 'file.pe.architecture': schemaString, + 'file.pe.company': schemaString, + 'file.pe.description': schemaString, + 'file.pe.file_version': schemaString, + 'file.pe.imphash': schemaString, + 'file.pe.original_file_name': schemaString, + 'file.pe.pehash': schemaString, + 'file.pe.product': schemaString, + 'file.size': schemaStringOrNumber, + 'file.target_path': schemaString, + 'file.type': schemaString, + 'file.uid': schemaString, + 'file.x509.alternative_names': schemaStringArray, + 'file.x509.issuer.common_name': schemaStringArray, + 'file.x509.issuer.country': schemaStringArray, + 'file.x509.issuer.distinguished_name': schemaString, + 'file.x509.issuer.locality': schemaStringArray, + 'file.x509.issuer.organization': schemaStringArray, + 'file.x509.issuer.organizational_unit': schemaStringArray, + 'file.x509.issuer.state_or_province': schemaStringArray, + 'file.x509.not_after': schemaDate, + 'file.x509.not_before': schemaDate, + 'file.x509.public_key_algorithm': schemaString, + 'file.x509.public_key_curve': schemaString, + 'file.x509.public_key_exponent': schemaStringOrNumber, + 'file.x509.public_key_size': schemaStringOrNumber, + 'file.x509.serial_number': schemaString, + 'file.x509.signature_algorithm': schemaString, + 'file.x509.subject.common_name': schemaStringArray, + 'file.x509.subject.country': schemaStringArray, + 'file.x509.subject.distinguished_name': schemaString, + 'file.x509.subject.locality': schemaStringArray, + 'file.x509.subject.organization': schemaStringArray, + 'file.x509.subject.organizational_unit': schemaStringArray, + 'file.x509.subject.state_or_province': schemaStringArray, + 'file.x509.version_number': schemaString, + 'group.domain': schemaString, + 'group.id': schemaString, + 'group.name': schemaString, + 'host.architecture': schemaString, + 'host.boot.id': schemaString, + 'host.cpu.usage': schemaStringOrNumber, + 'host.disk.read.bytes': schemaStringOrNumber, + 'host.disk.write.bytes': schemaStringOrNumber, + 'host.domain': schemaString, + 'host.geo.city_name': schemaString, + 'host.geo.continent_code': schemaString, + 'host.geo.continent_name': schemaString, + 'host.geo.country_iso_code': schemaString, + 'host.geo.country_name': schemaString, + 'host.geo.location': schemaGeoPoint, + 'host.geo.name': schemaString, + 'host.geo.postal_code': schemaString, + 'host.geo.region_iso_code': schemaString, + 'host.geo.region_name': schemaString, + 'host.geo.timezone': schemaString, + 'host.hostname': schemaString, + 'host.id': schemaString, + 'host.ip': schemaStringArray, + 'host.mac': schemaStringArray, + 'host.name': schemaString, + 'host.network.egress.bytes': schemaStringOrNumber, + 'host.network.egress.packets': schemaStringOrNumber, + 'host.network.ingress.bytes': schemaStringOrNumber, + 'host.network.ingress.packets': schemaStringOrNumber, + 'host.os.family': schemaString, + 'host.os.full': schemaString, + 'host.os.kernel': schemaString, + 'host.os.name': schemaString, + 'host.os.platform': schemaString, + 'host.os.type': schemaString, + 'host.os.version': schemaString, + 'host.pid_ns_ino': schemaString, + 'host.risk.calculated_level': schemaString, + 'host.risk.calculated_score': schemaNumber, + 'host.risk.calculated_score_norm': schemaNumber, + 'host.risk.static_level': schemaString, + 'host.risk.static_score': schemaNumber, + 'host.risk.static_score_norm': schemaNumber, + 'host.type': schemaString, + 'host.uptime': schemaStringOrNumber, + 'http.request.body.bytes': schemaStringOrNumber, + 'http.request.body.content': schemaString, + 'http.request.bytes': schemaStringOrNumber, + 'http.request.id': schemaString, + 'http.request.method': schemaString, + 'http.request.mime_type': schemaString, + 'http.request.referrer': schemaString, + 'http.response.body.bytes': schemaStringOrNumber, + 'http.response.body.content': schemaString, + 'http.response.bytes': schemaStringOrNumber, + 'http.response.mime_type': schemaString, + 'http.response.status_code': schemaStringOrNumber, + 'http.version': schemaString, + 'log.file.path': schemaString, + 'log.level': schemaString, + 'log.logger': schemaString, + 'log.origin.file.line': schemaStringOrNumber, + 'log.origin.file.name': schemaString, + 'log.origin.function': schemaString, + message: schemaString, + 'network.application': schemaString, + 'network.bytes': schemaStringOrNumber, + 'network.community_id': schemaString, + 'network.direction': schemaString, + 'network.forwarded_ip': schemaString, + 'network.iana_number': schemaString, + 'network.name': schemaString, + 'network.packets': schemaStringOrNumber, + 'network.protocol': schemaString, + 'network.transport': schemaString, + 'network.type': schemaString, + 'network.vlan.id': schemaString, + 'network.vlan.name': schemaString, + 'observer.geo.city_name': schemaString, + 'observer.geo.continent_code': schemaString, + 'observer.geo.continent_name': schemaString, + 'observer.geo.country_iso_code': schemaString, + 'observer.geo.country_name': schemaString, + 'observer.geo.location': schemaGeoPoint, + 'observer.geo.name': schemaString, + 'observer.geo.postal_code': schemaString, + 'observer.geo.region_iso_code': schemaString, + 'observer.geo.region_name': schemaString, + 'observer.geo.timezone': schemaString, + 'observer.hostname': schemaString, + 'observer.ip': schemaStringArray, + 'observer.mac': schemaStringArray, + 'observer.name': schemaString, + 'observer.os.family': schemaString, + 'observer.os.full': schemaString, + 'observer.os.kernel': schemaString, + 'observer.os.name': schemaString, + 'observer.os.platform': schemaString, + 'observer.os.type': schemaString, + 'observer.os.version': schemaString, + 'observer.product': schemaString, + 'observer.serial_number': schemaString, + 'observer.type': schemaString, + 'observer.vendor': schemaString, + 'observer.version': schemaString, + 'orchestrator.api_version': schemaString, + 'orchestrator.cluster.id': schemaString, + 'orchestrator.cluster.name': schemaString, + 'orchestrator.cluster.url': schemaString, + 'orchestrator.cluster.version': schemaString, + 'orchestrator.namespace': schemaString, + 'orchestrator.organization': schemaString, + 'orchestrator.resource.id': schemaString, + 'orchestrator.resource.ip': schemaStringArray, + 'orchestrator.resource.name': schemaString, + 'orchestrator.resource.parent.type': schemaString, + 'orchestrator.resource.type': schemaString, + 'orchestrator.type': schemaString, + 'organization.id': schemaString, + 'organization.name': schemaString, + 'package.architecture': schemaString, + 'package.build_version': schemaString, + 'package.checksum': schemaString, + 'package.description': schemaString, + 'package.install_scope': schemaString, + 'package.installed': schemaDate, + 'package.license': schemaString, + 'package.name': schemaString, + 'package.path': schemaString, + 'package.reference': schemaString, + 'package.size': schemaStringOrNumber, + 'package.type': schemaString, + 'package.version': schemaString, + 'process.args': schemaStringArray, + 'process.args_count': schemaStringOrNumber, + 'process.code_signature.digest_algorithm': schemaString, + 'process.code_signature.exists': schemaBoolean, + 'process.code_signature.signing_id': schemaString, + 'process.code_signature.status': schemaString, + 'process.code_signature.subject_name': schemaString, + 'process.code_signature.team_id': schemaString, + 'process.code_signature.timestamp': schemaDate, + 'process.code_signature.trusted': schemaBoolean, + 'process.code_signature.valid': schemaBoolean, + 'process.command_line': schemaString, + 'process.elf.architecture': schemaString, + 'process.elf.byte_order': schemaString, + 'process.elf.cpu_type': schemaString, + 'process.elf.creation_date': schemaDate, + 'process.elf.exports': schemaUnknownArray, + 'process.elf.header.abi_version': schemaString, + 'process.elf.header.class': schemaString, + 'process.elf.header.data': schemaString, + 'process.elf.header.entrypoint': schemaStringOrNumber, + 'process.elf.header.object_version': schemaString, + 'process.elf.header.os_abi': schemaString, + 'process.elf.header.type': schemaString, + 'process.elf.header.version': schemaString, + 'process.elf.imports': schemaUnknownArray, + 'process.elf.sections': rt.array( + rt.partial({ + chi2: schemaStringOrNumber, + entropy: schemaStringOrNumber, + flags: schemaString, name: schemaString, - parent: rt.partial({ - type: schemaString, - }), + physical_offset: schemaString, + physical_size: schemaStringOrNumber, type: schemaString, - }), - type: schemaString, - }), - organization: rt.partial({ - id: schemaString, - name: schemaString, - }), - package: rt.partial({ - architecture: schemaString, - build_version: schemaString, - checksum: schemaString, - description: schemaString, - install_scope: schemaString, - installed: schemaDate, - license: schemaString, - name: schemaString, - path: schemaString, - reference: schemaString, - size: schemaStringOrNumber, - type: schemaString, - version: schemaString, - }), - process: rt.partial({ - args: schemaStringArray, - args_count: schemaStringOrNumber, - code_signature: rt.partial({ - digest_algorithm: schemaString, - exists: schemaBoolean, - signing_id: schemaString, - status: schemaString, - subject_name: schemaString, - team_id: schemaString, - timestamp: schemaDate, - trusted: schemaBoolean, - valid: schemaBoolean, - }), - command_line: schemaString, - elf: rt.partial({ - architecture: schemaString, - byte_order: schemaString, - cpu_type: schemaString, - creation_date: schemaDate, - exports: schemaUnknownArray, - header: rt.partial({ - abi_version: schemaString, - class: schemaString, - data: schemaString, - entrypoint: schemaStringOrNumber, - object_version: schemaString, - os_abi: schemaString, - type: schemaString, - version: schemaString, - }), - imports: schemaUnknownArray, - sections: rt.array( - rt.partial({ - chi2: schemaStringOrNumber, - entropy: schemaStringOrNumber, - flags: schemaString, - name: schemaString, - physical_offset: schemaString, - physical_size: schemaStringOrNumber, - type: schemaString, - virtual_address: schemaStringOrNumber, - virtual_size: schemaStringOrNumber, - }) - ), - segments: rt.array( - rt.partial({ - sections: schemaString, - type: schemaString, - }) - ), - shared_libraries: schemaStringArray, - telfhash: schemaString, - }), - end: schemaDate, - entity_id: schemaString, - entry_leader: rt.partial({ - args: schemaStringArray, - args_count: schemaStringOrNumber, - attested_groups: rt.partial({ - name: schemaString, - }), - attested_user: rt.partial({ - id: schemaString, - name: schemaString, - }), - command_line: schemaString, - entity_id: schemaString, - entry_meta: rt.partial({ - source: rt.partial({ - ip: schemaString, - }), - type: schemaString, - }), - executable: schemaString, - group: rt.partial({ - id: schemaString, - name: schemaString, - }), - interactive: schemaBoolean, - name: schemaString, - parent: rt.partial({ - entity_id: schemaString, - pid: schemaStringOrNumber, - session_leader: rt.partial({ - entity_id: schemaString, - pid: schemaStringOrNumber, - start: schemaDate, - }), - start: schemaDate, - }), - pid: schemaStringOrNumber, - real_group: rt.partial({ - id: schemaString, - name: schemaString, - }), - real_user: rt.partial({ - id: schemaString, - name: schemaString, - }), - same_as_process: schemaBoolean, - saved_group: rt.partial({ - id: schemaString, - name: schemaString, - }), - saved_user: rt.partial({ - id: schemaString, - name: schemaString, - }), - start: schemaDate, - supplemental_groups: rt.partial({ - id: schemaString, - name: schemaString, - }), - user: rt.partial({ - id: schemaString, - name: schemaString, - }), - working_directory: schemaString, - }), - env_vars: schemaStringArray, - executable: schemaString, - exit_code: schemaStringOrNumber, - group_leader: rt.partial({ - args: schemaStringArray, - args_count: schemaStringOrNumber, - command_line: schemaString, - entity_id: schemaString, - executable: schemaString, - group: rt.partial({ - id: schemaString, - name: schemaString, - }), - interactive: schemaBoolean, - name: schemaString, - pid: schemaStringOrNumber, - real_group: rt.partial({ - id: schemaString, - name: schemaString, - }), - real_user: rt.partial({ - id: schemaString, - name: schemaString, - }), - same_as_process: schemaBoolean, - saved_group: rt.partial({ - id: schemaString, - name: schemaString, - }), - saved_user: rt.partial({ - id: schemaString, - name: schemaString, - }), - start: schemaDate, - supplemental_groups: rt.partial({ - id: schemaString, - name: schemaString, - }), - user: rt.partial({ - id: schemaString, - name: schemaString, - }), - working_directory: schemaString, - }), - hash: rt.partial({ - md5: schemaString, - sha1: schemaString, - sha256: schemaString, - sha384: schemaString, - sha512: schemaString, - ssdeep: schemaString, - tlsh: schemaString, - }), - interactive: schemaBoolean, - name: schemaString, - parent: rt.partial({ - args: schemaStringArray, - args_count: schemaStringOrNumber, - code_signature: rt.partial({ - digest_algorithm: schemaString, - exists: schemaBoolean, - signing_id: schemaString, - status: schemaString, - subject_name: schemaString, - team_id: schemaString, - timestamp: schemaDate, - trusted: schemaBoolean, - valid: schemaBoolean, - }), - command_line: schemaString, - elf: rt.partial({ - architecture: schemaString, - byte_order: schemaString, - cpu_type: schemaString, - creation_date: schemaDate, - exports: schemaUnknownArray, - header: rt.partial({ - abi_version: schemaString, - class: schemaString, - data: schemaString, - entrypoint: schemaStringOrNumber, - object_version: schemaString, - os_abi: schemaString, - type: schemaString, - version: schemaString, - }), - imports: schemaUnknownArray, - sections: rt.array( - rt.partial({ - chi2: schemaStringOrNumber, - entropy: schemaStringOrNumber, - flags: schemaString, - name: schemaString, - physical_offset: schemaString, - physical_size: schemaStringOrNumber, - type: schemaString, - virtual_address: schemaStringOrNumber, - virtual_size: schemaStringOrNumber, - }) - ), - segments: rt.array( - rt.partial({ - sections: schemaString, - type: schemaString, - }) - ), - shared_libraries: schemaStringArray, - telfhash: schemaString, - }), - end: schemaDate, - entity_id: schemaString, - executable: schemaString, - exit_code: schemaStringOrNumber, - group: rt.partial({ - id: schemaString, - name: schemaString, - }), - group_leader: rt.partial({ - entity_id: schemaString, - pid: schemaStringOrNumber, - start: schemaDate, - }), - hash: rt.partial({ - md5: schemaString, - sha1: schemaString, - sha256: schemaString, - sha384: schemaString, - sha512: schemaString, - ssdeep: schemaString, - tlsh: schemaString, - }), - interactive: schemaBoolean, - name: schemaString, - pe: rt.partial({ - architecture: schemaString, - company: schemaString, - description: schemaString, - file_version: schemaString, - imphash: schemaString, - original_file_name: schemaString, - pehash: schemaString, - product: schemaString, - }), - pgid: schemaStringOrNumber, - pid: schemaStringOrNumber, - real_group: rt.partial({ - id: schemaString, - name: schemaString, - }), - real_user: rt.partial({ - id: schemaString, - name: schemaString, - }), - saved_group: rt.partial({ - id: schemaString, - name: schemaString, - }), - saved_user: rt.partial({ - id: schemaString, - name: schemaString, - }), - start: schemaDate, - supplemental_groups: rt.partial({ - id: schemaString, - name: schemaString, - }), - thread: rt.partial({ - id: schemaStringOrNumber, - name: schemaString, - }), - title: schemaString, - uptime: schemaStringOrNumber, - user: rt.partial({ - id: schemaString, - name: schemaString, - }), - working_directory: schemaString, - }), - pe: rt.partial({ - architecture: schemaString, - company: schemaString, - description: schemaString, - file_version: schemaString, - imphash: schemaString, - original_file_name: schemaString, - pehash: schemaString, - product: schemaString, - }), - pgid: schemaStringOrNumber, - pid: schemaStringOrNumber, - previous: rt.partial({ - args: schemaStringArray, - args_count: schemaStringOrNumber, - executable: schemaString, - }), - real_group: rt.partial({ - id: schemaString, - name: schemaString, - }), - real_user: rt.partial({ - id: schemaString, - name: schemaString, - }), - saved_group: rt.partial({ - id: schemaString, - name: schemaString, - }), - saved_user: rt.partial({ - id: schemaString, - name: schemaString, - }), - session_leader: rt.partial({ - args: schemaStringArray, - args_count: schemaStringOrNumber, - command_line: schemaString, - entity_id: schemaString, - executable: schemaString, - group: rt.partial({ - id: schemaString, - name: schemaString, - }), - interactive: schemaBoolean, - name: schemaString, - parent: rt.partial({ - entity_id: schemaString, - pid: schemaStringOrNumber, - session_leader: rt.partial({ - entity_id: schemaString, - pid: schemaStringOrNumber, - start: schemaDate, - }), - start: schemaDate, - }), - pid: schemaStringOrNumber, - real_group: rt.partial({ - id: schemaString, - name: schemaString, - }), - real_user: rt.partial({ - id: schemaString, - name: schemaString, - }), - same_as_process: schemaBoolean, - saved_group: rt.partial({ - id: schemaString, - name: schemaString, - }), - saved_user: rt.partial({ - id: schemaString, - name: schemaString, - }), - start: schemaDate, - supplemental_groups: rt.partial({ - id: schemaString, - name: schemaString, - }), - user: rt.partial({ - id: schemaString, - name: schemaString, - }), - working_directory: schemaString, - }), - start: schemaDate, - supplemental_groups: rt.partial({ - id: schemaString, - name: schemaString, - }), - thread: rt.partial({ - id: schemaStringOrNumber, - name: schemaString, - }), - title: schemaString, - uptime: schemaStringOrNumber, - user: rt.partial({ - id: schemaString, - name: schemaString, - }), - working_directory: schemaString, - }), - registry: rt.partial({ - data: rt.partial({ - bytes: schemaString, - strings: schemaStringArray, + virtual_address: schemaStringOrNumber, + virtual_size: schemaStringOrNumber, + }) + ), + 'process.elf.segments': rt.array( + rt.partial({ + sections: schemaString, type: schemaString, - }), - hive: schemaString, - key: schemaString, - path: schemaString, - value: schemaString, - }), - related: rt.partial({ - hash: schemaStringArray, - hosts: schemaStringArray, - ip: schemaStringArray, - user: schemaStringArray, - }), - rule: rt.partial({ - author: schemaStringArray, - category: schemaString, - description: schemaString, - id: schemaString, - license: schemaString, - name: schemaString, - reference: schemaString, - ruleset: schemaString, - uuid: schemaString, - version: schemaString, - }), - server: rt.partial({ - address: schemaString, - as: rt.partial({ - number: schemaStringOrNumber, - organization: rt.partial({ - name: schemaString, - }), - }), - bytes: schemaStringOrNumber, - domain: schemaString, - geo: rt.partial({ - city_name: schemaString, - continent_code: schemaString, - continent_name: schemaString, - country_iso_code: schemaString, - country_name: schemaString, - location: schemaGeoPoint, - name: schemaString, - postal_code: schemaString, - region_iso_code: schemaString, - region_name: schemaString, - timezone: schemaString, - }), - ip: schemaString, - mac: schemaString, - nat: rt.partial({ - ip: schemaString, - port: schemaStringOrNumber, - }), - packets: schemaStringOrNumber, - port: schemaStringOrNumber, - registered_domain: schemaString, - subdomain: schemaString, - top_level_domain: schemaString, - user: rt.partial({ - domain: schemaString, - email: schemaString, - full_name: schemaString, - group: rt.partial({ - domain: schemaString, - id: schemaString, - name: schemaString, - }), - hash: schemaString, - id: schemaString, - name: schemaString, - roles: schemaStringArray, - }), - }), - service: rt.partial({ - address: schemaString, - environment: schemaString, - ephemeral_id: schemaString, - id: schemaString, - name: schemaString, - node: rt.partial({ + }) + ), + 'process.elf.shared_libraries': schemaStringArray, + 'process.elf.telfhash': schemaString, + 'process.end': schemaDate, + 'process.entity_id': schemaString, + 'process.entry_leader.args': schemaStringArray, + 'process.entry_leader.args_count': schemaStringOrNumber, + 'process.entry_leader.attested_groups.name': schemaString, + 'process.entry_leader.attested_user.id': schemaString, + 'process.entry_leader.attested_user.name': schemaString, + 'process.entry_leader.command_line': schemaString, + 'process.entry_leader.entity_id': schemaString, + 'process.entry_leader.entry_meta.source.ip': schemaString, + 'process.entry_leader.entry_meta.type': schemaString, + 'process.entry_leader.executable': schemaString, + 'process.entry_leader.group.id': schemaString, + 'process.entry_leader.group.name': schemaString, + 'process.entry_leader.interactive': schemaBoolean, + 'process.entry_leader.name': schemaString, + 'process.entry_leader.parent.entity_id': schemaString, + 'process.entry_leader.parent.pid': schemaStringOrNumber, + 'process.entry_leader.parent.session_leader.entity_id': schemaString, + 'process.entry_leader.parent.session_leader.pid': schemaStringOrNumber, + 'process.entry_leader.parent.session_leader.start': schemaDate, + 'process.entry_leader.parent.start': schemaDate, + 'process.entry_leader.pid': schemaStringOrNumber, + 'process.entry_leader.real_group.id': schemaString, + 'process.entry_leader.real_group.name': schemaString, + 'process.entry_leader.real_user.id': schemaString, + 'process.entry_leader.real_user.name': schemaString, + 'process.entry_leader.same_as_process': schemaBoolean, + 'process.entry_leader.saved_group.id': schemaString, + 'process.entry_leader.saved_group.name': schemaString, + 'process.entry_leader.saved_user.id': schemaString, + 'process.entry_leader.saved_user.name': schemaString, + 'process.entry_leader.start': schemaDate, + 'process.entry_leader.supplemental_groups.id': schemaString, + 'process.entry_leader.supplemental_groups.name': schemaString, + 'process.entry_leader.user.id': schemaString, + 'process.entry_leader.user.name': schemaString, + 'process.entry_leader.working_directory': schemaString, + 'process.env_vars': schemaStringArray, + 'process.executable': schemaString, + 'process.exit_code': schemaStringOrNumber, + 'process.group_leader.args': schemaStringArray, + 'process.group_leader.args_count': schemaStringOrNumber, + 'process.group_leader.command_line': schemaString, + 'process.group_leader.entity_id': schemaString, + 'process.group_leader.executable': schemaString, + 'process.group_leader.group.id': schemaString, + 'process.group_leader.group.name': schemaString, + 'process.group_leader.interactive': schemaBoolean, + 'process.group_leader.name': schemaString, + 'process.group_leader.pid': schemaStringOrNumber, + 'process.group_leader.real_group.id': schemaString, + 'process.group_leader.real_group.name': schemaString, + 'process.group_leader.real_user.id': schemaString, + 'process.group_leader.real_user.name': schemaString, + 'process.group_leader.same_as_process': schemaBoolean, + 'process.group_leader.saved_group.id': schemaString, + 'process.group_leader.saved_group.name': schemaString, + 'process.group_leader.saved_user.id': schemaString, + 'process.group_leader.saved_user.name': schemaString, + 'process.group_leader.start': schemaDate, + 'process.group_leader.supplemental_groups.id': schemaString, + 'process.group_leader.supplemental_groups.name': schemaString, + 'process.group_leader.user.id': schemaString, + 'process.group_leader.user.name': schemaString, + 'process.group_leader.working_directory': schemaString, + 'process.hash.md5': schemaString, + 'process.hash.sha1': schemaString, + 'process.hash.sha256': schemaString, + 'process.hash.sha384': schemaString, + 'process.hash.sha512': schemaString, + 'process.hash.ssdeep': schemaString, + 'process.hash.tlsh': schemaString, + 'process.interactive': schemaBoolean, + 'process.name': schemaString, + 'process.parent.args': schemaStringArray, + 'process.parent.args_count': schemaStringOrNumber, + 'process.parent.code_signature.digest_algorithm': schemaString, + 'process.parent.code_signature.exists': schemaBoolean, + 'process.parent.code_signature.signing_id': schemaString, + 'process.parent.code_signature.status': schemaString, + 'process.parent.code_signature.subject_name': schemaString, + 'process.parent.code_signature.team_id': schemaString, + 'process.parent.code_signature.timestamp': schemaDate, + 'process.parent.code_signature.trusted': schemaBoolean, + 'process.parent.code_signature.valid': schemaBoolean, + 'process.parent.command_line': schemaString, + 'process.parent.elf.architecture': schemaString, + 'process.parent.elf.byte_order': schemaString, + 'process.parent.elf.cpu_type': schemaString, + 'process.parent.elf.creation_date': schemaDate, + 'process.parent.elf.exports': schemaUnknownArray, + 'process.parent.elf.header.abi_version': schemaString, + 'process.parent.elf.header.class': schemaString, + 'process.parent.elf.header.data': schemaString, + 'process.parent.elf.header.entrypoint': schemaStringOrNumber, + 'process.parent.elf.header.object_version': schemaString, + 'process.parent.elf.header.os_abi': schemaString, + 'process.parent.elf.header.type': schemaString, + 'process.parent.elf.header.version': schemaString, + 'process.parent.elf.imports': schemaUnknownArray, + 'process.parent.elf.sections': rt.array( + rt.partial({ + chi2: schemaStringOrNumber, + entropy: schemaStringOrNumber, + flags: schemaString, name: schemaString, - role: schemaString, - roles: schemaStringArray, - }), - origin: rt.partial({ - address: schemaString, - environment: schemaString, - ephemeral_id: schemaString, - id: schemaString, - name: schemaString, - node: rt.partial({ - name: schemaString, - role: schemaString, - roles: schemaStringArray, - }), - state: schemaString, + physical_offset: schemaString, + physical_size: schemaStringOrNumber, type: schemaString, - version: schemaString, - }), - state: schemaString, - target: rt.partial({ - address: schemaString, - environment: schemaString, - ephemeral_id: schemaString, - id: schemaString, - name: schemaString, - node: rt.partial({ - name: schemaString, - role: schemaString, - roles: schemaStringArray, - }), - state: schemaString, + virtual_address: schemaStringOrNumber, + virtual_size: schemaStringOrNumber, + }) + ), + 'process.parent.elf.segments': rt.array( + rt.partial({ + sections: schemaString, type: schemaString, - version: schemaString, - }), - type: schemaString, - version: schemaString, - }), - source: rt.partial({ - address: schemaString, - as: rt.partial({ - number: schemaStringOrNumber, - organization: rt.partial({ - name: schemaString, - }), - }), - bytes: schemaStringOrNumber, - domain: schemaString, - geo: rt.partial({ - city_name: schemaString, - continent_code: schemaString, - continent_name: schemaString, - country_iso_code: schemaString, - country_name: schemaString, - location: schemaGeoPoint, - name: schemaString, - postal_code: schemaString, - region_iso_code: schemaString, - region_name: schemaString, - timezone: schemaString, - }), - ip: schemaString, - mac: schemaString, - nat: rt.partial({ - ip: schemaString, - port: schemaStringOrNumber, - }), - packets: schemaStringOrNumber, - port: schemaStringOrNumber, - registered_domain: schemaString, - subdomain: schemaString, - top_level_domain: schemaString, - user: rt.partial({ - domain: schemaString, - email: schemaString, - full_name: schemaString, - group: rt.partial({ - domain: schemaString, - id: schemaString, - name: schemaString, - }), - hash: schemaString, - id: schemaString, - name: schemaString, - roles: schemaStringArray, - }), - }), - span: rt.partial({ - id: schemaString, - }), + }) + ), + 'process.parent.elf.shared_libraries': schemaStringArray, + 'process.parent.elf.telfhash': schemaString, + 'process.parent.end': schemaDate, + 'process.parent.entity_id': schemaString, + 'process.parent.executable': schemaString, + 'process.parent.exit_code': schemaStringOrNumber, + 'process.parent.group.id': schemaString, + 'process.parent.group.name': schemaString, + 'process.parent.group_leader.entity_id': schemaString, + 'process.parent.group_leader.pid': schemaStringOrNumber, + 'process.parent.group_leader.start': schemaDate, + 'process.parent.hash.md5': schemaString, + 'process.parent.hash.sha1': schemaString, + 'process.parent.hash.sha256': schemaString, + 'process.parent.hash.sha384': schemaString, + 'process.parent.hash.sha512': schemaString, + 'process.parent.hash.ssdeep': schemaString, + 'process.parent.hash.tlsh': schemaString, + 'process.parent.interactive': schemaBoolean, + 'process.parent.name': schemaString, + 'process.parent.pe.architecture': schemaString, + 'process.parent.pe.company': schemaString, + 'process.parent.pe.description': schemaString, + 'process.parent.pe.file_version': schemaString, + 'process.parent.pe.imphash': schemaString, + 'process.parent.pe.original_file_name': schemaString, + 'process.parent.pe.pehash': schemaString, + 'process.parent.pe.product': schemaString, + 'process.parent.pgid': schemaStringOrNumber, + 'process.parent.pid': schemaStringOrNumber, + 'process.parent.real_group.id': schemaString, + 'process.parent.real_group.name': schemaString, + 'process.parent.real_user.id': schemaString, + 'process.parent.real_user.name': schemaString, + 'process.parent.saved_group.id': schemaString, + 'process.parent.saved_group.name': schemaString, + 'process.parent.saved_user.id': schemaString, + 'process.parent.saved_user.name': schemaString, + 'process.parent.start': schemaDate, + 'process.parent.supplemental_groups.id': schemaString, + 'process.parent.supplemental_groups.name': schemaString, + 'process.parent.thread.id': schemaStringOrNumber, + 'process.parent.thread.name': schemaString, + 'process.parent.title': schemaString, + 'process.parent.uptime': schemaStringOrNumber, + 'process.parent.user.id': schemaString, + 'process.parent.user.name': schemaString, + 'process.parent.working_directory': schemaString, + 'process.pe.architecture': schemaString, + 'process.pe.company': schemaString, + 'process.pe.description': schemaString, + 'process.pe.file_version': schemaString, + 'process.pe.imphash': schemaString, + 'process.pe.original_file_name': schemaString, + 'process.pe.pehash': schemaString, + 'process.pe.product': schemaString, + 'process.pgid': schemaStringOrNumber, + 'process.pid': schemaStringOrNumber, + 'process.previous.args': schemaStringArray, + 'process.previous.args_count': schemaStringOrNumber, + 'process.previous.executable': schemaString, + 'process.real_group.id': schemaString, + 'process.real_group.name': schemaString, + 'process.real_user.id': schemaString, + 'process.real_user.name': schemaString, + 'process.saved_group.id': schemaString, + 'process.saved_group.name': schemaString, + 'process.saved_user.id': schemaString, + 'process.saved_user.name': schemaString, + 'process.session_leader.args': schemaStringArray, + 'process.session_leader.args_count': schemaStringOrNumber, + 'process.session_leader.command_line': schemaString, + 'process.session_leader.entity_id': schemaString, + 'process.session_leader.executable': schemaString, + 'process.session_leader.group.id': schemaString, + 'process.session_leader.group.name': schemaString, + 'process.session_leader.interactive': schemaBoolean, + 'process.session_leader.name': schemaString, + 'process.session_leader.parent.entity_id': schemaString, + 'process.session_leader.parent.pid': schemaStringOrNumber, + 'process.session_leader.parent.session_leader.entity_id': schemaString, + 'process.session_leader.parent.session_leader.pid': schemaStringOrNumber, + 'process.session_leader.parent.session_leader.start': schemaDate, + 'process.session_leader.parent.start': schemaDate, + 'process.session_leader.pid': schemaStringOrNumber, + 'process.session_leader.real_group.id': schemaString, + 'process.session_leader.real_group.name': schemaString, + 'process.session_leader.real_user.id': schemaString, + 'process.session_leader.real_user.name': schemaString, + 'process.session_leader.same_as_process': schemaBoolean, + 'process.session_leader.saved_group.id': schemaString, + 'process.session_leader.saved_group.name': schemaString, + 'process.session_leader.saved_user.id': schemaString, + 'process.session_leader.saved_user.name': schemaString, + 'process.session_leader.start': schemaDate, + 'process.session_leader.supplemental_groups.id': schemaString, + 'process.session_leader.supplemental_groups.name': schemaString, + 'process.session_leader.user.id': schemaString, + 'process.session_leader.user.name': schemaString, + 'process.session_leader.working_directory': schemaString, + 'process.start': schemaDate, + 'process.supplemental_groups.id': schemaString, + 'process.supplemental_groups.name': schemaString, + 'process.thread.id': schemaStringOrNumber, + 'process.thread.name': schemaString, + 'process.title': schemaString, + 'process.uptime': schemaStringOrNumber, + 'process.user.id': schemaString, + 'process.user.name': schemaString, + 'process.working_directory': schemaString, + 'registry.data.bytes': schemaString, + 'registry.data.strings': schemaStringArray, + 'registry.data.type': schemaString, + 'registry.hive': schemaString, + 'registry.key': schemaString, + 'registry.path': schemaString, + 'registry.value': schemaString, + 'related.hash': schemaStringArray, + 'related.hosts': schemaStringArray, + 'related.ip': schemaStringArray, + 'related.user': schemaStringArray, + 'rule.author': schemaStringArray, + 'rule.category': schemaString, + 'rule.description': schemaString, + 'rule.id': schemaString, + 'rule.license': schemaString, + 'rule.name': schemaString, + 'rule.reference': schemaString, + 'rule.ruleset': schemaString, + 'rule.uuid': schemaString, + 'rule.version': schemaString, + 'server.address': schemaString, + 'server.as.number': schemaStringOrNumber, + 'server.as.organization.name': schemaString, + 'server.bytes': schemaStringOrNumber, + 'server.domain': schemaString, + 'server.geo.city_name': schemaString, + 'server.geo.continent_code': schemaString, + 'server.geo.continent_name': schemaString, + 'server.geo.country_iso_code': schemaString, + 'server.geo.country_name': schemaString, + 'server.geo.location': schemaGeoPoint, + 'server.geo.name': schemaString, + 'server.geo.postal_code': schemaString, + 'server.geo.region_iso_code': schemaString, + 'server.geo.region_name': schemaString, + 'server.geo.timezone': schemaString, + 'server.ip': schemaString, + 'server.mac': schemaString, + 'server.nat.ip': schemaString, + 'server.nat.port': schemaStringOrNumber, + 'server.packets': schemaStringOrNumber, + 'server.port': schemaStringOrNumber, + 'server.registered_domain': schemaString, + 'server.subdomain': schemaString, + 'server.top_level_domain': schemaString, + 'server.user.domain': schemaString, + 'server.user.email': schemaString, + 'server.user.full_name': schemaString, + 'server.user.group.domain': schemaString, + 'server.user.group.id': schemaString, + 'server.user.group.name': schemaString, + 'server.user.hash': schemaString, + 'server.user.id': schemaString, + 'server.user.name': schemaString, + 'server.user.roles': schemaStringArray, + 'service.address': schemaString, + 'service.environment': schemaString, + 'service.ephemeral_id': schemaString, + 'service.id': schemaString, + 'service.name': schemaString, + 'service.node.name': schemaString, + 'service.node.role': schemaString, + 'service.node.roles': schemaStringArray, + 'service.origin.address': schemaString, + 'service.origin.environment': schemaString, + 'service.origin.ephemeral_id': schemaString, + 'service.origin.id': schemaString, + 'service.origin.name': schemaString, + 'service.origin.node.name': schemaString, + 'service.origin.node.role': schemaString, + 'service.origin.node.roles': schemaStringArray, + 'service.origin.state': schemaString, + 'service.origin.type': schemaString, + 'service.origin.version': schemaString, + 'service.state': schemaString, + 'service.target.address': schemaString, + 'service.target.environment': schemaString, + 'service.target.ephemeral_id': schemaString, + 'service.target.id': schemaString, + 'service.target.name': schemaString, + 'service.target.node.name': schemaString, + 'service.target.node.role': schemaString, + 'service.target.node.roles': schemaStringArray, + 'service.target.state': schemaString, + 'service.target.type': schemaString, + 'service.target.version': schemaString, + 'service.type': schemaString, + 'service.version': schemaString, + 'source.address': schemaString, + 'source.as.number': schemaStringOrNumber, + 'source.as.organization.name': schemaString, + 'source.bytes': schemaStringOrNumber, + 'source.domain': schemaString, + 'source.geo.city_name': schemaString, + 'source.geo.continent_code': schemaString, + 'source.geo.continent_name': schemaString, + 'source.geo.country_iso_code': schemaString, + 'source.geo.country_name': schemaString, + 'source.geo.location': schemaGeoPoint, + 'source.geo.name': schemaString, + 'source.geo.postal_code': schemaString, + 'source.geo.region_iso_code': schemaString, + 'source.geo.region_name': schemaString, + 'source.geo.timezone': schemaString, + 'source.ip': schemaString, + 'source.mac': schemaString, + 'source.nat.ip': schemaString, + 'source.nat.port': schemaStringOrNumber, + 'source.packets': schemaStringOrNumber, + 'source.port': schemaStringOrNumber, + 'source.registered_domain': schemaString, + 'source.subdomain': schemaString, + 'source.top_level_domain': schemaString, + 'source.user.domain': schemaString, + 'source.user.email': schemaString, + 'source.user.full_name': schemaString, + 'source.user.group.domain': schemaString, + 'source.user.group.id': schemaString, + 'source.user.group.name': schemaString, + 'source.user.hash': schemaString, + 'source.user.id': schemaString, + 'source.user.name': schemaString, + 'source.user.roles': schemaStringArray, + 'span.id': schemaString, tags: schemaStringArray, - threat: rt.partial({ - enrichments: rt.array( - rt.partial({ - matched: rt.partial({ - atomic: schemaString, - field: schemaString, - id: schemaString, - index: schemaString, - occurred: schemaDate, - type: schemaString, - }), - }) - ), - feed: rt.partial({ - dashboard_id: schemaString, - description: schemaString, - name: schemaString, - reference: schemaString, - }), - framework: schemaString, - group: rt.partial({ - alias: schemaStringArray, - id: schemaString, - name: schemaString, - reference: schemaString, - }), - indicator: rt.partial({ - as: rt.partial({ - number: schemaStringOrNumber, - organization: rt.partial({ - name: schemaString, - }), - }), - confidence: schemaString, - description: schemaString, - email: rt.partial({ - address: schemaString, - }), - file: rt.partial({ - accessed: schemaDate, - attributes: schemaStringArray, - code_signature: rt.partial({ - digest_algorithm: schemaString, - exists: schemaBoolean, - signing_id: schemaString, - status: schemaString, - subject_name: schemaString, - team_id: schemaString, - timestamp: schemaDate, - trusted: schemaBoolean, - valid: schemaBoolean, - }), - created: schemaDate, - ctime: schemaDate, - device: schemaString, - directory: schemaString, - drive_letter: schemaString, - elf: rt.partial({ - architecture: schemaString, - byte_order: schemaString, - cpu_type: schemaString, - creation_date: schemaDate, - exports: schemaUnknownArray, - header: rt.partial({ - abi_version: schemaString, - class: schemaString, - data: schemaString, - entrypoint: schemaStringOrNumber, - object_version: schemaString, - os_abi: schemaString, - type: schemaString, - version: schemaString, - }), - imports: schemaUnknownArray, - sections: rt.array( - rt.partial({ - chi2: schemaStringOrNumber, - entropy: schemaStringOrNumber, - flags: schemaString, - name: schemaString, - physical_offset: schemaString, - physical_size: schemaStringOrNumber, - type: schemaString, - virtual_address: schemaStringOrNumber, - virtual_size: schemaStringOrNumber, - }) - ), - segments: rt.array( - rt.partial({ - sections: schemaString, - type: schemaString, - }) - ), - shared_libraries: schemaStringArray, - telfhash: schemaString, - }), - extension: schemaString, - fork_name: schemaString, - gid: schemaString, - group: schemaString, - hash: rt.partial({ - md5: schemaString, - sha1: schemaString, - sha256: schemaString, - sha384: schemaString, - sha512: schemaString, - ssdeep: schemaString, - tlsh: schemaString, - }), - inode: schemaString, - mime_type: schemaString, - mode: schemaString, - mtime: schemaDate, - name: schemaString, - owner: schemaString, - path: schemaString, - pe: rt.partial({ - architecture: schemaString, - company: schemaString, - description: schemaString, - file_version: schemaString, - imphash: schemaString, - original_file_name: schemaString, - pehash: schemaString, - product: schemaString, - }), - size: schemaStringOrNumber, - target_path: schemaString, - type: schemaString, - uid: schemaString, - x509: rt.partial({ - alternative_names: schemaStringArray, - issuer: rt.partial({ - common_name: schemaStringArray, - country: schemaStringArray, - distinguished_name: schemaString, - locality: schemaStringArray, - organization: schemaStringArray, - organizational_unit: schemaStringArray, - state_or_province: schemaStringArray, - }), - not_after: schemaDate, - not_before: schemaDate, - public_key_algorithm: schemaString, - public_key_curve: schemaString, - public_key_exponent: schemaStringOrNumber, - public_key_size: schemaStringOrNumber, - serial_number: schemaString, - signature_algorithm: schemaString, - subject: rt.partial({ - common_name: schemaStringArray, - country: schemaStringArray, - distinguished_name: schemaString, - locality: schemaStringArray, - organization: schemaStringArray, - organizational_unit: schemaStringArray, - state_or_province: schemaStringArray, - }), - version_number: schemaString, - }), - }), - first_seen: schemaDate, - geo: rt.partial({ - city_name: schemaString, - continent_code: schemaString, - continent_name: schemaString, - country_iso_code: schemaString, - country_name: schemaString, - location: schemaGeoPoint, - name: schemaString, - postal_code: schemaString, - region_iso_code: schemaString, - region_name: schemaString, - timezone: schemaString, - }), - ip: schemaString, - last_seen: schemaDate, - marking: rt.partial({ - tlp: schemaString, - tlp_version: schemaString, - }), - modified_at: schemaDate, - port: schemaStringOrNumber, - provider: schemaString, - reference: schemaString, - registry: rt.partial({ - data: rt.partial({ - bytes: schemaString, - strings: schemaStringArray, - type: schemaString, - }), - hive: schemaString, - key: schemaString, - path: schemaString, - value: schemaString, - }), - scanner_stats: schemaStringOrNumber, - sightings: schemaStringOrNumber, - type: schemaString, - url: rt.partial({ - domain: schemaString, - extension: schemaString, - fragment: schemaString, - full: schemaString, - original: schemaString, - password: schemaString, - path: schemaString, - port: schemaStringOrNumber, - query: schemaString, - registered_domain: schemaString, - scheme: schemaString, - subdomain: schemaString, - top_level_domain: schemaString, - username: schemaString, - }), - x509: rt.partial({ - alternative_names: schemaStringArray, - issuer: rt.partial({ - common_name: schemaStringArray, - country: schemaStringArray, - distinguished_name: schemaString, - locality: schemaStringArray, - organization: schemaStringArray, - organizational_unit: schemaStringArray, - state_or_province: schemaStringArray, - }), - not_after: schemaDate, - not_before: schemaDate, - public_key_algorithm: schemaString, - public_key_curve: schemaString, - public_key_exponent: schemaStringOrNumber, - public_key_size: schemaStringOrNumber, - serial_number: schemaString, - signature_algorithm: schemaString, - subject: rt.partial({ - common_name: schemaStringArray, - country: schemaStringArray, - distinguished_name: schemaString, - locality: schemaStringArray, - organization: schemaStringArray, - organizational_unit: schemaStringArray, - state_or_province: schemaStringArray, - }), - version_number: schemaString, - }), - }), - software: rt.partial({ - alias: schemaStringArray, - id: schemaString, + 'threat.enrichments': rt.array( + rt.partial({ + 'matched.atomic': schemaString, + 'matched.field': schemaString, + 'matched.id': schemaString, + 'matched.index': schemaString, + 'matched.occurred': schemaDate, + 'matched.type': schemaString, + }) + ), + 'threat.feed.dashboard_id': schemaString, + 'threat.feed.description': schemaString, + 'threat.feed.name': schemaString, + 'threat.feed.reference': schemaString, + 'threat.framework': schemaString, + 'threat.group.alias': schemaStringArray, + 'threat.group.id': schemaString, + 'threat.group.name': schemaString, + 'threat.group.reference': schemaString, + 'threat.indicator.as.number': schemaStringOrNumber, + 'threat.indicator.as.organization.name': schemaString, + 'threat.indicator.confidence': schemaString, + 'threat.indicator.description': schemaString, + 'threat.indicator.email.address': schemaString, + 'threat.indicator.file.accessed': schemaDate, + 'threat.indicator.file.attributes': schemaStringArray, + 'threat.indicator.file.code_signature.digest_algorithm': schemaString, + 'threat.indicator.file.code_signature.exists': schemaBoolean, + 'threat.indicator.file.code_signature.signing_id': schemaString, + 'threat.indicator.file.code_signature.status': schemaString, + 'threat.indicator.file.code_signature.subject_name': schemaString, + 'threat.indicator.file.code_signature.team_id': schemaString, + 'threat.indicator.file.code_signature.timestamp': schemaDate, + 'threat.indicator.file.code_signature.trusted': schemaBoolean, + 'threat.indicator.file.code_signature.valid': schemaBoolean, + 'threat.indicator.file.created': schemaDate, + 'threat.indicator.file.ctime': schemaDate, + 'threat.indicator.file.device': schemaString, + 'threat.indicator.file.directory': schemaString, + 'threat.indicator.file.drive_letter': schemaString, + 'threat.indicator.file.elf.architecture': schemaString, + 'threat.indicator.file.elf.byte_order': schemaString, + 'threat.indicator.file.elf.cpu_type': schemaString, + 'threat.indicator.file.elf.creation_date': schemaDate, + 'threat.indicator.file.elf.exports': schemaUnknownArray, + 'threat.indicator.file.elf.header.abi_version': schemaString, + 'threat.indicator.file.elf.header.class': schemaString, + 'threat.indicator.file.elf.header.data': schemaString, + 'threat.indicator.file.elf.header.entrypoint': schemaStringOrNumber, + 'threat.indicator.file.elf.header.object_version': schemaString, + 'threat.indicator.file.elf.header.os_abi': schemaString, + 'threat.indicator.file.elf.header.type': schemaString, + 'threat.indicator.file.elf.header.version': schemaString, + 'threat.indicator.file.elf.imports': schemaUnknownArray, + 'threat.indicator.file.elf.sections': rt.array( + rt.partial({ + chi2: schemaStringOrNumber, + entropy: schemaStringOrNumber, + flags: schemaString, name: schemaString, - platforms: schemaStringArray, - reference: schemaString, + physical_offset: schemaString, + physical_size: schemaStringOrNumber, type: schemaString, - }), - tactic: rt.partial({ - id: schemaStringArray, - name: schemaStringArray, - reference: schemaStringArray, - }), - technique: rt.partial({ - id: schemaStringArray, - name: schemaStringArray, - reference: schemaStringArray, - subtechnique: rt.partial({ - id: schemaStringArray, - name: schemaStringArray, - reference: schemaStringArray, - }), - }), - }), - tls: rt.partial({ - cipher: schemaString, - client: rt.partial({ - certificate: schemaString, - certificate_chain: schemaStringArray, - hash: rt.partial({ - md5: schemaString, - sha1: schemaString, - sha256: schemaString, - }), - issuer: schemaString, - ja3: schemaString, - not_after: schemaDate, - not_before: schemaDate, - server_name: schemaString, - subject: schemaString, - supported_ciphers: schemaStringArray, - x509: rt.partial({ - alternative_names: schemaStringArray, - issuer: rt.partial({ - common_name: schemaStringArray, - country: schemaStringArray, - distinguished_name: schemaString, - locality: schemaStringArray, - organization: schemaStringArray, - organizational_unit: schemaStringArray, - state_or_province: schemaStringArray, - }), - not_after: schemaDate, - not_before: schemaDate, - public_key_algorithm: schemaString, - public_key_curve: schemaString, - public_key_exponent: schemaStringOrNumber, - public_key_size: schemaStringOrNumber, - serial_number: schemaString, - signature_algorithm: schemaString, - subject: rt.partial({ - common_name: schemaStringArray, - country: schemaStringArray, - distinguished_name: schemaString, - locality: schemaStringArray, - organization: schemaStringArray, - organizational_unit: schemaStringArray, - state_or_province: schemaStringArray, - }), - version_number: schemaString, - }), - }), - curve: schemaString, - established: schemaBoolean, - next_protocol: schemaString, - resumed: schemaBoolean, - server: rt.partial({ - certificate: schemaString, - certificate_chain: schemaStringArray, - hash: rt.partial({ - md5: schemaString, - sha1: schemaString, - sha256: schemaString, - }), - issuer: schemaString, - ja3s: schemaString, - not_after: schemaDate, - not_before: schemaDate, - subject: schemaString, - x509: rt.partial({ - alternative_names: schemaStringArray, - issuer: rt.partial({ - common_name: schemaStringArray, - country: schemaStringArray, - distinguished_name: schemaString, - locality: schemaStringArray, - organization: schemaStringArray, - organizational_unit: schemaStringArray, - state_or_province: schemaStringArray, - }), - not_after: schemaDate, - not_before: schemaDate, - public_key_algorithm: schemaString, - public_key_curve: schemaString, - public_key_exponent: schemaStringOrNumber, - public_key_size: schemaStringOrNumber, - serial_number: schemaString, - signature_algorithm: schemaString, - subject: rt.partial({ - common_name: schemaStringArray, - country: schemaStringArray, - distinguished_name: schemaString, - locality: schemaStringArray, - organization: schemaStringArray, - organizational_unit: schemaStringArray, - state_or_province: schemaStringArray, - }), - version_number: schemaString, - }), - }), - version: schemaString, - version_protocol: schemaString, - }), - trace: rt.partial({ - id: schemaString, - }), - transaction: rt.partial({ - id: schemaString, - }), - url: rt.partial({ - domain: schemaString, - extension: schemaString, - fragment: schemaString, - full: schemaString, - original: schemaString, - password: schemaString, - path: schemaString, - port: schemaStringOrNumber, - query: schemaString, - registered_domain: schemaString, - scheme: schemaString, - subdomain: schemaString, - top_level_domain: schemaString, - username: schemaString, - }), - user: rt.partial({ - changes: rt.partial({ - domain: schemaString, - email: schemaString, - full_name: schemaString, - group: rt.partial({ - domain: schemaString, - id: schemaString, - name: schemaString, - }), - hash: schemaString, - id: schemaString, - name: schemaString, - roles: schemaStringArray, - }), - domain: schemaString, - effective: rt.partial({ - domain: schemaString, - email: schemaString, - full_name: schemaString, - group: rt.partial({ - domain: schemaString, - id: schemaString, - name: schemaString, - }), - hash: schemaString, - id: schemaString, - name: schemaString, - roles: schemaStringArray, - }), - email: schemaString, - full_name: schemaString, - group: rt.partial({ - domain: schemaString, - id: schemaString, - name: schemaString, - }), - hash: schemaString, - id: schemaString, - name: schemaString, - risk: rt.partial({ - calculated_level: schemaString, - calculated_score: schemaNumber, - calculated_score_norm: schemaNumber, - static_level: schemaString, - static_score: schemaNumber, - static_score_norm: schemaNumber, - }), - roles: schemaStringArray, - target: rt.partial({ - domain: schemaString, - email: schemaString, - full_name: schemaString, - group: rt.partial({ - domain: schemaString, - id: schemaString, - name: schemaString, - }), - hash: schemaString, - id: schemaString, - name: schemaString, - roles: schemaStringArray, - }), - }), - user_agent: rt.partial({ - device: rt.partial({ - name: schemaString, - }), - name: schemaString, - original: schemaString, - os: rt.partial({ - family: schemaString, - full: schemaString, - kernel: schemaString, - name: schemaString, - platform: schemaString, + virtual_address: schemaStringOrNumber, + virtual_size: schemaStringOrNumber, + }) + ), + 'threat.indicator.file.elf.segments': rt.array( + rt.partial({ + sections: schemaString, type: schemaString, - version: schemaString, - }), - version: schemaString, - }), - vulnerability: rt.partial({ - category: schemaStringArray, - classification: schemaString, - description: schemaString, - enumeration: schemaString, - id: schemaString, - reference: schemaString, - report_id: schemaString, - scanner: rt.partial({ - vendor: schemaString, - }), - score: rt.partial({ - base: schemaNumber, - environmental: schemaNumber, - temporal: schemaNumber, - version: schemaString, - }), - severity: schemaString, - }), + }) + ), + 'threat.indicator.file.elf.shared_libraries': schemaStringArray, + 'threat.indicator.file.elf.telfhash': schemaString, + 'threat.indicator.file.extension': schemaString, + 'threat.indicator.file.fork_name': schemaString, + 'threat.indicator.file.gid': schemaString, + 'threat.indicator.file.group': schemaString, + 'threat.indicator.file.hash.md5': schemaString, + 'threat.indicator.file.hash.sha1': schemaString, + 'threat.indicator.file.hash.sha256': schemaString, + 'threat.indicator.file.hash.sha384': schemaString, + 'threat.indicator.file.hash.sha512': schemaString, + 'threat.indicator.file.hash.ssdeep': schemaString, + 'threat.indicator.file.hash.tlsh': schemaString, + 'threat.indicator.file.inode': schemaString, + 'threat.indicator.file.mime_type': schemaString, + 'threat.indicator.file.mode': schemaString, + 'threat.indicator.file.mtime': schemaDate, + 'threat.indicator.file.name': schemaString, + 'threat.indicator.file.owner': schemaString, + 'threat.indicator.file.path': schemaString, + 'threat.indicator.file.pe.architecture': schemaString, + 'threat.indicator.file.pe.company': schemaString, + 'threat.indicator.file.pe.description': schemaString, + 'threat.indicator.file.pe.file_version': schemaString, + 'threat.indicator.file.pe.imphash': schemaString, + 'threat.indicator.file.pe.original_file_name': schemaString, + 'threat.indicator.file.pe.pehash': schemaString, + 'threat.indicator.file.pe.product': schemaString, + 'threat.indicator.file.size': schemaStringOrNumber, + 'threat.indicator.file.target_path': schemaString, + 'threat.indicator.file.type': schemaString, + 'threat.indicator.file.uid': schemaString, + 'threat.indicator.file.x509.alternative_names': schemaStringArray, + 'threat.indicator.file.x509.issuer.common_name': schemaStringArray, + 'threat.indicator.file.x509.issuer.country': schemaStringArray, + 'threat.indicator.file.x509.issuer.distinguished_name': schemaString, + 'threat.indicator.file.x509.issuer.locality': schemaStringArray, + 'threat.indicator.file.x509.issuer.organization': schemaStringArray, + 'threat.indicator.file.x509.issuer.organizational_unit': schemaStringArray, + 'threat.indicator.file.x509.issuer.state_or_province': schemaStringArray, + 'threat.indicator.file.x509.not_after': schemaDate, + 'threat.indicator.file.x509.not_before': schemaDate, + 'threat.indicator.file.x509.public_key_algorithm': schemaString, + 'threat.indicator.file.x509.public_key_curve': schemaString, + 'threat.indicator.file.x509.public_key_exponent': schemaStringOrNumber, + 'threat.indicator.file.x509.public_key_size': schemaStringOrNumber, + 'threat.indicator.file.x509.serial_number': schemaString, + 'threat.indicator.file.x509.signature_algorithm': schemaString, + 'threat.indicator.file.x509.subject.common_name': schemaStringArray, + 'threat.indicator.file.x509.subject.country': schemaStringArray, + 'threat.indicator.file.x509.subject.distinguished_name': schemaString, + 'threat.indicator.file.x509.subject.locality': schemaStringArray, + 'threat.indicator.file.x509.subject.organization': schemaStringArray, + 'threat.indicator.file.x509.subject.organizational_unit': schemaStringArray, + 'threat.indicator.file.x509.subject.state_or_province': schemaStringArray, + 'threat.indicator.file.x509.version_number': schemaString, + 'threat.indicator.first_seen': schemaDate, + 'threat.indicator.geo.city_name': schemaString, + 'threat.indicator.geo.continent_code': schemaString, + 'threat.indicator.geo.continent_name': schemaString, + 'threat.indicator.geo.country_iso_code': schemaString, + 'threat.indicator.geo.country_name': schemaString, + 'threat.indicator.geo.location': schemaGeoPoint, + 'threat.indicator.geo.name': schemaString, + 'threat.indicator.geo.postal_code': schemaString, + 'threat.indicator.geo.region_iso_code': schemaString, + 'threat.indicator.geo.region_name': schemaString, + 'threat.indicator.geo.timezone': schemaString, + 'threat.indicator.ip': schemaString, + 'threat.indicator.last_seen': schemaDate, + 'threat.indicator.marking.tlp': schemaString, + 'threat.indicator.marking.tlp_version': schemaString, + 'threat.indicator.modified_at': schemaDate, + 'threat.indicator.port': schemaStringOrNumber, + 'threat.indicator.provider': schemaString, + 'threat.indicator.reference': schemaString, + 'threat.indicator.registry.data.bytes': schemaString, + 'threat.indicator.registry.data.strings': schemaStringArray, + 'threat.indicator.registry.data.type': schemaString, + 'threat.indicator.registry.hive': schemaString, + 'threat.indicator.registry.key': schemaString, + 'threat.indicator.registry.path': schemaString, + 'threat.indicator.registry.value': schemaString, + 'threat.indicator.scanner_stats': schemaStringOrNumber, + 'threat.indicator.sightings': schemaStringOrNumber, + 'threat.indicator.type': schemaString, + 'threat.indicator.url.domain': schemaString, + 'threat.indicator.url.extension': schemaString, + 'threat.indicator.url.fragment': schemaString, + 'threat.indicator.url.full': schemaString, + 'threat.indicator.url.original': schemaString, + 'threat.indicator.url.password': schemaString, + 'threat.indicator.url.path': schemaString, + 'threat.indicator.url.port': schemaStringOrNumber, + 'threat.indicator.url.query': schemaString, + 'threat.indicator.url.registered_domain': schemaString, + 'threat.indicator.url.scheme': schemaString, + 'threat.indicator.url.subdomain': schemaString, + 'threat.indicator.url.top_level_domain': schemaString, + 'threat.indicator.url.username': schemaString, + 'threat.indicator.x509.alternative_names': schemaStringArray, + 'threat.indicator.x509.issuer.common_name': schemaStringArray, + 'threat.indicator.x509.issuer.country': schemaStringArray, + 'threat.indicator.x509.issuer.distinguished_name': schemaString, + 'threat.indicator.x509.issuer.locality': schemaStringArray, + 'threat.indicator.x509.issuer.organization': schemaStringArray, + 'threat.indicator.x509.issuer.organizational_unit': schemaStringArray, + 'threat.indicator.x509.issuer.state_or_province': schemaStringArray, + 'threat.indicator.x509.not_after': schemaDate, + 'threat.indicator.x509.not_before': schemaDate, + 'threat.indicator.x509.public_key_algorithm': schemaString, + 'threat.indicator.x509.public_key_curve': schemaString, + 'threat.indicator.x509.public_key_exponent': schemaStringOrNumber, + 'threat.indicator.x509.public_key_size': schemaStringOrNumber, + 'threat.indicator.x509.serial_number': schemaString, + 'threat.indicator.x509.signature_algorithm': schemaString, + 'threat.indicator.x509.subject.common_name': schemaStringArray, + 'threat.indicator.x509.subject.country': schemaStringArray, + 'threat.indicator.x509.subject.distinguished_name': schemaString, + 'threat.indicator.x509.subject.locality': schemaStringArray, + 'threat.indicator.x509.subject.organization': schemaStringArray, + 'threat.indicator.x509.subject.organizational_unit': schemaStringArray, + 'threat.indicator.x509.subject.state_or_province': schemaStringArray, + 'threat.indicator.x509.version_number': schemaString, + 'threat.software.alias': schemaStringArray, + 'threat.software.id': schemaString, + 'threat.software.name': schemaString, + 'threat.software.platforms': schemaStringArray, + 'threat.software.reference': schemaString, + 'threat.software.type': schemaString, + 'threat.tactic.id': schemaStringArray, + 'threat.tactic.name': schemaStringArray, + 'threat.tactic.reference': schemaStringArray, + 'threat.technique.id': schemaStringArray, + 'threat.technique.name': schemaStringArray, + 'threat.technique.reference': schemaStringArray, + 'threat.technique.subtechnique.id': schemaStringArray, + 'threat.technique.subtechnique.name': schemaStringArray, + 'threat.technique.subtechnique.reference': schemaStringArray, + 'tls.cipher': schemaString, + 'tls.client.certificate': schemaString, + 'tls.client.certificate_chain': schemaStringArray, + 'tls.client.hash.md5': schemaString, + 'tls.client.hash.sha1': schemaString, + 'tls.client.hash.sha256': schemaString, + 'tls.client.issuer': schemaString, + 'tls.client.ja3': schemaString, + 'tls.client.not_after': schemaDate, + 'tls.client.not_before': schemaDate, + 'tls.client.server_name': schemaString, + 'tls.client.subject': schemaString, + 'tls.client.supported_ciphers': schemaStringArray, + 'tls.client.x509.alternative_names': schemaStringArray, + 'tls.client.x509.issuer.common_name': schemaStringArray, + 'tls.client.x509.issuer.country': schemaStringArray, + 'tls.client.x509.issuer.distinguished_name': schemaString, + 'tls.client.x509.issuer.locality': schemaStringArray, + 'tls.client.x509.issuer.organization': schemaStringArray, + 'tls.client.x509.issuer.organizational_unit': schemaStringArray, + 'tls.client.x509.issuer.state_or_province': schemaStringArray, + 'tls.client.x509.not_after': schemaDate, + 'tls.client.x509.not_before': schemaDate, + 'tls.client.x509.public_key_algorithm': schemaString, + 'tls.client.x509.public_key_curve': schemaString, + 'tls.client.x509.public_key_exponent': schemaStringOrNumber, + 'tls.client.x509.public_key_size': schemaStringOrNumber, + 'tls.client.x509.serial_number': schemaString, + 'tls.client.x509.signature_algorithm': schemaString, + 'tls.client.x509.subject.common_name': schemaStringArray, + 'tls.client.x509.subject.country': schemaStringArray, + 'tls.client.x509.subject.distinguished_name': schemaString, + 'tls.client.x509.subject.locality': schemaStringArray, + 'tls.client.x509.subject.organization': schemaStringArray, + 'tls.client.x509.subject.organizational_unit': schemaStringArray, + 'tls.client.x509.subject.state_or_province': schemaStringArray, + 'tls.client.x509.version_number': schemaString, + 'tls.curve': schemaString, + 'tls.established': schemaBoolean, + 'tls.next_protocol': schemaString, + 'tls.resumed': schemaBoolean, + 'tls.server.certificate': schemaString, + 'tls.server.certificate_chain': schemaStringArray, + 'tls.server.hash.md5': schemaString, + 'tls.server.hash.sha1': schemaString, + 'tls.server.hash.sha256': schemaString, + 'tls.server.issuer': schemaString, + 'tls.server.ja3s': schemaString, + 'tls.server.not_after': schemaDate, + 'tls.server.not_before': schemaDate, + 'tls.server.subject': schemaString, + 'tls.server.x509.alternative_names': schemaStringArray, + 'tls.server.x509.issuer.common_name': schemaStringArray, + 'tls.server.x509.issuer.country': schemaStringArray, + 'tls.server.x509.issuer.distinguished_name': schemaString, + 'tls.server.x509.issuer.locality': schemaStringArray, + 'tls.server.x509.issuer.organization': schemaStringArray, + 'tls.server.x509.issuer.organizational_unit': schemaStringArray, + 'tls.server.x509.issuer.state_or_province': schemaStringArray, + 'tls.server.x509.not_after': schemaDate, + 'tls.server.x509.not_before': schemaDate, + 'tls.server.x509.public_key_algorithm': schemaString, + 'tls.server.x509.public_key_curve': schemaString, + 'tls.server.x509.public_key_exponent': schemaStringOrNumber, + 'tls.server.x509.public_key_size': schemaStringOrNumber, + 'tls.server.x509.serial_number': schemaString, + 'tls.server.x509.signature_algorithm': schemaString, + 'tls.server.x509.subject.common_name': schemaStringArray, + 'tls.server.x509.subject.country': schemaStringArray, + 'tls.server.x509.subject.distinguished_name': schemaString, + 'tls.server.x509.subject.locality': schemaStringArray, + 'tls.server.x509.subject.organization': schemaStringArray, + 'tls.server.x509.subject.organizational_unit': schemaStringArray, + 'tls.server.x509.subject.state_or_province': schemaStringArray, + 'tls.server.x509.version_number': schemaString, + 'tls.version': schemaString, + 'tls.version_protocol': schemaString, + 'trace.id': schemaString, + 'transaction.id': schemaString, + 'url.domain': schemaString, + 'url.extension': schemaString, + 'url.fragment': schemaString, + 'url.full': schemaString, + 'url.original': schemaString, + 'url.password': schemaString, + 'url.path': schemaString, + 'url.port': schemaStringOrNumber, + 'url.query': schemaString, + 'url.registered_domain': schemaString, + 'url.scheme': schemaString, + 'url.subdomain': schemaString, + 'url.top_level_domain': schemaString, + 'url.username': schemaString, + 'user.changes.domain': schemaString, + 'user.changes.email': schemaString, + 'user.changes.full_name': schemaString, + 'user.changes.group.domain': schemaString, + 'user.changes.group.id': schemaString, + 'user.changes.group.name': schemaString, + 'user.changes.hash': schemaString, + 'user.changes.id': schemaString, + 'user.changes.name': schemaString, + 'user.changes.roles': schemaStringArray, + 'user.domain': schemaString, + 'user.effective.domain': schemaString, + 'user.effective.email': schemaString, + 'user.effective.full_name': schemaString, + 'user.effective.group.domain': schemaString, + 'user.effective.group.id': schemaString, + 'user.effective.group.name': schemaString, + 'user.effective.hash': schemaString, + 'user.effective.id': schemaString, + 'user.effective.name': schemaString, + 'user.effective.roles': schemaStringArray, + 'user.email': schemaString, + 'user.full_name': schemaString, + 'user.group.domain': schemaString, + 'user.group.id': schemaString, + 'user.group.name': schemaString, + 'user.hash': schemaString, + 'user.id': schemaString, + 'user.name': schemaString, + 'user.risk.calculated_level': schemaString, + 'user.risk.calculated_score': schemaNumber, + 'user.risk.calculated_score_norm': schemaNumber, + 'user.risk.static_level': schemaString, + 'user.risk.static_score': schemaNumber, + 'user.risk.static_score_norm': schemaNumber, + 'user.roles': schemaStringArray, + 'user.target.domain': schemaString, + 'user.target.email': schemaString, + 'user.target.full_name': schemaString, + 'user.target.group.domain': schemaString, + 'user.target.group.id': schemaString, + 'user.target.group.name': schemaString, + 'user.target.hash': schemaString, + 'user.target.id': schemaString, + 'user.target.name': schemaString, + 'user.target.roles': schemaStringArray, + 'user_agent.device.name': schemaString, + 'user_agent.name': schemaString, + 'user_agent.original': schemaString, + 'user_agent.os.family': schemaString, + 'user_agent.os.full': schemaString, + 'user_agent.os.kernel': schemaString, + 'user_agent.os.name': schemaString, + 'user_agent.os.platform': schemaString, + 'user_agent.os.type': schemaString, + 'user_agent.os.version': schemaString, + 'user_agent.version': schemaString, + 'vulnerability.category': schemaStringArray, + 'vulnerability.classification': schemaString, + 'vulnerability.description': schemaString, + 'vulnerability.enumeration': schemaString, + 'vulnerability.id': schemaString, + 'vulnerability.reference': schemaString, + 'vulnerability.report_id': schemaString, + 'vulnerability.scanner.vendor': schemaString, + 'vulnerability.score.base': schemaNumber, + 'vulnerability.score.environmental': schemaNumber, + 'vulnerability.score.temporal': schemaNumber, + 'vulnerability.score.version': schemaString, + 'vulnerability.severity': schemaString, }); // prettier-ignore diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/legacy_alert_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/legacy_alert_schema.ts index 107cdd65464bc..5db2dbc6e0d52 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/legacy_alert_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/legacy_alert_schema.ts @@ -25,19 +25,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; @@ -69,46 +69,34 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); const LegacyAlertRequired = rt.type({ }); const LegacyAlertOptional = rt.partial({ - ecs: rt.partial({ - version: schemaString, - }), - kibana: rt.partial({ - alert: rt.partial({ - risk_score: schemaNumber, - rule: rt.partial({ - author: schemaString, - created_at: schemaDate, - created_by: schemaString, - description: schemaString, - enabled: schemaString, - from: schemaString, - interval: schemaString, - license: schemaString, - note: schemaString, - references: schemaStringArray, - rule_id: schemaString, - rule_name_override: schemaString, - to: schemaString, - type: schemaString, - updated_at: schemaDate, - updated_by: schemaString, - version: schemaString, - }), - severity: schemaString, - suppression: rt.partial({ - docs_count: schemaStringOrNumber, - end: schemaDate, - start: schemaDate, - terms: rt.partial({ - field: schemaStringArray, - value: schemaStringArray, - }), - }), - system_status: schemaString, - workflow_reason: schemaString, - workflow_user: schemaString, - }), - }), + 'ecs.version': schemaString, + 'kibana.alert.risk_score': schemaNumber, + 'kibana.alert.rule.author': schemaString, + 'kibana.alert.rule.created_at': schemaDate, + 'kibana.alert.rule.created_by': schemaString, + 'kibana.alert.rule.description': schemaString, + 'kibana.alert.rule.enabled': schemaString, + 'kibana.alert.rule.from': schemaString, + 'kibana.alert.rule.interval': schemaString, + 'kibana.alert.rule.license': schemaString, + 'kibana.alert.rule.note': schemaString, + 'kibana.alert.rule.references': schemaStringArray, + 'kibana.alert.rule.rule_id': schemaString, + 'kibana.alert.rule.rule_name_override': schemaString, + 'kibana.alert.rule.to': schemaString, + 'kibana.alert.rule.type': schemaString, + 'kibana.alert.rule.updated_at': schemaDate, + 'kibana.alert.rule.updated_by': schemaString, + 'kibana.alert.rule.version': schemaString, + 'kibana.alert.severity': schemaString, + 'kibana.alert.suppression.docs_count': schemaStringOrNumber, + 'kibana.alert.suppression.end': schemaDate, + 'kibana.alert.suppression.start': schemaDate, + 'kibana.alert.suppression.terms.field': schemaStringArray, + 'kibana.alert.suppression.terms.value': schemaStringArray, + 'kibana.alert.system_status': schemaString, + 'kibana.alert.workflow_reason': schemaString, + 'kibana.alert.workflow_user': schemaString, }); // prettier-ignore diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/ml_anomaly_detection_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/ml_anomaly_detection_schema.ts index 2e5912bca84c2..591a51b2fec57 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/ml_anomaly_detection_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/ml_anomaly_detection_schema.ts @@ -25,19 +25,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; @@ -67,51 +67,43 @@ export const schemaGeoPoint = rt.union([ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const MlAnomalyDetectionAlertRequired = rt.type({ - kibana: rt.type({ - alert: rt.type({ - job_id: schemaString, - }), - }), + 'kibana.alert.job_id': schemaString, }); const MlAnomalyDetectionAlertOptional = rt.partial({ - kibana: rt.partial({ - alert: rt.partial({ - anomaly_score: schemaNumber, - anomaly_timestamp: schemaDate, + 'kibana.alert.anomaly_score': schemaNumber, + 'kibana.alert.anomaly_timestamp': schemaDate, + 'kibana.alert.is_interim': schemaBoolean, + 'kibana.alert.top_influencers': rt.array( + rt.partial({ + influencer_field_name: schemaString, + influencer_field_value: schemaString, + influencer_score: schemaNumber, + initial_influencer_score: schemaNumber, is_interim: schemaBoolean, - top_influencers: rt.array( - rt.partial({ - influencer_field_name: schemaString, - influencer_field_value: schemaString, - influencer_score: schemaNumber, - initial_influencer_score: schemaNumber, - is_interim: schemaBoolean, - job_id: schemaString, - timestamp: schemaDate, - }) - ), - top_records: rt.array( - rt.partial({ - actual: schemaNumber, - by_field_name: schemaString, - by_field_value: schemaString, - detector_index: schemaNumber, - field_name: schemaString, - function: schemaString, - initial_record_score: schemaNumber, - is_interim: schemaBoolean, - job_id: schemaString, - over_field_name: schemaString, - over_field_value: schemaString, - partition_field_name: schemaString, - partition_field_value: schemaString, - record_score: schemaNumber, - timestamp: schemaDate, - typical: schemaNumber, - }) - ), - }), - }), + job_id: schemaString, + timestamp: schemaDate, + }) + ), + 'kibana.alert.top_records': rt.array( + rt.partial({ + actual: schemaNumber, + by_field_name: schemaString, + by_field_value: schemaString, + detector_index: schemaNumber, + field_name: schemaString, + function: schemaString, + initial_record_score: schemaNumber, + is_interim: schemaBoolean, + job_id: schemaString, + over_field_name: schemaString, + over_field_value: schemaString, + partition_field_name: schemaString, + partition_field_value: schemaString, + record_score: schemaNumber, + timestamp: schemaDate, + typical: schemaNumber, + }) + ), }); // prettier-ignore diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_apm_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_apm_schema.ts index a3aaadbc076d7..b15fe189b7e25 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_apm_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_apm_schema.ts @@ -26,19 +26,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; @@ -70,42 +70,24 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); const ObservabilityApmAlertRequired = rt.type({ }); const ObservabilityApmAlertOptional = rt.partial({ - agent: rt.partial({ - name: schemaString, - }), - error: rt.partial({ - grouping_key: schemaString, - grouping_name: schemaString, - }), - kibana: rt.partial({ - alert: rt.partial({ - evaluation: rt.partial({ - threshold: schemaStringOrNumber, - value: schemaStringOrNumber, - values: schemaStringOrNumberArray, - }), - group: rt.array( - rt.partial({ - field: schemaString, - value: schemaString, - }) - ), - }), - }), - processor: rt.partial({ - event: schemaString, - }), - service: rt.partial({ - environment: schemaString, - language: rt.partial({ - name: schemaString, - }), - name: schemaString, - }), - transaction: rt.partial({ - name: schemaString, - type: schemaString, - }), + 'agent.name': schemaString, + 'error.grouping_key': schemaString, + 'error.grouping_name': schemaString, + 'kibana.alert.evaluation.threshold': schemaStringOrNumber, + 'kibana.alert.evaluation.value': schemaStringOrNumber, + 'kibana.alert.evaluation.values': schemaStringOrNumberArray, + 'kibana.alert.group': rt.array( + rt.partial({ + field: schemaString, + value: schemaString, + }) + ), + 'processor.event': schemaString, + 'service.environment': schemaString, + 'service.language.name': schemaString, + 'service.name': schemaString, + 'transaction.name': schemaString, + 'transaction.type': schemaString, }); // prettier-ignore diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_logs_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_logs_schema.ts index e77fa6c6ce679..62b23dcab24ee 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_logs_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_logs_schema.ts @@ -27,19 +27,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; @@ -71,21 +71,15 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); const ObservabilityLogsAlertRequired = rt.type({ }); const ObservabilityLogsAlertOptional = rt.partial({ - kibana: rt.partial({ - alert: rt.partial({ - evaluation: rt.partial({ - threshold: schemaStringOrNumber, - value: schemaStringOrNumber, - values: schemaStringOrNumberArray, - }), - group: rt.array( - rt.partial({ - field: schemaString, - value: schemaString, - }) - ), - }), - }), + 'kibana.alert.evaluation.threshold': schemaStringOrNumber, + 'kibana.alert.evaluation.value': schemaStringOrNumber, + 'kibana.alert.evaluation.values': schemaStringOrNumberArray, + 'kibana.alert.group': rt.array( + rt.partial({ + field: schemaString, + value: schemaString, + }) + ), }); // prettier-ignore diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_metrics_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_metrics_schema.ts index eda82a6a8bf8e..3806f2d096bd2 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_metrics_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_metrics_schema.ts @@ -27,19 +27,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; @@ -71,21 +71,15 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); const ObservabilityMetricsAlertRequired = rt.type({ }); const ObservabilityMetricsAlertOptional = rt.partial({ - kibana: rt.partial({ - alert: rt.partial({ - evaluation: rt.partial({ - threshold: schemaStringOrNumber, - value: schemaStringOrNumber, - values: schemaStringOrNumberArray, - }), - group: rt.array( - rt.partial({ - field: schemaString, - value: schemaString, - }) - ), - }), - }), + 'kibana.alert.evaluation.threshold': schemaStringOrNumber, + 'kibana.alert.evaluation.value': schemaStringOrNumber, + 'kibana.alert.evaluation.values': schemaStringOrNumberArray, + 'kibana.alert.group': rt.array( + rt.partial({ + field: schemaString, + value: schemaString, + }) + ), }); // prettier-ignore diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_slo_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_slo_schema.ts index 071c34e117932..62ad6c82c6391 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_slo_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_slo_schema.ts @@ -26,19 +26,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; @@ -70,26 +70,18 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); const ObservabilitySloAlertRequired = rt.type({ }); const ObservabilitySloAlertOptional = rt.partial({ - kibana: rt.partial({ - alert: rt.partial({ - evaluation: rt.partial({ - threshold: schemaStringOrNumber, - value: schemaStringOrNumber, - values: schemaStringOrNumberArray, - }), - group: rt.array( - rt.partial({ - field: schemaString, - value: schemaString, - }) - ), - }), - }), - slo: rt.partial({ - id: schemaString, - instanceId: schemaString, - revision: schemaStringOrNumber, - }), + 'kibana.alert.evaluation.threshold': schemaStringOrNumber, + 'kibana.alert.evaluation.value': schemaStringOrNumber, + 'kibana.alert.evaluation.values': schemaStringOrNumberArray, + 'kibana.alert.group': rt.array( + rt.partial({ + field: schemaString, + value: schemaString, + }) + ), + 'slo.id': schemaString, + 'slo.instanceId': schemaString, + 'slo.revision': schemaStringOrNumber, }); // prettier-ignore diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts index 5e4561f5728a6..12a54768d71e4 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts @@ -26,19 +26,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; @@ -70,63 +70,29 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); const ObservabilityUptimeAlertRequired = rt.type({ }); const ObservabilityUptimeAlertOptional = rt.partial({ - agent: rt.partial({ - name: schemaString, - }), - anomaly: rt.partial({ - bucket_span: rt.partial({ - minutes: schemaString, - }), - start: schemaDate, - }), - error: rt.partial({ - message: schemaString, - }), - kibana: rt.partial({ - alert: rt.partial({ - evaluation: rt.partial({ - threshold: schemaStringOrNumber, - value: schemaStringOrNumber, - values: schemaStringOrNumberArray, - }), - group: rt.array( - rt.partial({ - field: schemaString, - value: schemaString, - }) - ), - }), - }), - monitor: rt.partial({ - id: schemaString, - name: schemaString, - type: schemaString, - }), - observer: rt.partial({ - geo: rt.partial({ - name: schemaString, - }), - }), - tls: rt.partial({ - server: rt.partial({ - hash: rt.partial({ - sha256: schemaString, - }), - x509: rt.partial({ - issuer: rt.partial({ - common_name: schemaString, - }), - not_after: schemaDate, - not_before: schemaDate, - subject: rt.partial({ - common_name: schemaString, - }), - }), - }), - }), - url: rt.partial({ - full: schemaString, - }), + 'agent.name': schemaString, + 'anomaly.bucket_span.minutes': schemaString, + 'anomaly.start': schemaDate, + 'error.message': schemaString, + 'kibana.alert.evaluation.threshold': schemaStringOrNumber, + 'kibana.alert.evaluation.value': schemaStringOrNumber, + 'kibana.alert.evaluation.values': schemaStringOrNumberArray, + 'kibana.alert.group': rt.array( + rt.partial({ + field: schemaString, + value: schemaString, + }) + ), + 'monitor.id': schemaString, + 'monitor.name': schemaString, + 'monitor.type': schemaString, + 'observer.geo.name': schemaString, + 'tls.server.hash.sha256': schemaString, + 'tls.server.x509.issuer.common_name': schemaString, + 'tls.server.x509.not_after': schemaDate, + 'tls.server.x509.not_before': schemaDate, + 'tls.server.x509.subject.common_name': schemaString, + 'url.full': schemaString, }); // prettier-ignore diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts index 03124f6bef160..e27f8c1c8157b 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts @@ -27,19 +27,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; @@ -70,277 +70,133 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const SecurityAlertRequired = rt.type({ '@timestamp': schemaDate, - kibana: rt.type({ - alert: rt.type({ - ancestors: rt.array( - rt.type({ - depth: schemaStringOrNumber, - id: schemaString, - index: schemaString, - type: schemaString, - }) - ), + 'kibana.alert.ancestors': rt.array( + rt.type({ depth: schemaStringOrNumber, - instance: rt.type({ - id: schemaString, - }), - original_event: rt.type({ - action: schemaString, - category: schemaStringArray, - created: schemaDate, - dataset: schemaString, - id: schemaString, - ingested: schemaDate, - kind: schemaString, - module: schemaString, - original: schemaString, - outcome: schemaString, - provider: schemaString, - sequence: schemaStringOrNumber, - type: schemaStringArray, - }), - original_time: schemaDate, - rule: rt.type({ - category: schemaString, - consumer: schemaString, - false_positives: schemaStringArray, - max_signals: schemaStringOrNumberArray, - name: schemaString, - producer: schemaString, - revision: schemaStringOrNumber, - rule_type_id: schemaString, - threat: rt.type({ - framework: schemaString, - tactic: rt.type({ - id: schemaString, - name: schemaString, - reference: schemaString, - }), - technique: rt.type({ - id: schemaString, - name: schemaString, - reference: schemaString, - subtechnique: rt.type({ - id: schemaString, - name: schemaString, - reference: schemaString, - }), - }), - }), - uuid: schemaString, - }), - status: schemaString, - uuid: schemaString, - }), - space_ids: schemaStringArray, - }), + id: schemaString, + index: schemaString, + type: schemaString, + }) + ), + 'kibana.alert.depth': schemaStringOrNumber, + 'kibana.alert.instance.id': schemaString, + 'kibana.alert.original_event.action': schemaString, + 'kibana.alert.original_event.category': schemaStringArray, + 'kibana.alert.original_event.created': schemaDate, + 'kibana.alert.original_event.dataset': schemaString, + 'kibana.alert.original_event.id': schemaString, + 'kibana.alert.original_event.ingested': schemaDate, + 'kibana.alert.original_event.kind': schemaString, + 'kibana.alert.original_event.module': schemaString, + 'kibana.alert.original_event.original': schemaString, + 'kibana.alert.original_event.outcome': schemaString, + 'kibana.alert.original_event.provider': schemaString, + 'kibana.alert.original_event.sequence': schemaStringOrNumber, + 'kibana.alert.original_event.type': schemaStringArray, + 'kibana.alert.original_time': schemaDate, + 'kibana.alert.rule.category': schemaString, + 'kibana.alert.rule.consumer': schemaString, + 'kibana.alert.rule.false_positives': schemaStringArray, + 'kibana.alert.rule.max_signals': schemaStringOrNumberArray, + 'kibana.alert.rule.name': schemaString, + 'kibana.alert.rule.producer': schemaString, + 'kibana.alert.rule.revision': schemaStringOrNumber, + 'kibana.alert.rule.rule_type_id': schemaString, + 'kibana.alert.rule.threat.framework': schemaString, + 'kibana.alert.rule.threat.tactic.id': schemaString, + 'kibana.alert.rule.threat.tactic.name': schemaString, + 'kibana.alert.rule.threat.tactic.reference': schemaString, + 'kibana.alert.rule.threat.technique.id': schemaString, + 'kibana.alert.rule.threat.technique.name': schemaString, + 'kibana.alert.rule.threat.technique.reference': schemaString, + 'kibana.alert.rule.threat.technique.subtechnique.id': schemaString, + 'kibana.alert.rule.threat.technique.subtechnique.name': schemaString, + 'kibana.alert.rule.threat.technique.subtechnique.reference': schemaString, + 'kibana.alert.rule.uuid': schemaString, + 'kibana.alert.status': schemaString, + 'kibana.alert.uuid': schemaString, + 'kibana.space_ids': schemaStringArray, }); const SecurityAlertOptional = rt.partial({ - ecs: rt.partial({ - version: schemaString, - }), - event: rt.partial({ - action: schemaString, - kind: schemaString, - }), - kibana: rt.partial({ - alert: rt.partial({ - action_group: schemaString, - ancestors: rt.partial({ - rule: schemaString, - }), - building_block_type: schemaString, - case_ids: schemaStringArray, - duration: rt.partial({ - us: schemaStringOrNumber, - }), - end: schemaDate, - flapping: schemaBoolean, - flapping_history: schemaBooleanArray, - group: rt.partial({ - id: schemaString, - index: schemaNumber, - }), - last_detected: schemaDate, - maintenance_window_ids: schemaStringArray, - new_terms: schemaStringArray, - original_event: rt.partial({ - agent_id_status: schemaString, - code: schemaString, - duration: schemaString, - end: schemaDate, - hash: schemaString, - reason: schemaString, - reference: schemaString, - risk_score: schemaNumber, - risk_score_norm: schemaNumber, - severity: schemaStringOrNumber, - start: schemaDate, - timezone: schemaString, - url: schemaString, - }), - reason: schemaString, - risk_score: schemaNumber, - rule: rt.partial({ - author: schemaString, - building_block_type: schemaString, - created_at: schemaDate, - created_by: schemaString, - description: schemaString, - enabled: schemaString, - execution: rt.partial({ - uuid: schemaString, - }), - from: schemaString, - immutable: schemaStringArray, - interval: schemaString, - license: schemaString, - note: schemaString, - parameters: schemaUnknown, - references: schemaStringArray, - rule_id: schemaString, - rule_name_override: schemaString, - tags: schemaStringArray, - timeline_id: schemaStringArray, - timeline_title: schemaStringArray, - timestamp_override: schemaString, - to: schemaString, - type: schemaString, - updated_at: schemaDate, - updated_by: schemaString, - version: schemaString, - }), - severity: schemaString, - start: schemaDate, - suppression: rt.partial({ - docs_count: schemaStringOrNumber, - end: schemaDate, - start: schemaDate, - terms: rt.partial({ - field: schemaStringArray, - value: schemaStringArray, - }), - }), - system_status: schemaString, - threshold_result: rt.partial({ - count: schemaStringOrNumber, - from: schemaDate, - terms: rt.array( - rt.partial({ - field: schemaString, - value: schemaString, - }) - ), - }), - time_range: schemaDateRange, - url: schemaString, - workflow_reason: schemaString, - workflow_status: schemaString, - workflow_tags: schemaStringArray, - workflow_user: schemaString, - }), - version: schemaString, - }), - signal: rt.partial({ - ancestors: rt.partial({ - depth: schemaUnknown, - id: schemaUnknown, - index: schemaUnknown, - type: schemaUnknown, - }), - depth: schemaUnknown, - group: rt.partial({ - id: schemaUnknown, - index: schemaUnknown, - }), - original_event: rt.partial({ - action: schemaUnknown, - category: schemaUnknown, - code: schemaUnknown, - created: schemaUnknown, - dataset: schemaUnknown, - duration: schemaUnknown, - end: schemaUnknown, - hash: schemaUnknown, - id: schemaUnknown, - kind: schemaUnknown, - module: schemaUnknown, - outcome: schemaUnknown, - provider: schemaUnknown, - reason: schemaUnknown, - risk_score: schemaUnknown, - risk_score_norm: schemaUnknown, - sequence: schemaUnknown, - severity: schemaUnknown, - start: schemaUnknown, - timezone: schemaUnknown, - type: schemaUnknown, - }), - original_time: schemaUnknown, - reason: schemaUnknown, - rule: rt.partial({ - author: schemaUnknown, - building_block_type: schemaUnknown, - created_at: schemaUnknown, - created_by: schemaUnknown, - description: schemaUnknown, - enabled: schemaUnknown, - false_positives: schemaUnknown, - from: schemaUnknown, - id: schemaUnknown, - immutable: schemaUnknown, - interval: schemaUnknown, - license: schemaUnknown, - max_signals: schemaUnknown, - name: schemaUnknown, - note: schemaUnknown, - references: schemaUnknown, - risk_score: schemaUnknown, - rule_id: schemaUnknown, - rule_name_override: schemaUnknown, - severity: schemaUnknown, - tags: schemaUnknown, - threat: rt.partial({ - framework: schemaUnknown, - tactic: rt.partial({ - id: schemaUnknown, - name: schemaUnknown, - reference: schemaUnknown, - }), - technique: rt.partial({ - id: schemaUnknown, - name: schemaUnknown, - reference: schemaUnknown, - subtechnique: rt.partial({ - id: schemaUnknown, - name: schemaUnknown, - reference: schemaUnknown, - }), - }), - }), - timeline_id: schemaUnknown, - timeline_title: schemaUnknown, - timestamp_override: schemaUnknown, - to: schemaUnknown, - type: schemaUnknown, - updated_at: schemaUnknown, - updated_by: schemaUnknown, - version: schemaUnknown, - }), - status: schemaUnknown, - threshold_result: rt.partial({ - cardinality: rt.partial({ - field: schemaUnknown, - value: schemaUnknown, - }), - count: schemaUnknown, - from: schemaUnknown, - terms: rt.partial({ - field: schemaUnknown, - value: schemaUnknown, - }), - }), - }), + 'ecs.version': schemaString, + 'event.action': schemaString, + 'event.kind': schemaString, + 'kibana.alert.action_group': schemaString, + 'kibana.alert.ancestors.rule': schemaString, + 'kibana.alert.building_block_type': schemaString, + 'kibana.alert.case_ids': schemaStringArray, + 'kibana.alert.duration.us': schemaStringOrNumber, + 'kibana.alert.end': schemaDate, + 'kibana.alert.flapping': schemaBoolean, + 'kibana.alert.flapping_history': schemaBooleanArray, + 'kibana.alert.group.id': schemaString, + 'kibana.alert.group.index': schemaNumber, + 'kibana.alert.last_detected': schemaDate, + 'kibana.alert.maintenance_window_ids': schemaStringArray, + 'kibana.alert.new_terms': schemaStringArray, + 'kibana.alert.original_event.agent_id_status': schemaString, + 'kibana.alert.original_event.code': schemaString, + 'kibana.alert.original_event.duration': schemaString, + 'kibana.alert.original_event.end': schemaDate, + 'kibana.alert.original_event.hash': schemaString, + 'kibana.alert.original_event.reason': schemaString, + 'kibana.alert.original_event.reference': schemaString, + 'kibana.alert.original_event.risk_score': schemaNumber, + 'kibana.alert.original_event.risk_score_norm': schemaNumber, + 'kibana.alert.original_event.severity': schemaStringOrNumber, + 'kibana.alert.original_event.start': schemaDate, + 'kibana.alert.original_event.timezone': schemaString, + 'kibana.alert.original_event.url': schemaString, + 'kibana.alert.reason': schemaString, + 'kibana.alert.risk_score': schemaNumber, + 'kibana.alert.rule.author': schemaString, + 'kibana.alert.rule.building_block_type': schemaString, + 'kibana.alert.rule.created_at': schemaDate, + 'kibana.alert.rule.created_by': schemaString, + 'kibana.alert.rule.description': schemaString, + 'kibana.alert.rule.enabled': schemaString, + 'kibana.alert.rule.execution.uuid': schemaString, + 'kibana.alert.rule.from': schemaString, + 'kibana.alert.rule.immutable': schemaStringArray, + 'kibana.alert.rule.interval': schemaString, + 'kibana.alert.rule.license': schemaString, + 'kibana.alert.rule.note': schemaString, + 'kibana.alert.rule.parameters': schemaUnknown, + 'kibana.alert.rule.references': schemaStringArray, + 'kibana.alert.rule.rule_id': schemaString, + 'kibana.alert.rule.rule_name_override': schemaString, + 'kibana.alert.rule.tags': schemaStringArray, + 'kibana.alert.rule.timeline_id': schemaStringArray, + 'kibana.alert.rule.timeline_title': schemaStringArray, + 'kibana.alert.rule.timestamp_override': schemaString, + 'kibana.alert.rule.to': schemaString, + 'kibana.alert.rule.type': schemaString, + 'kibana.alert.rule.updated_at': schemaDate, + 'kibana.alert.rule.updated_by': schemaString, + 'kibana.alert.rule.version': schemaString, + 'kibana.alert.severity': schemaString, + 'kibana.alert.start': schemaDate, + 'kibana.alert.suppression.docs_count': schemaStringOrNumber, + 'kibana.alert.suppression.end': schemaDate, + 'kibana.alert.suppression.start': schemaDate, + 'kibana.alert.suppression.terms.field': schemaStringArray, + 'kibana.alert.suppression.terms.value': schemaStringArray, + 'kibana.alert.system_status': schemaString, + 'kibana.alert.threshold_result.count': schemaStringOrNumber, + 'kibana.alert.threshold_result.from': schemaDate, + 'kibana.alert.threshold_result.terms': rt.array( + rt.partial({ + field: schemaString, + value: schemaString, + }) + ), + 'kibana.alert.time_range': schemaDateRange, + 'kibana.alert.url': schemaString, + 'kibana.alert.workflow_reason': schemaString, + 'kibana.alert.workflow_status': schemaString, + 'kibana.alert.workflow_tags': schemaStringArray, + 'kibana.alert.workflow_user': schemaString, + 'kibana.version': schemaString, tags: schemaStringArray, }); diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts index 362d64de05d98..a051eda6519c1 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts @@ -25,19 +25,19 @@ export const IsoDateString = new rt.Type( rt.identity ); export type IsoDateStringC = typeof IsoDateString; -export const schemaDate = IsoDateString; -export const schemaDateArray = rt.array(IsoDateString); -export const schemaDateRange = rt.partial({ - gte: schemaDate, - lte: schemaDate, -}); -export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaUnknown = rt.unknown; export const schemaUnknownArray = rt.array(rt.unknown); export const schemaString = rt.string; export const schemaStringArray = rt.array(schemaString); export const schemaNumber = rt.number; export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); export const schemaBoolean = rt.boolean; @@ -69,15 +69,9 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); const StackAlertRequired = rt.type({ }); const StackAlertOptional = rt.partial({ - kibana: rt.partial({ - alert: rt.partial({ - evaluation: rt.partial({ - conditions: schemaString, - value: schemaString, - }), - title: schemaString, - }), - }), + 'kibana.alert.evaluation.conditions': schemaString, + 'kibana.alert.evaluation.value': schemaString, + 'kibana.alert.title': schemaString, }); // prettier-ignore diff --git a/x-pack/plugins/alerting/server/alert/alert.test.ts b/x-pack/plugins/alerting/server/alert/alert.test.ts index c4db2189e6d26..a0d0ef25f9a86 100644 --- a/x-pack/plugins/alerting/server/alert/alert.test.ts +++ b/x-pack/plugins/alerting/server/alert/alert.test.ts @@ -695,6 +695,7 @@ describe('isFilteredOut', () => { _id: '1', _index: '.alerts', '@timestamp': '', + // @ts-expect-error kibana: { alert: { instance: { id: 'a' }, diff --git a/x-pack/plugins/alerting/server/alert/alert.ts b/x-pack/plugins/alerting/server/alert/alert.ts index b05c3bea22cc3..ee8c48a18447b 100644 --- a/x-pack/plugins/alerting/server/alert/alert.ts +++ b/x-pack/plugins/alerting/server/alert/alert.ts @@ -6,8 +6,9 @@ */ import { v4 as uuidV4 } from 'uuid'; -import { isEmpty } from 'lodash'; +import { get, isEmpty } from 'lodash'; import { MutableAlertInstanceMeta } from '@kbn/alerting-state-types'; +import { ALERT_UUID } from '@kbn/rule-data-utils'; import { AlertHit, CombinedSummarizedAlerts } from '../types'; import { AlertInstanceMeta, @@ -298,10 +299,10 @@ export class Alert< // // Related issue: https://github.com/elastic/kibana/issues/144862 - return !summarizedAlerts.all.data.some( - (alert: AlertHit) => - alert?.kibana?.alert?.uuid === this.getId() || alert?.kibana?.alert?.uuid === this.getUuid() - ); + return !summarizedAlerts.all.data.some((alert: AlertHit) => { + const alertUuid = get(alert, ALERT_UUID); + return alertUuid === this.getId() || alertUuid === this.getUuid(); + }); } setMaintenanceWindowIds(maintenanceWindowIds: string[] = []) { diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts index 6921ad7a99402..c870a0c52d13b 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts @@ -12,6 +12,36 @@ import { RecoveredActionGroup, RuleAlertData, } from '../types'; +import { + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_END, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_RULE_CATEGORY, + ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + ALERT_RULE_PRODUCER, + ALERT_RULE_REVISION, + ALERT_RULE_TAGS, + ALERT_RULE_TYPE_ID, + ALERT_RULE_UUID, + ALERT_START, + ALERT_STATUS, + ALERT_TIME_RANGE, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + EVENT_ACTION, + EVENT_KIND, + SPACE_IDS, + TAGS, + TIMESTAMP, + VERSION, +} from '@kbn/rule-data-utils'; import * as LegacyAlertsClientModule from './legacy_alerts_client'; import { LegacyAlertsClient } from './legacy_alerts_client'; import { Alert } from '../alert/alert'; @@ -21,7 +51,7 @@ import { legacyAlertsClientMock } from './legacy_alerts_client.mock'; import { keys, range } from 'lodash'; import { alertingEventLoggerMock } from '../lib/alerting_event_logger/alerting_event_logger.mock'; import { ruleRunMetricsStoreMock } from '../lib/rule_run_metrics_store.mock'; -import { expandFlattenedAlert } from './lib/get_summarized_alerts_query'; +import { expandFlattenedAlert } from './lib'; import { alertRuleData, getExpectedQueryByExecutionUuid, @@ -81,6 +111,149 @@ const mockCreate = jest.fn().mockImplementation(() => ({ })); const mockSetContext = jest.fn(); +const trackedAlert1Raw = { + state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '0' }, + meta: { + flapping: false, + flappingHistory: [true], + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, + uuid: 'abc', + }, +}; +const trackedAlert1 = new Alert('1', trackedAlert1Raw); +const trackedAlert2Raw = { + state: { foo: true, start: '2023-03-28T02:27:28.159Z', duration: '36000000000000' }, + meta: { + flapping: false, + flappingHistory: [true, false, false], + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, + uuid: 'def', + }, +}; +const trackedAlert2 = new Alert('2', trackedAlert2Raw); +const trackedRecovered3 = new Alert('3', { + state: { foo: false }, + meta: { + flapping: false, + flappingHistory: [true, false, false], + uuid: 'xyz', + }, +}); + +const fetchedAlert1 = { + [TIMESTAMP]: '2023-03-28T12:27:28.159Z', + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_DURATION]: '0', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true], + [ALERT_INSTANCE_ID]: '1', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_STATUS]: 'active', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [ALERT_UUID]: 'abc', + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.8.0', + [TAGS]: ['rule-', '-tags'], +}; + +const fetchedAlert2 = { + [TIMESTAMP]: '2023-03-28T13:27:28.159Z', + [EVENT_ACTION]: 'active', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_DURATION]: '36000000000000', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true, false], + [ALERT_INSTANCE_ID]: '2', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_START]: '2023-03-28T02:27:28.159Z', + [ALERT_STATUS]: 'active', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T02:27:28.159Z' }, + [ALERT_UUID]: 'def', + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.8.0', + [TAGS]: ['rule-', '-tags'], +}; + +const getNewIndexedAlertDoc = (overrides = {}) => ({ + [TIMESTAMP]: date, + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_DURATION]: '0', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true], + [ALERT_INSTANCE_ID]: '1', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_START]: date, + [ALERT_STATUS]: 'active', + [ALERT_TIME_RANGE]: { gte: date }, + [ALERT_UUID]: 'uuid', + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...overrides, +}); + +const getOngoingIndexedAlertDoc = (overrides = {}) => ({ + ...getNewIndexedAlertDoc(), + [EVENT_ACTION]: 'active', + [ALERT_DURATION]: '36000000000000', + [ALERT_FLAPPING_HISTORY]: [true, false], + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + ...overrides, +}); + +const getRecoveredIndexedAlertDoc = (overrides = {}) => ({ + ...getNewIndexedAlertDoc(), + [EVENT_ACTION]: 'close', + [ALERT_DURATION]: '36000000000000', + [ALERT_ACTION_GROUP]: 'recovered', + [ALERT_FLAPPING_HISTORY]: [true, true], + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_END]: date, + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z', lte: date }, + [ALERT_STATUS]: 'recovered', + ...overrides, +}); + describe('Alerts Client', () => { let alertsClientParams: AlertsClientParams; let processAndLogAlertsOpts: ProcessAndLogAlertsOpts; @@ -180,36 +353,8 @@ describe('Alerts Client', () => { test('should query for alert UUIDs if they exist', async () => { mockLegacyAlertsClient.getTrackedAlerts.mockImplementation(() => ({ - active: { - '1': new Alert('1', { - state: { foo: true }, - meta: { - flapping: false, - flappingHistory: [true, false], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'abc', - }, - }), - '2': new Alert('2', { - state: { foo: false }, - meta: { - flapping: false, - flappingHistory: [true, false, false], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'def', - }, - }), - }, - recovered: { - '3': new Alert('3', { - state: { foo: false }, - meta: { - flapping: false, - flappingHistory: [true, false, false], - uuid: 'xyz', - }, - }), - }, + active: { '1': trackedAlert1, '2': trackedAlert2 }, + recovered: { '3': trackedRecovered3 }, })); const spy = jest .spyOn(LegacyAlertsClientModule, 'LegacyAlertsClient') @@ -292,17 +437,7 @@ describe('Alerts Client', () => { throw new Error('search failed!'); }); mockLegacyAlertsClient.getTrackedAlerts.mockImplementation(() => ({ - active: { - '1': new Alert('1', { - state: { foo: true }, - meta: { - flapping: false, - flappingHistory: [true, false], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'abc', - }, - }), - }, + active: { '1': trackedAlert1 }, recovered: {}, })); const spy = jest @@ -384,170 +519,99 @@ describe('Alerts Client', () => { create: { _id: uuid1, ...(useDataStreamForAlerts ? {} : { require_alias: true }) }, }, // new alert doc + getNewIndexedAlertDoc({ [ALERT_UUID]: uuid1 }), { - '@timestamp': date, - event: { - action: 'open', - kind: 'signal', + create: { _id: uuid2, ...(useDataStreamForAlerts ? {} : { require_alias: true }) }, + }, + // new alert doc + getNewIndexedAlertDoc({ [ALERT_UUID]: uuid2, [ALERT_INSTANCE_ID]: '2' }), + ], + }); + }); + + test('should update ongoing alerts in existing index', async () => { + clusterClient.search.mockResolvedValue({ + took: 10, + timed_out: false, + _shards: { failed: 0, successful: 1, total: 1, skipped: 0 }, + hits: { + total: { relation: 'eq', value: 1 }, + hits: [ + { + _id: 'abc', + _index: '.internal.alerts-test.alerts-default-000001', + _seq_no: 41, + _primary_term: 665, + _source: fetchedAlert1, }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: date, - status: 'active', - time_range: { - gte: date, - }, - uuid: uuid1, - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', + ], + }, + }); + const alertsClient = new AlertsClient<{}, {}, {}, 'default', 'recovered'>( + alertsClientParams + ); + + await alertsClient.initializeExecution({ + maxAlerts, + ruleLabel: `test: rule-name`, + flappingSettings: DEFAULT_FLAPPING_SETTINGS, + activeAlertsFromState: { + '1': trackedAlert1Raw, + }, + recoveredAlertsFromState: {}, + }); + + // Report 1 new alert and 1 active alert + const alertExecutorService = alertsClient.factory(); + alertExecutorService.create('1').scheduleActions('default'); + alertExecutorService.create('2').scheduleActions('default'); + + alertsClient.processAndLogAlerts(processAndLogAlertsOpts); + + await alertsClient.persistAlerts(); + + const { alertsToReturn } = alertsClient.getAlertsToSerialize(); + const uuid2 = alertsToReturn['2'].meta?.uuid; + + expect(clusterClient.bulk).toHaveBeenCalledWith({ + index: '.alerts-test.alerts-default', + refresh: true, + require_alias: !useDataStreamForAlerts, + body: [ + { + index: { + _id: 'abc', + _index: '.internal.alerts-test.alerts-default-000001', + if_seq_no: 41, + if_primary_term: 665, + require_alias: false, }, - tags: ['rule-', '-tags'], }, + // ongoing alert doc + getOngoingIndexedAlertDoc({ [ALERT_UUID]: 'abc' }), { create: { _id: uuid2, ...(useDataStreamForAlerts ? {} : { require_alias: true }) }, }, // new alert doc - { - '@timestamp': date, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '2', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: date, - status: 'active', - time_range: { - gte: date, - }, - uuid: uuid2, - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], - }, + getNewIndexedAlertDoc({ [ALERT_UUID]: uuid2, [ALERT_INSTANCE_ID]: '2' }), ], }); }); - test('should update ongoing alerts in existing index', async () => { + test('should update unflattened ongoing alerts in existing index', async () => { clusterClient.search.mockResolvedValue({ took: 10, timed_out: false, _shards: { failed: 0, successful: 1, total: 1, skipped: 0 }, hits: { - total: { - relation: 'eq', - value: 1, - }, + total: { relation: 'eq', value: 1 }, hits: [ { _id: 'abc', _index: '.internal.alerts-test.alerts-default-000001', _seq_no: 41, _primary_term: 665, - _source: { - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abc', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.0', - }, - tags: ['rule-', '-tags'], - }, + _source: expandFlattenedAlert(fetchedAlert1), }, ], }, @@ -561,16 +625,7 @@ describe('Alerts Client', () => { ruleLabel: `test: rule-name`, flappingSettings: DEFAULT_FLAPPING_SETTINGS, activeAlertsFromState: { - '1': { - state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '0' }, - meta: { - flapping: false, - flappingHistory: [true], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'abc', - }, - }, + '1': trackedAlert1Raw, }, recoveredAlertsFromState: {}, }); @@ -603,110 +658,50 @@ describe('Alerts Client', () => { }, // ongoing alert doc { - '@timestamp': date, - event: { - action: 'active', - kind: 'signal', - }, + event: { kind: 'signal' }, kibana: { alert: { - action_group: 'default', - duration: { - us: '36000000000000', - }, - flapping: false, - flapping_history: [true, false], - instance: { - id: '1', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, + instance: { id: '1' }, start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, uuid: 'abc', - workflow_status: 'open', }, - space_ids: ['default'], - version: '8.9.0', }, - tags: ['rule-', '-tags'], + [TIMESTAMP]: date, + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_DURATION]: '36000000000000', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true, false], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_STATUS]: 'active', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }, { create: { _id: uuid2, ...(useDataStreamForAlerts ? {} : { require_alias: true }) }, }, // new alert doc - { - '@timestamp': date, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '2', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: date, - status: 'active', - time_range: { - gte: date, - }, - uuid: uuid2, - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], - }, + getNewIndexedAlertDoc({ [ALERT_UUID]: uuid2, [ALERT_INSTANCE_ID]: '2' }), ], }); }); test('should not update ongoing alerts in existing index when they are not in the processed alerts', async () => { const activeAlert = { - state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '0' }, + state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '36000000000000' }, meta: { flapping: false, flappingHistory: [true, false], @@ -730,60 +725,12 @@ describe('Alerts Client', () => { timed_out: false, _shards: { failed: 0, successful: 1, total: 1, skipped: 0 }, hits: { - total: { - relation: 'eq', - value: 1, - }, + total: { relation: 'eq', value: 1 }, hits: [ { _id: 'abc', _index: '.internal.alerts-test.alerts-default-000001', - _source: { - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abc', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.0', - }, - tags: ['rule-', '-tags'], - }, + _source: fetchedAlert1, }, ], }, @@ -832,53 +779,7 @@ describe('Alerts Client', () => { }, }, // ongoing alert doc - { - '@timestamp': date, - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true, false], - instance: { - id: '1', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abc', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], - }, + getOngoingIndexedAlertDoc({ [ALERT_UUID]: 'abc' }), ], }); }); @@ -889,114 +790,21 @@ describe('Alerts Client', () => { timed_out: false, _shards: { failed: 0, successful: 1, total: 1, skipped: 0 }, hits: { - total: { - relation: 'eq', - value: 1, - }, + total: { relation: 'eq', value: 2 }, hits: [ { _id: 'abc', _index: '.internal.alerts-test.alerts-default-000001', _seq_no: 41, _primary_term: 665, - _source: { - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abc', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.0', - }, - tags: ['rule-', '-tags'], - }, + _source: fetchedAlert1, }, { _id: 'def', _index: '.internal.alerts-test.alerts-default-000002', _seq_no: 42, _primary_term: 666, - _source: { - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '36000000000000', - }, - flapping: false, - flapping_history: [true, false], - instance: { - id: '2', - }, - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T02:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T02:27:28.159Z', - }, - uuid: 'def', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.0', - }, - tags: ['rule-', '-tags'], - }, + _source: fetchedAlert2, }, ], }, @@ -1010,26 +818,103 @@ describe('Alerts Client', () => { ruleLabel: `test: rule-name`, flappingSettings: DEFAULT_FLAPPING_SETTINGS, activeAlertsFromState: { - '1': { - state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '0' }, - meta: { - flapping: false, - flappingHistory: [true], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'abc', + '1': trackedAlert1Raw, + '2': trackedAlert2Raw, + }, + recoveredAlertsFromState: {}, + }); + + // Report 1 new alert and 1 active alert, recover 1 alert + const alertExecutorService = alertsClient.factory(); + alertExecutorService.create('2').scheduleActions('default'); + alertExecutorService.create('3').scheduleActions('default'); + + alertsClient.processAndLogAlerts(processAndLogAlertsOpts); + + await alertsClient.persistAlerts(); + + const { alertsToReturn } = alertsClient.getAlertsToSerialize(); + const uuid3 = alertsToReturn['3'].meta?.uuid; + + expect(clusterClient.bulk).toHaveBeenCalledWith({ + index: '.alerts-test.alerts-default', + refresh: true, + require_alias: !useDataStreamForAlerts, + body: [ + { + index: { + _id: 'def', + _index: '.internal.alerts-test.alerts-default-000002', + if_seq_no: 42, + if_primary_term: 666, + require_alias: false, }, }, - '2': { - state: { foo: true, start: '2023-03-28T02:27:28.159Z', duration: '36000000000000' }, - meta: { - flapping: false, - flappingHistory: [true, false], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'def', + // ongoing alert doc + getOngoingIndexedAlertDoc({ + [ALERT_UUID]: 'def', + [ALERT_INSTANCE_ID]: '2', + [ALERT_FLAPPING_HISTORY]: [true, false, false, false], + [ALERT_DURATION]: '72000000000000', + [ALERT_START]: '2023-03-28T02:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T02:27:28.159Z' }, + }), + { + create: { _id: uuid3, ...(useDataStreamForAlerts ? {} : { require_alias: true }) }, + }, + // new alert doc + getNewIndexedAlertDoc({ [ALERT_UUID]: uuid3, [ALERT_INSTANCE_ID]: '3' }), + { + index: { + _id: 'abc', + _index: '.internal.alerts-test.alerts-default-000001', + if_seq_no: 41, + if_primary_term: 665, + require_alias: false, }, }, + // recovered alert doc + getRecoveredIndexedAlertDoc({ [ALERT_UUID]: 'abc' }), + ], + }); + }); + + test('should recover unflattened recovered alerts in existing index', async () => { + clusterClient.search.mockResolvedValue({ + took: 10, + timed_out: false, + _shards: { failed: 0, successful: 1, total: 1, skipped: 0 }, + hits: { + total: { relation: 'eq', value: 2 }, + hits: [ + { + _id: 'abc', + _index: '.internal.alerts-test.alerts-default-000001', + _seq_no: 41, + _primary_term: 665, + _source: expandFlattenedAlert(fetchedAlert1), + }, + { + _id: 'def', + _index: '.internal.alerts-test.alerts-default-000002', + _seq_no: 42, + _primary_term: 666, + _source: expandFlattenedAlert(fetchedAlert2), + }, + ], + }, + }); + const alertsClient = new AlertsClient<{}, {}, {}, 'default', 'recovered'>( + alertsClientParams + ); + + await alertsClient.initializeExecution({ + maxAlerts, + ruleLabel: `test: rule-name`, + flappingSettings: DEFAULT_FLAPPING_SETTINGS, + activeAlertsFromState: { + '1': trackedAlert1Raw, + '2': trackedAlert2Raw, }, recoveredAlertsFromState: {}, }); @@ -1062,103 +947,43 @@ describe('Alerts Client', () => { }, // ongoing alert doc { - '@timestamp': date, - event: { - action: 'active', - kind: 'signal', - }, + event: { kind: 'signal' }, kibana: { alert: { - action_group: 'default', - duration: { - us: '72000000000000', - }, - flapping: false, - flapping_history: [true, false, false], - instance: { - id: '2', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, + instance: { id: '2' }, start: '2023-03-28T02:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T02:27:28.159Z', - }, uuid: 'def', - workflow_status: 'open', }, - space_ids: ['default'], - version: '8.9.0', }, - tags: ['rule-', '-tags'], + [TIMESTAMP]: date, + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_DURATION]: '72000000000000', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true, false, false, false], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_STATUS]: 'active', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T02:27:28.159Z' }, + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }, { create: { _id: uuid3, ...(useDataStreamForAlerts ? {} : { require_alias: true }) }, }, // new alert doc - { - '@timestamp': date, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '3', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: date, - status: 'active', - time_range: { - gte: date, - }, - uuid: uuid3, - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], - }, + getNewIndexedAlertDoc({ [ALERT_UUID]: uuid3, [ALERT_INSTANCE_ID]: '3' }), { index: { _id: 'abc', @@ -1170,53 +995,38 @@ describe('Alerts Client', () => { }, // recovered alert doc { - '@timestamp': date, - event: { - action: 'close', - kind: 'signal', - }, + event: { kind: 'signal' }, kibana: { alert: { - action_group: 'recovered', - duration: { - us: '36000000000000', - }, - end: date, - flapping: false, - flapping_history: [true, true], - instance: { - id: '1', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'recovered', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - lte: date, - }, + instance: { id: '1' }, uuid: 'abc', - workflow_status: 'open', }, - space_ids: ['default'], - version: '8.9.0', }, - tags: ['rule-', '-tags'], + [TIMESTAMP]: date, + [EVENT_ACTION]: 'close', + [ALERT_ACTION_GROUP]: 'recovered', + [ALERT_DURATION]: '36000000000000', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true, true], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_END]: date, + [ALERT_STATUS]: 'recovered', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z', lte: date }, + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }, ], }); @@ -1265,11 +1075,7 @@ describe('Alerts Client', () => { _id: '1', _version: 1, result: 'created', - _shards: { - total: 2, - successful: 1, - failed: 0, - }, + _shards: { total: 2, successful: 1, failed: 0 }, status: 201, _seq_no: 0, _primary_term: 1, @@ -1310,114 +1116,21 @@ describe('Alerts Client', () => { timed_out: false, _shards: { failed: 0, successful: 1, total: 1, skipped: 0 }, hits: { - total: { - relation: 'eq', - value: 2, - }, + total: { relation: 'eq', value: 2 }, hits: [ { _id: 'abc', _index: 'partial-.internal.alerts-test.alerts-default-000001', _seq_no: 41, _primary_term: 665, - _source: { - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abc', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.0', - }, - tags: ['rule-', '-tags'], - }, + _source: fetchedAlert1, }, { - _id: 'xyz', + _id: 'def', _index: '.internal.alerts-test.alerts-default-000002', - _seq_no: 41, - _primary_term: 665, - _source: { - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '2', - }, - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'xyz', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.0', - }, - tags: ['rule-', '-tags'], - }, + _seq_no: 42, + _primary_term: 666, + _source: fetchedAlert2, }, ], }, @@ -1431,26 +1144,8 @@ describe('Alerts Client', () => { ruleLabel: `test: rule-name`, flappingSettings: DEFAULT_FLAPPING_SETTINGS, activeAlertsFromState: { - '1': { - state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '0' }, - meta: { - flapping: false, - flappingHistory: [true], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'abc', - }, - }, - '2': { - state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '0' }, - meta: { - flapping: false, - flappingHistory: [true], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'xyz', - }, - }, + '1': trackedAlert1Raw, + '2': trackedAlert2Raw, }, recoveredAlertsFromState: {}, }); @@ -1471,61 +1166,22 @@ describe('Alerts Client', () => { body: [ { index: { - _id: 'xyz', + _id: 'def', _index: '.internal.alerts-test.alerts-default-000002', - if_seq_no: 41, - if_primary_term: 665, + if_seq_no: 42, + if_primary_term: 666, require_alias: false, }, }, // ongoing alert doc - { - '@timestamp': date, - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '36000000000000', - }, - flapping: false, - flapping_history: [true, false], - instance: { - id: '2', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'xyz', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], - }, + getOngoingIndexedAlertDoc({ + [ALERT_UUID]: 'def', + [ALERT_INSTANCE_ID]: '2', + [ALERT_DURATION]: '72000000000000', + [ALERT_FLAPPING_HISTORY]: [true, false, false, false], + [ALERT_START]: '2023-03-28T02:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T02:27:28.159Z' }, + }), ], }); @@ -2137,106 +1793,70 @@ describe('Alerts Client', () => { }, // new alert doc { - '@timestamp': date, + ...getNewIndexedAlertDoc({ [ALERT_UUID]: uuid1 }), count: 1, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: date, - status: 'active', - time_range: { - gte: date, - }, - uuid: uuid1, - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], url: `https://url1`, + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_DURATION]: '0', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true], + [ALERT_INSTANCE_ID]: '1', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_START]: date, + [ALERT_STATUS]: 'active', + [ALERT_TIME_RANGE]: { gte: date }, + [ALERT_UUID]: uuid1, + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }, { create: { _id: uuid2, ...(useDataStreamForAlerts ? {} : { require_alias: true }) }, }, // new alert doc { - '@timestamp': date, + ...getNewIndexedAlertDoc({ [ALERT_UUID]: uuid2, [ALERT_INSTANCE_ID]: '2' }), count: 2, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '2', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: date, - status: 'active', - time_range: { - gte: date, - }, - uuid: uuid2, - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], url: `https://url2`, + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_DURATION]: '0', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true], + [ALERT_INSTANCE_ID]: '2', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_START]: date, + [ALERT_STATUS]: 'active', + [ALERT_TIME_RANGE]: { gte: date }, + [ALERT_UUID]: uuid2, + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }, ], }); @@ -2265,26 +1885,8 @@ describe('Alerts Client', () => { ruleLabel: `test: rule-name`, flappingSettings: DEFAULT_FLAPPING_SETTINGS, activeAlertsFromState: { - '1': { - state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '0' }, - meta: { - flapping: false, - flappingHistory: [true], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'abc', - }, - }, - '2': { - state: { foo: true, start: '2023-03-28T02:27:28.159Z', duration: '36000000000000' }, - meta: { - flapping: false, - flappingHistory: [true, false], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'def', - }, - }, + '1': trackedAlert1Raw, + '2': trackedAlert2Raw, }, recoveredAlertsFromState: {}, }); @@ -2313,26 +1915,8 @@ describe('Alerts Client', () => { ruleLabel: `test: rule-name`, flappingSettings: DEFAULT_FLAPPING_SETTINGS, activeAlertsFromState: { - '1': { - state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '0' }, - meta: { - flapping: false, - flappingHistory: [true], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'abc', - }, - }, - '2': { - state: { foo: true, start: '2023-03-28T02:27:28.159Z', duration: '36000000000000' }, - meta: { - flapping: false, - flappingHistory: [true, false], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'def', - }, - }, + '1': trackedAlert1Raw, + '2': trackedAlert2Raw, }, recoveredAlertsFromState: {}, }); @@ -2352,10 +1936,7 @@ describe('Alerts Client', () => { timed_out: false, _shards: { failed: 0, successful: 1, total: 1, skipped: 0 }, hits: { - total: { - relation: 'eq', - value: 0, - }, + total: { relation: 'eq', value: 0 }, hits: [], }, }); @@ -2406,53 +1987,35 @@ describe('Alerts Client', () => { }, }, { - '@timestamp': date, + ...getNewIndexedAlertDoc({ [ALERT_UUID]: expect.any(String) }), count: 100, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: date, - status: 'active', - time_range: { - gte: date, - }, - uuid: expect.any(String), - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], url: 'https://elastic.co', + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_DURATION]: '0', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true], + [ALERT_INSTANCE_ID]: '1', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_START]: date, + [ALERT_STATUS]: 'active', + [ALERT_TIME_RANGE]: { gte: date }, + [ALERT_UUID]: expect.any(String), + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }, ], }); @@ -2464,61 +2027,15 @@ describe('Alerts Client', () => { timed_out: false, _shards: { failed: 0, successful: 1, total: 1, skipped: 0 }, hits: { - total: { - relation: 'eq', - value: 1, - }, + total: { relation: 'eq', value: 1 }, hits: [ { _id: 'abc', _index: '.internal.alerts-test.alerts-default-000001', _source: { - '@timestamp': '2023-03-28T12:27:28.159Z', + ...fetchedAlert1, count: 1, url: 'https://localhost:5601/abc', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abc', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.0', - }, - tags: ['rule-', '-tags'], }, }, ], @@ -2537,16 +2054,7 @@ describe('Alerts Client', () => { ruleLabel: `test: rule-name`, flappingSettings: DEFAULT_FLAPPING_SETTINGS, activeAlertsFromState: { - '1': { - state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '0' }, - meta: { - flapping: false, - flappingHistory: [true], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'abc', - }, - }, + '1': trackedAlert1Raw, }, recoveredAlertsFromState: {}, }); @@ -2582,53 +2090,35 @@ describe('Alerts Client', () => { }, }, { - '@timestamp': date, + ...getOngoingIndexedAlertDoc({ [ALERT_UUID]: 'abc' }), count: 100, - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '36000000000000', - }, - flapping: false, - flapping_history: [true, false], - instance: { - id: '1', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abc', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], url: 'https://elastic.co', + [EVENT_ACTION]: 'active', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_DURATION]: '36000000000000', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true, false], + [ALERT_INSTANCE_ID]: '1', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_UUID]: 'abc', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_STATUS]: 'active', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }, ], }); @@ -2640,10 +2130,7 @@ describe('Alerts Client', () => { timed_out: false, _shards: { failed: 0, successful: 1, total: 1, skipped: 0 }, hits: { - total: { - relation: 'eq', - value: 1, - }, + total: { relation: 'eq', value: 1 }, hits: [ { _id: 'abc', @@ -2651,52 +2138,9 @@ describe('Alerts Client', () => { _seq_no: 42, _primary_term: 666, _source: { - '@timestamp': '2023-03-28T12:27:28.159Z', + ...fetchedAlert1, count: 1, url: 'https://localhost:5601/abc', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T11:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T11:27:28.159Z', - }, - uuid: 'abc', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.0', - }, - tags: ['rule-', '-tags'], }, }, ], @@ -2715,16 +2159,7 @@ describe('Alerts Client', () => { ruleLabel: `test: rule-name`, flappingSettings: DEFAULT_FLAPPING_SETTINGS, activeAlertsFromState: { - '1': { - state: { foo: true, start: '2023-03-28T11:27:28.159Z', duration: '0' }, - meta: { - flapping: false, - flappingHistory: [true], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'abc', - }, - }, + '1': trackedAlert1Raw, }, recoveredAlertsFromState: {}, }); @@ -2757,55 +2192,36 @@ describe('Alerts Client', () => { }, }, { - '@timestamp': date, + ...getRecoveredIndexedAlertDoc({ [ALERT_UUID]: 'abc' }), count: 100, - event: { - action: 'close', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'recovered', - duration: { - us: '39600000000000', - }, - end: date, - flapping: false, - flapping_history: [true, true], - instance: { - id: '1', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T11:27:28.159Z', - status: 'recovered', - time_range: { - gte: '2023-03-28T11:27:28.159Z', - lte: date, - }, - uuid: 'abc', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], url: 'https://elastic.co', + [EVENT_ACTION]: 'close', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'recovered', + [ALERT_DURATION]: '36000000000000', + [ALERT_END]: date, + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true, true], + [ALERT_INSTANCE_ID]: '1', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_UUID]: 'abc', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_STATUS]: 'recovered', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z', lte: date }, + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }, ], }); @@ -2843,60 +2259,14 @@ describe('Alerts Client', () => { timed_out: false, _shards: { failed: 0, successful: 1, total: 1, skipped: 0 }, hits: { - total: { - relation: 'eq', - value: 1, - }, + total: { relation: 'eq', value: 1 }, hits: [ { _id: 'abc', _index: '.internal.alerts-test.alerts-default-000001', - _source: { - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abc', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.0', - }, - tags: ['rule-', '-tags'], - }, + _seq_no: 42, + _primary_term: 666, + _source: fetchedAlert1, }, ], }, @@ -2909,16 +2279,7 @@ describe('Alerts Client', () => { ruleLabel: `test: rule-name`, flappingSettings: DEFAULT_FLAPPING_SETTINGS, activeAlertsFromState: { - '1': { - state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '0' }, - meta: { - flapping: false, - flappingHistory: [true], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'abc', - }, - }, + '1': trackedAlert1Raw, }, recoveredAlertsFromState: {}, }); @@ -2932,52 +2293,7 @@ describe('Alerts Client', () => { expect(recoveredAlert.alert.getId()).toEqual('1'); expect(recoveredAlert.alert.getUuid()).toEqual('abc'); expect(recoveredAlert.alert.getStart()).toEqual('2023-03-28T12:27:28.159Z'); - expect(recoveredAlert.hit).toEqual({ - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: '2023-03-28T12:27:28.159Z', - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abc', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.0', - }, - tags: ['rule-', '-tags'], - }); + expect(recoveredAlert.hit).toEqual(fetchedAlert1); }); test('should return undefined document with recovered alert, if it does not exists', async () => { @@ -2986,10 +2302,7 @@ describe('Alerts Client', () => { timed_out: false, _shards: { failed: 0, successful: 1, total: 1, skipped: 0 }, hits: { - total: { - relation: 'eq', - value: 0, - }, + total: { relation: 'eq', value: 0 }, hits: [], }, }); @@ -3001,16 +2314,7 @@ describe('Alerts Client', () => { ruleLabel: `test: rule-name`, flappingSettings: DEFAULT_FLAPPING_SETTINGS, activeAlertsFromState: { - '1': { - state: { foo: true, start: '2023-03-28T12:27:28.159Z', duration: '0' }, - meta: { - flapping: false, - flappingHistory: [true], - maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date().toISOString() }, - uuid: 'abc', - }, - }, + '1': trackedAlert1Raw, }, recoveredAlertsFromState: {}, }); diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index eec5d3c5595bd..34d4e994cfe8b 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -7,13 +7,14 @@ import { ElasticsearchClient } from '@kbn/core/server'; import { + ALERT_INSTANCE_ID, ALERT_RULE_UUID, ALERT_STATUS, ALERT_STATUS_UNTRACKED, ALERT_STATUS_ACTIVE, ALERT_UUID, } from '@kbn/rule-data-utils'; -import { chunk, flatMap, isEmpty, keys } from 'lodash'; +import { chunk, flatMap, get, isEmpty, keys } from 'lodash'; import { SearchRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { Alert } from '@kbn/alerts-as-data-utils'; import { DEFAULT_NAMESPACE_STRING } from '@kbn/core-saved-objects-utils-server'; @@ -92,7 +93,7 @@ export class AlertsClient< primaryTerm: Record; }; - private rule: AlertRule = {}; + private rule: AlertRule; private ruleType: UntypedNormalizedRuleType; private indexTemplateAndPattern: IIndexPatternString; @@ -173,8 +174,8 @@ export class AlertsClient< for (const hit of results.flat()) { const alertHit: Alert & AlertData = hit._source as Alert & AlertData; - const alertUuid = alertHit.kibana.alert.uuid; - const alertId = alertHit.kibana.alert.instance.id; + const alertUuid = get(alertHit, ALERT_UUID); + const alertId = get(alertHit, ALERT_INSTANCE_ID); // Keep track of existing alert document so we can copy over data if alert is ongoing this.fetchedAlerts.data[alertId] = alertHit; @@ -350,7 +351,7 @@ export class AlertsClient< if (!!activeAlerts[id]) { if ( this.fetchedAlerts.data.hasOwnProperty(id) && - this.fetchedAlerts.data[id].kibana.alert.status === 'active' + get(this.fetchedAlerts.data[id], ALERT_STATUS) === 'active' ) { activeAlertsToIndex.push( buildOngoingAlert< @@ -432,12 +433,13 @@ export class AlertsClient< const alertsToIndex = [...activeAlertsToIndex, ...recoveredAlertsToIndex].filter( (alert: Alert & AlertData) => { - const alertIndex = this.fetchedAlerts.indices[alert.kibana.alert.uuid]; + const alertUuid = get(alert, ALERT_UUID); + const alertIndex = this.fetchedAlerts.indices[alertUuid]; if (!alertIndex) { return true; } else if (!isValidAlertIndexName(alertIndex)) { this.options.logger.warn( - `Could not update alert ${alert.kibana.alert.uuid} in ${alertIndex}. Partial and restored alert indices are not supported.` + `Could not update alert ${alertUuid} in ${alertIndex}. Partial and restored alert indices are not supported.` ); return false; } @@ -446,16 +448,19 @@ export class AlertsClient< ); if (alertsToIndex.length > 0) { const bulkBody = flatMap( - alertsToIndex.map((alert: Alert & AlertData) => [ - getBulkMeta( - alert.kibana.alert.uuid, - this.fetchedAlerts.indices[alert.kibana.alert.uuid], - this.fetchedAlerts.seqNo[alert.kibana.alert.uuid], - this.fetchedAlerts.primaryTerm[alert.kibana.alert.uuid], - this.isUsingDataStreams() - ), - alert, - ]) + alertsToIndex.map((alert: Alert & AlertData) => { + const alertUuid = get(alert, ALERT_UUID); + return [ + getBulkMeta( + alertUuid, + this.fetchedAlerts.indices[alertUuid], + this.fetchedAlerts.seqNo[alertUuid], + this.fetchedAlerts.primaryTerm[alertUuid], + this.isUsingDataStreams() + ), + alert, + ]; + }) ); try { diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.test.ts b/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.test.ts index ffa2adc96f54f..281b358854be9 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.test.ts @@ -7,6 +7,13 @@ import { loggingSystemMock } from '@kbn/core/server/mocks'; import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import { + ALERT_CASE_IDS, + ALERT_STATUS, + ALERT_WORKFLOW_STATUS, + ALERT_WORKFLOW_TAGS, + EVENT_ACTION, +} from '@kbn/rule-data-utils'; import { BulkRequest, BulkResponse, @@ -20,15 +27,11 @@ const logger = loggingSystemMock.create().get(); const esClient = elasticsearchServiceMock.createElasticsearchClient(); const alertDoc = { - event: { action: 'active' }, - kibana: { - alert: { - status: 'untracked', - workflow_status: 'a-ok!', - workflow_tags: ['fee', 'fi', 'fo', 'fum'], - case_ids: ['123', '456', '789'], - }, - }, + [EVENT_ACTION]: 'active', + [ALERT_STATUS]: 'untracked', + [ALERT_WORKFLOW_STATUS]: 'a-ok!', + [ALERT_WORKFLOW_TAGS]: ['fee', 'fi', 'fo', 'fum'], + [ALERT_CASE_IDS]: ['123', '456', '789'], }; describe('alert_conflict_resolver', () => { diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.ts b/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.ts index 223070c0e7245..3c5ce6e25a1a8 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.ts @@ -22,13 +22,12 @@ import { ALERT_CASE_IDS, } from '@kbn/rule-data-utils'; -import { set } from '@kbn/safer-lodash-set'; import { zip, get } from 'lodash'; // these fields are the one's we'll refresh from the fresh mget'd docs const REFRESH_FIELDS_ALWAYS = [ALERT_WORKFLOW_STATUS, ALERT_WORKFLOW_TAGS, ALERT_CASE_IDS]; const REFRESH_FIELDS_CONDITIONAL = [ALERT_STATUS]; -const REFRESH_FIELDS_ALL = [...REFRESH_FIELDS_ALWAYS, ...REFRESH_FIELDS_CONDITIONAL]; +export const REFRESH_FIELDS_ALL = [...REFRESH_FIELDS_ALWAYS, ...REFRESH_FIELDS_CONDITIONAL]; export interface ResolveAlertConflictsParams { esClient: ElasticsearchClient; @@ -147,7 +146,7 @@ async function refreshFieldsInDocs( for (const refreshField of REFRESH_FIELDS_ALWAYS) { const val = get(freshDoc, refreshField); - set(conflictDoc, refreshField, val); + conflictDoc[refreshField] = val; } // structured this way to make sure all conditional refresh @@ -160,7 +159,7 @@ async function refreshFieldsInDocs( const freshStatus = get(freshDoc, ALERT_STATUS); if (freshStatus !== ALERT_STATUS_ACTIVE && freshStatus !== ALERT_STATUS_RECOVERED) { - set(conflictDoc, ALERT_STATUS, freshStatus); + conflictDoc[ALERT_STATUS] = freshStatus; } break; } diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/build_new_alert.test.ts b/x-pack/plugins/alerting/server/alerts_client/lib/build_new_alert.test.ts index d31d506854f49..4e317d5f5592f 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/build_new_alert.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/build_new_alert.test.ts @@ -6,33 +6,27 @@ */ import { Alert as LegacyAlert } from '../../alert/alert'; import { buildNewAlert } from './build_new_alert'; -import type { AlertRule } from '../types'; import { Alert } from '@kbn/alerts-as-data-utils'; - -const rule = { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', -}; -const alertRule: AlertRule = { - kibana: { - alert: { - rule, - }, - space_ids: ['default'], - }, -}; +import { + SPACE_IDS, + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_START, + ALERT_STATUS, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + EVENT_ACTION, + EVENT_KIND, + TAGS, + TIMESTAMP, + VERSION, + ALERT_TIME_RANGE, +} from '@kbn/rule-data-utils'; +import { alertRule } from './test_fixtures'; describe('buildNewAlert', () => { test('should build alert document with info from legacy alert', () => { @@ -47,29 +41,21 @@ describe('buildNewAlert', () => { kibanaVersion: '8.9.0', }) ).toEqual({ - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: [], - rule, - status: 'active', - uuid: legacyAlert.getUuid(), - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], + ...alertRule, + [TIMESTAMP]: '2023-03-28T12:27:28.159Z', + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_UUID]: legacyAlert.getUuid(), + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }); }); @@ -86,36 +72,24 @@ describe('buildNewAlert', () => { kibanaVersion: '8.9.0', }) ).toEqual({ - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: [], - start: now, - rule, - status: 'active', - time_range: { - gte: now, - }, - uuid: legacyAlert.getUuid(), - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], + ...alertRule, + [TIMESTAMP]: '2023-03-28T12:27:28.159Z', + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_UUID]: legacyAlert.getUuid(), + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '0', + [ALERT_START]: now, + [ALERT_TIME_RANGE]: { gte: now }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }); }); @@ -133,29 +107,21 @@ describe('buildNewAlert', () => { kibanaVersion: '8.9.0', }) ).toEqual({ - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - flapping: false, - flapping_history: [true, false, false, false, true, true], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: ['maint-1', 'maint-321'], - rule, - status: 'active', - uuid: legacyAlert.getUuid(), - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], + ...alertRule, + [TIMESTAMP]: '2023-03-28T12:27:28.159Z', + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true, false, false, false, true, true], + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-1', 'maint-321'], + [ALERT_STATUS]: 'active', + [ALERT_UUID]: legacyAlert.getUuid(), + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }); }); @@ -165,7 +131,7 @@ describe('buildNewAlert', () => { expect( buildNewAlert< - { count: number; url: string; kibana: { alert: { nested_field: number } } }, + { count: number; url: string; 'kibana.alert.nested_field': number }, {}, {}, 'default', @@ -174,36 +140,28 @@ describe('buildNewAlert', () => { legacyAlert, rule: alertRule, timestamp: '2023-03-28T12:27:28.159Z', - payload: { count: 1, url: `https://url1`, kibana: { alert: { nested_field: 2 } } }, + payload: { count: 1, url: `https://url1`, 'kibana.alert.nested_field': 2 }, kibanaVersion: '8.9.0', }) ).toEqual({ - '@timestamp': '2023-03-28T12:27:28.159Z', + ...alertRule, count: 1, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: [], - nested_field: 2, - rule, - status: 'active', - uuid: legacyAlert.getUuid(), - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], url: `https://url1`, + 'kibana.alert.nested_field': 2, + [TIMESTAMP]: '2023-03-28T12:27:28.159Z', + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_UUID]: legacyAlert.getUuid(), + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }); }); @@ -213,7 +171,7 @@ describe('buildNewAlert', () => { expect( buildNewAlert< - Alert & { count: number; url: string; kibana: { alert: { nested_field: number } } }, + Alert & { count: number; url: string; 'kibana.alert.nested_field': number }, {}, {}, 'default', @@ -225,37 +183,30 @@ describe('buildNewAlert', () => { payload: { count: 1, url: `https://url1`, - kibana: { alert: { nested_field: 2, workflow_status: 'custom_workflow' } }, + 'kibana.alert.nested_field': 2, + [ALERT_WORKFLOW_STATUS]: 'custom_workflow', }, kibanaVersion: '8.9.0', }) ).toEqual({ - '@timestamp': '2023-03-28T12:27:28.159Z', + ...alertRule, count: 1, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: [], - nested_field: 2, - rule, - status: 'active', - uuid: legacyAlert.getUuid(), - workflow_status: 'custom_workflow', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], url: `https://url1`, + 'kibana.alert.nested_field': 2, + [TIMESTAMP]: '2023-03-28T12:27:28.159Z', + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_UUID]: legacyAlert.getUuid(), + [ALERT_WORKFLOW_STATUS]: 'custom_workflow', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }); }); @@ -268,7 +219,8 @@ describe('buildNewAlert', () => { { count: number; url: string; - kibana: { alert: { action_group: string; nested_field: number } }; + [ALERT_ACTION_GROUP]: string; + 'kibana.alert.nested_field': number; }, {}, {}, @@ -281,37 +233,30 @@ describe('buildNewAlert', () => { payload: { count: 1, url: `https://url1`, - kibana: { alert: { action_group: 'bad action group', nested_field: 2 } }, + [ALERT_ACTION_GROUP]: 'bad action group', + 'kibana.alert.nested_field': 2, }, kibanaVersion: '8.9.0', }) ).toEqual({ - '@timestamp': '2023-03-28T12:27:28.159Z', + ...alertRule, count: 1, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: [], - nested_field: 2, - rule, - status: 'active', - uuid: legacyAlert.getUuid(), - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], url: `https://url1`, + 'kibana.alert.nested_field': 2, + [TIMESTAMP]: '2023-03-28T12:27:28.159Z', + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_UUID]: legacyAlert.getUuid(), + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], }); }); @@ -324,7 +269,8 @@ describe('buildNewAlert', () => { { count: number; url: string; - kibana: { alert: { action_group: string; nested_field: number } }; + [ALERT_ACTION_GROUP]: string; + 'kibana.alert.nested_field': number; tags: string[]; }, {}, @@ -338,38 +284,31 @@ describe('buildNewAlert', () => { payload: { count: 1, url: `https://url1`, - kibana: { alert: { action_group: 'bad action group', nested_field: 2 } }, + [ALERT_ACTION_GROUP]: 'bad action group', + 'kibana.alert.nested_field': 2, tags: ['custom-tag1', '-tags'], }, kibanaVersion: '8.9.0', }) ).toEqual({ - '@timestamp': '2023-03-28T12:27:28.159Z', + ...alertRule, count: 1, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: [], - nested_field: 2, - rule, - status: 'active', - uuid: legacyAlert.getUuid(), - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['custom-tag1', '-tags', 'rule-'], url: `https://url1`, + 'kibana.alert.nested_field': 2, + [TIMESTAMP]: '2023-03-28T12:27:28.159Z', + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_UUID]: legacyAlert.getUuid(), + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['custom-tag1', '-tags', 'rule-'], }); }); }); diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/build_new_alert.ts b/x-pack/plugins/alerting/server/alerts_client/lib/build_new_alert.ts index 5d163504a9606..9c4c4bb6c095c 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/build_new_alert.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/build_new_alert.ts @@ -7,7 +7,26 @@ import deepmerge from 'deepmerge'; import { get } from 'lodash'; import type { Alert } from '@kbn/alerts-as-data-utils'; -import { ALERT_WORKFLOW_STATUS } from '@kbn/rule-data-utils'; +import { + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_RULE_TAGS, + ALERT_START, + ALERT_STATUS, + ALERT_TIME_RANGE, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + EVENT_ACTION, + EVENT_KIND, + SPACE_IDS, + TAGS, + TIMESTAMP, + VERSION, +} from '@kbn/rule-data-utils'; import { DeepPartial } from '@kbn/utility-types'; import { Alert as LegacyAlert } from '../../alert/alert'; import { AlertInstanceContext, AlertInstanceState, RuleAlertData } from '../../types'; @@ -56,45 +75,32 @@ export const buildNewAlert = < return deepmerge.all( [ cleanedPayload, + rule, { - '@timestamp': timestamp, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: legacyAlert.getScheduledActionOptions()?.actionGroup, - flapping: legacyAlert.getFlapping(), - flapping_history: legacyAlert.getFlappingHistory(), - instance: { - id: legacyAlert.getId(), - }, - maintenance_window_ids: legacyAlert.getMaintenanceWindowIds(), - rule: rule.kibana?.alert.rule, - status: 'active', - uuid: legacyAlert.getUuid(), - workflow_status: get(cleanedPayload, ALERT_WORKFLOW_STATUS, 'open'), - ...(legacyAlert.getState().duration - ? { duration: { us: legacyAlert.getState().duration } } - : {}), - ...(legacyAlert.getState().start - ? { - start: legacyAlert.getState().start, - time_range: { - gte: legacyAlert.getState().start, - }, - } - : {}), - }, - space_ids: rule.kibana?.space_ids, - version: kibanaVersion, - }, - tags: Array.from( - new Set([ - ...((cleanedPayload?.tags as string[]) ?? []), - ...(rule.kibana?.alert.rule.tags ?? []), - ]) + [TIMESTAMP]: timestamp, + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: legacyAlert.getScheduledActionOptions()?.actionGroup, + [ALERT_FLAPPING]: legacyAlert.getFlapping(), + [ALERT_FLAPPING_HISTORY]: legacyAlert.getFlappingHistory(), + [ALERT_INSTANCE_ID]: legacyAlert.getId(), + [ALERT_MAINTENANCE_WINDOW_IDS]: legacyAlert.getMaintenanceWindowIds(), + [ALERT_STATUS]: 'active', + [ALERT_UUID]: legacyAlert.getUuid(), + [ALERT_WORKFLOW_STATUS]: get(cleanedPayload, ALERT_WORKFLOW_STATUS, 'open'), + ...(legacyAlert.getState().duration + ? { [ALERT_DURATION]: legacyAlert.getState().duration } + : {}), + ...(legacyAlert.getState().start + ? { + [ALERT_START]: legacyAlert.getState().start, + [ALERT_TIME_RANGE]: { gte: legacyAlert.getState().start }, + } + : {}), + [SPACE_IDS]: rule[SPACE_IDS], + [VERSION]: kibanaVersion, + [TAGS]: Array.from( + new Set([...((cleanedPayload?.tags as string[]) ?? []), ...(rule[ALERT_RULE_TAGS] ?? [])]) ), }, ], diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/build_ongoing_alert.test.ts b/x-pack/plugins/alerting/server/alerts_client/lib/build_ongoing_alert.test.ts index 078d16602ac84..7ccef435a5a49 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/build_ongoing_alert.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/build_ongoing_alert.test.ts @@ -6,498 +6,683 @@ */ import { Alert as LegacyAlert } from '../../alert/alert'; import { buildOngoingAlert } from './build_ongoing_alert'; -import type { AlertRule } from '../types'; +import { + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + SPACE_IDS, + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_START, + ALERT_STATUS, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + EVENT_ACTION, + EVENT_KIND, + TAGS, + TIMESTAMP, + VERSION, + ALERT_TIME_RANGE, +} from '@kbn/rule-data-utils'; +import { alertRule, existingFlattenedNewAlert, existingExpandedNewAlert } from './test_fixtures'; -const rule = { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', -}; -const alertRule: AlertRule = { - kibana: { - alert: { - rule, - }, - space_ids: ['default'], - }, -}; -const existingAlert = { - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'error', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: 'alert-A', - }, - start: '2023-03-28T12:27:28.159Z', - rule, - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.1', - }, - tags: ['rule-', '-tags'], -}; +for (const flattened of [true, false]) { + const existingAlert = flattened ? existingFlattenedNewAlert : existingExpandedNewAlert; -describe('buildOngoingAlert', () => { - test('should update alert document with updated info from legacy alert', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A'); - legacyAlert - .scheduleActions('warning') - .replaceState({ start: '0000-00-00T00:00:00.000Z', duration: '36000000' }); + describe(`buildOngoingAlert for ${flattened ? 'flattened' : 'expanded'} existing alert`, () => { + test('should return alert document with updated info from legacy alert', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert + .scheduleActions('warning') + .replaceState({ start: '2023-03-28T12:27:28.159Z', duration: '36000000' }); - expect( - buildOngoingAlert<{}, {}, {}, 'error' | 'warning', 'recovered'>({ - alert: existingAlert, - legacyAlert, - rule: alertRule, - timestamp: '2023-03-29T12:27:28.159Z', - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'warning', - duration: { - us: '36000000', - }, - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: [], - start: '2023-03-28T12:27:28.159Z', - rule, - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], + expect( + buildOngoingAlert<{}, {}, {}, 'error' | 'warning', 'recovered'>({ + // @ts-expect-error + alert: existingAlert, + legacyAlert, + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + kibanaVersion: '8.9.0', + }) + ).toEqual({ + ...alertRule, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'warning', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_DURATION]: '36000000', + [ALERT_STATUS]: 'active', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + start: '2023-03-28T12:27:28.159Z', + uuid: 'abcdefg', + }, + }, + }), + }); }); - }); - test('should update alert document with updated rule data if rule definition has changed', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A'); - legacyAlert - .scheduleActions('warning') - .replaceState({ start: '0000-00-00T00:00:00.000Z', duration: '36000000' }); + test('should return alert document with updated rule data if rule definition has changed', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert + .scheduleActions('warning') + .replaceState({ start: '2023-03-28T12:27:28.159Z', duration: '36000000' }); - expect( - buildOngoingAlert<{}, {}, {}, 'error' | 'warning', 'recovered'>({ - alert: existingAlert, - legacyAlert, - rule: { - kibana: { - alert: { - rule: { - ...rule, - name: 'updated-rule-name', - parameters: { - bar: false, + const updatedRule = { + ...alertRule, + [ALERT_RULE_NAME]: 'updated-rule-name', + [ALERT_RULE_PARAMETERS]: { bar: false }, + }; + expect( + buildOngoingAlert<{}, {}, {}, 'error' | 'warning', 'recovered'>({ + // @ts-expect-error + alert: existingAlert, + legacyAlert, + rule: updatedRule, + timestamp: '2023-03-29T12:27:28.159Z', + kibanaVersion: '8.9.0', + }) + ).toEqual({ + ...updatedRule, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'warning', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + start: '2023-03-28T12:27:28.159Z', + uuid: 'abcdefg', }, }, + }), + }); + }); + + test('should return alert document with updated flapping history and maintenance window ids if set', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert + .scheduleActions('error') + .replaceState({ start: '2023-03-28T12:27:28.159Z', duration: '36000000' }); + legacyAlert.setFlappingHistory([false, false, true, true]); + legacyAlert.setMaintenanceWindowIds(['maint-xyz']); + + const alert = flattened + ? { + ...existingAlert, + [ALERT_FLAPPING_HISTORY]: [true, false, false, false, true, true], + [ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-1', 'maint-321'], + } + : { + ...existingAlert, + kibana: { + // @ts-expect-error + ...existingAlert.kibana, + alert: { + // @ts-expect-error + ...existingAlert.kibana.alert, + flapping_history: [true, false, false, false, true, true], + maintenance_window_ids: ['maint-1', 'maint-321'], + }, }, - space_ids: ['default'], - }, - }, - timestamp: '2023-03-29T12:27:28.159Z', - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'warning', - duration: { - us: '36000000', - }, - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: [], - start: '2023-03-28T12:27:28.159Z', - rule: { - ...rule, - name: 'updated-rule-name', - parameters: { - bar: false, - }, - }, - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], + }; + + expect( + buildOngoingAlert<{}, {}, {}, 'error' | 'warning', 'recovered'>({ + // @ts-expect-error + alert, + legacyAlert, + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + kibanaVersion: '8.9.0', + }) + ).toEqual({ + ...alertRule, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'error', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [false, false, true, true], + [ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-xyz'], + [ALERT_STATUS]: 'active', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + start: '2023-03-28T12:27:28.159Z', + uuid: 'abcdefg', + }, + }, + }), + }); }); - }); - test('should update alert document with updated flapping history and maintenance window ids if set', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('1'); - legacyAlert.scheduleActions('error'); - legacyAlert.setFlappingHistory([false, false, true, true]); - legacyAlert.setMaintenanceWindowIds(['maint-xyz']); + test('should return alert document with updated payload if specified', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert + .scheduleActions('warning') + .replaceState({ start: '2023-03-28T12:27:28.159Z', duration: '36000000' }); - expect( - buildOngoingAlert<{}, {}, {}, 'error' | 'warning', 'recovered'>({ - alert: { - ...existingAlert, - kibana: { - ...existingAlert.kibana, - alert: { - ...existingAlert.kibana.alert, - flapping_history: [true, false, false, false, true, true], - maintenance_window_ids: ['maint-1', 'maint-321'], + const alert = flattened + ? { + ...existingAlert, + count: 1, + url: `https://url1`, + 'kibana.alert.nested_field': 3, + } + : { + ...existingAlert, + count: 1, + url: `https://url1`, + kibana: { + // @ts-expect-error + ...existingAlert.kibana, + alert: { + // @ts-expect-error + ...existingAlert.kibana.alert, + nested_field: 3, + }, }, + }; + expect( + buildOngoingAlert< + { count: number; url: string; 'kibana.alert.nested_field'?: number }, + {}, + {}, + 'error' | 'warning', + 'recovered' + >({ + // @ts-expect-error + alert, + legacyAlert, + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + payload: { + count: 2, + url: `https://url2`, + 'kibana.alert.nested_field': 2, }, - }, - legacyAlert, - rule: alertRule, - timestamp: '2023-03-29T12:27:28.159Z', - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'error', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [false, false, true, true], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: ['maint-xyz'], - start: '2023-03-28T12:27:28.159Z', - rule, - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], + kibanaVersion: '8.9.0', + }) + ).toEqual({ + ...alertRule, + count: 2, + url: `https://url2`, + 'kibana.alert.nested_field': 2, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'warning', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + start: '2023-03-28T12:27:28.159Z', + uuid: 'abcdefg', + }, + }, + }), + }); }); - }); - test('should update alert document with updated payload if specified', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A'); - legacyAlert - .scheduleActions('warning') - .replaceState({ start: '0000-00-00T00:00:00.000Z', duration: '36000000' }); + test('should return alert document with updated payload if specified but not overwrite any framework fields', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert + .scheduleActions('warning') + .replaceState({ start: '2023-03-28T12:27:28.159Z', duration: '36000000' }); - expect( - buildOngoingAlert< - { count: number; url: string; kibana?: { alert?: { nested_field?: number } } }, - {}, - {}, - 'error' | 'warning', - 'recovered' - >({ - alert: { - ...existingAlert, - count: 1, - url: `https://url1`, - }, - legacyAlert, - rule: alertRule, - timestamp: '2023-03-29T12:27:28.159Z', - payload: { - count: 2, - url: `https://url2`, - kibana: { alert: { nested_field: 2 } }, - }, - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - count: 2, - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'warning', - duration: { - us: '36000000', - }, - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', + const alert = flattened + ? { + ...existingAlert, + count: 1, + url: `https://url1`, + 'kibana.alert.nested_field': 3, + } + : { + ...existingAlert, + count: 1, + url: `https://url1`, + kibana: { + // @ts-expect-error + ...existingAlert.kibana, + alert: { + // @ts-expect-error + ...existingAlert.kibana.alert, + nested_field: 3, + }, + }, + }; + + expect( + buildOngoingAlert< + { + count: number; + url: string; + [ALERT_ACTION_GROUP]: string; + 'kibana.alert.nested_field'?: number; }, - maintenance_window_ids: [], - nested_field: 2, - start: '2023-03-28T12:27:28.159Z', - rule, - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', + {}, + {}, + 'error' | 'warning', + 'recovered' + >({ + // @ts-expect-error + alert, + legacyAlert, + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + payload: { + count: 2, + url: `https://url2`, + [ALERT_ACTION_GROUP]: 'bad action group', + 'kibana.alert.nested_field': 2, }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - url: `https://url2`, - tags: ['rule-', '-tags'], + kibanaVersion: '8.9.0', + }) + ).toEqual({ + ...alertRule, + count: 2, + url: `https://url2`, + 'kibana.alert.nested_field': 2, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'warning', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + start: '2023-03-28T12:27:28.159Z', + uuid: 'abcdefg', + }, + }, + }), + }); }); - }); - test('should update alert document with updated payload is specified but not overwrite any framework fields', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A'); - legacyAlert - .scheduleActions('warning') - .replaceState({ start: '0000-00-00T00:00:00.000Z', duration: '36000000' }); + test('should merge and de-dupe tags from existing alert, reported payload and rule tags', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert + .scheduleActions('warning') + .replaceState({ start: '2023-03-28T12:27:28.159Z', duration: '36000000' }); - expect( - buildOngoingAlert< - { - count: number; - url: string; - kibana?: { alert?: { action_group: string; nested_field?: number } }; - }, - {}, - {}, - 'error' | 'warning', - 'recovered' - >({ - alert: { - ...existingAlert, - count: 1, - url: `https://url1`, - }, - legacyAlert, - rule: alertRule, - timestamp: '2023-03-29T12:27:28.159Z', - payload: { - count: 2, - url: `https://url2`, - kibana: { alert: { action_group: 'bad action group', nested_field: 2 } }, - }, - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - count: 2, - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'warning', - duration: { - us: '36000000', - }, - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', + const alert = flattened + ? { + ...existingAlert, + count: 1, + url: `https://url1`, + tags: ['old-tag1', '-tags'], + 'kibana.alert.nested_field': 3, + } + : { + ...existingAlert, + count: 1, + url: `https://url1`, + tags: ['old-tag1', '-tags'], + kibana: { + // @ts-expect-error + ...existingAlert.kibana, + alert: { + // @ts-expect-error + ...existingAlert.kibana.alert, + nested_field: 3, + }, + }, + }; + + expect( + buildOngoingAlert< + { + count: number; + url: string; + [ALERT_ACTION_GROUP]: string; + 'kibana.alert.nested_field'?: number; + tags?: string[]; }, - maintenance_window_ids: [], - nested_field: 2, - start: '2023-03-28T12:27:28.159Z', - rule, - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', + {}, + {}, + 'error' | 'warning', + 'recovered' + >({ + // @ts-expect-error + alert, + legacyAlert, + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + payload: { + count: 2, + url: `https://url2`, + [ALERT_ACTION_GROUP]: 'bad action group', + 'kibana.alert.nested_field': 2, + tags: ['-tags', 'custom-tag2'], }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - url: `https://url2`, - tags: ['rule-', '-tags'], + kibanaVersion: '8.9.0', + }) + ).toEqual({ + ...alertRule, + count: 2, + url: `https://url2`, + 'kibana.alert.nested_field': 2, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'warning', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['-tags', 'custom-tag2', 'old-tag1', 'rule-'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + start: '2023-03-28T12:27:28.159Z', + uuid: 'abcdefg', + }, + }, + }), + }); }); - }); - test('should merge and de-dupe tags from existing alert, reported payload and rule tags', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A'); - legacyAlert - .scheduleActions('warning') - .replaceState({ start: '0000-00-00T00:00:00.000Z', duration: '36000000' }); + test('should not update alert document if no payload is specified', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert + .scheduleActions('warning') + .replaceState({ start: '2023-03-28T12:27:28.159Z', duration: '36000000' }); - expect( - buildOngoingAlert< - { - count: number; - url: string; - kibana?: { alert?: { action_group: string; nested_field?: number } }; - tags?: string[]; - }, - {}, - {}, - 'error' | 'warning', - 'recovered' - >({ - alert: { - ...existingAlert, - count: 1, - tags: ['old-tag1', '-tags'], - url: `https://url1`, - }, - legacyAlert, - rule: alertRule, - timestamp: '2023-03-29T12:27:28.159Z', - payload: { - count: 2, - url: `https://url2`, - kibana: { alert: { action_group: 'bad action group', nested_field: 2 } }, - tags: ['-tags', 'custom-tag2'], - }, - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - count: 2, - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'warning', - duration: { - us: '36000000', - }, - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: [], - nested_field: 2, - start: '2023-03-28T12:27:28.159Z', - rule, - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - url: `https://url2`, - tags: ['-tags', 'custom-tag2', 'old-tag1', 'rule-'], + const alert = flattened + ? { + ...existingAlert, + count: 1, + url: `https://url1`, + 'kibana.alert.nested_field': 3, + } + : { + ...existingAlert, + count: 1, + url: `https://url1`, + kibana: { + // @ts-expect-error + ...existingAlert.kibana, + alert: { + // @ts-expect-error + ...existingAlert.kibana.alert, + nested_field: 3, + }, + }, + }; + + expect( + buildOngoingAlert<{ count: number; url: string }, {}, {}, 'error' | 'warning', 'recovered'>( + { + // @ts-expect-error + alert, + legacyAlert, + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + kibanaVersion: '8.9.0', + } + ) + ).toEqual({ + ...alertRule, + count: 1, + url: `https://url1`, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'warning', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + 'kibana.alert.nested_field': 3, + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + nested_field: 3, + instance: { id: 'alert-A' }, + start: '2023-03-28T12:27:28.159Z', + uuid: 'abcdefg', + }, + }, + }), + }); }); - }); - test('should not update alert document if no payload is specified', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A'); - legacyAlert - .scheduleActions('warning') - .replaceState({ start: '0000-00-00T00:00:00.000Z', duration: '36000000' }); + test('should use workflow_status from payload if specified', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert + .scheduleActions('warning') + .replaceState({ start: '2023-03-28T12:27:28.159Z', duration: '36000000' }); - expect( - buildOngoingAlert<{ count: number; url: string }, {}, {}, 'error' | 'warning', 'recovered'>({ - alert: { - ...existingAlert, - count: 1, - url: `https://url1`, - }, - legacyAlert, - rule: alertRule, - timestamp: '2023-03-29T12:27:28.159Z', - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - count: 1, - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'warning', - duration: { - us: '36000000', - }, - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', + const alert = flattened + ? { + ...existingAlert, + count: 1, + url: `https://url1`, + 'kibana.alert.deeply.nested_field': 3, + } + : { + ...existingAlert, + count: 1, + url: `https://url1`, + kibana: { + // @ts-expect-error + ...existingAlert.kibana, + alert: { + // @ts-expect-error + ...existingAlert.kibana.alert, + deeply: { nested_field: 3 }, + }, + }, + }; + + expect( + buildOngoingAlert< + { + count: number; + url: string; + [ALERT_WORKFLOW_STATUS]: string; + 'kibana.alert.deeply.nested_field'?: number; }, - maintenance_window_ids: [], - start: '2023-03-28T12:27:28.159Z', - rule, - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', + {}, + {}, + 'error' | 'warning', + 'recovered' + >({ + // @ts-expect-error + alert, + legacyAlert, + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + kibanaVersion: '8.9.0', + payload: { + count: 2, + url: `https://url2`, + [ALERT_WORKFLOW_STATUS]: 'custom_status', + 'kibana.alert.deeply.nested_field': 2, }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - url: `https://url1`, - tags: ['rule-', '-tags'], + }) + ).toEqual({ + ...alertRule, + count: 2, + url: `https://url2`, + 'kibana.alert.deeply.nested_field': 2, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'warning', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_WORKFLOW_STATUS]: 'custom_status', + [ALERT_DURATION]: '36000000', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + start: '2023-03-28T12:27:28.159Z', + uuid: 'abcdefg', + }, + }, + }), + }); }); }); -}); +} diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/build_ongoing_alert.ts b/x-pack/plugins/alerting/server/alerts_client/lib/build_ongoing_alert.ts index 491c4dfe7cca7..f22c14e62b464 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/build_ongoing_alert.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/build_ongoing_alert.ts @@ -7,11 +7,26 @@ import deepmerge from 'deepmerge'; import type { Alert } from '@kbn/alerts-as-data-utils'; +import { + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_RULE_TAGS, + ALERT_TIME_RANGE, + EVENT_ACTION, + SPACE_IDS, + TAGS, + TIMESTAMP, + VERSION, +} from '@kbn/rule-data-utils'; import { DeepPartial } from '@kbn/utility-types'; import { Alert as LegacyAlert } from '../../alert/alert'; import { AlertInstanceContext, AlertInstanceState, RuleAlertData } from '../../types'; import type { AlertRule } from '../types'; import { stripFrameworkFields } from './strip_framework_fields'; +import { removeUnflattenedFieldsFromAlert, replaceRefreshableAlertFields } from './format_alert'; interface BuildOngoingAlertOpts< AlertData extends RuleAlertData, @@ -54,59 +69,71 @@ export const buildOngoingAlert = < RecoveryActionGroupId >): Alert & AlertData => { const cleanedPayload = stripFrameworkFields(payload); - return deepmerge.all( - [ - alert, - cleanedPayload, - { - // Update the timestamp to reflect latest update time - '@timestamp': timestamp, - event: { - action: 'active', - }, - kibana: { - alert: { - // Because we're building this alert after the action execution handler has been - // run, the scheduledExecutionOptions for the alert has been cleared and - // the lastScheduledActions has been set. If we ever change the order of operations - // to build and persist the alert before action execution handler, we will need to - // update where we pull the action group from. - // Set latest action group as this may have changed during execution (ex: error -> warning) - action_group: legacyAlert.getScheduledActionOptions()?.actionGroup, - // Set latest flapping state - flapping: legacyAlert.getFlapping(), - // Set latest flapping_history - flapping_history: legacyAlert.getFlappingHistory(), - // Set latest maintenance window IDs - maintenance_window_ids: legacyAlert.getMaintenanceWindowIds(), - // Set latest rule configuration - rule: rule.kibana?.alert.rule, - // Set latest duration as ongoing alerts should have updated duration - ...(legacyAlert.getState().duration - ? { duration: { us: legacyAlert.getState().duration } } - : {}), - // Fields that are explicitly not updated: - // event.kind - // instance.id - // status - ongoing alerts should maintain 'active' status - // uuid - ongoing alerts should carry over previous UUID - // start - ongoing alerts should keep the initial start time - // time_range - ongoing alerts should keep the initial time_range - // workflow_status - ongoing alerts should keep the initial workflow status - }, - space_ids: rule.kibana?.space_ids, - // Set latest kibana version - version: kibanaVersion, - }, - tags: Array.from( - new Set([ - ...((cleanedPayload?.tags as string[]) ?? []), - ...(alert.tags ?? []), - ...(rule.kibana?.alert.rule.tags ?? []), - ]) - ), - }, - ], - { arrayMerge: (_, sourceArray) => sourceArray } - ) as Alert & AlertData; + + // Make sure that any alert fields that are updateable are flattened. + const refreshableAlertFields = replaceRefreshableAlertFields(alert); + + const alertUpdates = { + // Set latest rule configuration + ...rule, + // Update the timestamp to reflect latest update time + [TIMESTAMP]: timestamp, + [EVENT_ACTION]: 'active', + // Because we're building this alert after the action execution handler has been + // run, the scheduledExecutionOptions for the alert has been cleared and + // the lastScheduledActions has been set. If we ever change the order of operations + // to build and persist the alert before action execution handler, we will need to + // update where we pull the action group from. + // Set latest action group as this may have changed during execution (ex: error -> warning) + [ALERT_ACTION_GROUP]: legacyAlert.getScheduledActionOptions()?.actionGroup, + // Set latest flapping state + [ALERT_FLAPPING]: legacyAlert.getFlapping(), + // Set latest flapping_history + [ALERT_FLAPPING_HISTORY]: legacyAlert.getFlappingHistory(), + // Set latest maintenance window IDs + [ALERT_MAINTENANCE_WINDOW_IDS]: legacyAlert.getMaintenanceWindowIds(), + // Set the time range + ...(legacyAlert.getState().start + ? { + [ALERT_TIME_RANGE]: { gte: legacyAlert.getState().start }, + } + : {}), + // Set latest duration as ongoing alerts should have updated duration + ...(legacyAlert.getState().duration + ? { [ALERT_DURATION]: legacyAlert.getState().duration } + : {}), + [SPACE_IDS]: rule[SPACE_IDS], + [VERSION]: kibanaVersion, + [TAGS]: Array.from( + new Set([ + ...((cleanedPayload?.tags as string[]) ?? []), + ...(alert.tags ?? []), + ...(rule[ALERT_RULE_TAGS] ?? []), + ]) + ), + }; + + // Clean the existing alert document so any nested fields that will be updated + // are removed, to avoid duplicate data. + // e.g. if the existing alert document has the field: + // { + // kibana: { + // alert: { + // field1: 'value1' + // } + // } + // } + // and the updated alert has the field + // { + // 'kibana.alert.field1': 'value2' + // } + // the expanded field from the existing alert is removed + const cleanedAlert = removeUnflattenedFieldsFromAlert(alert, { + ...cleanedPayload, + ...alertUpdates, + ...refreshableAlertFields, + }); + return deepmerge.all([cleanedAlert, refreshableAlertFields, cleanedPayload, alertUpdates], { + arrayMerge: (_, sourceArray) => sourceArray, + }) as Alert & AlertData; }; diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/build_recovered_alert.test.ts b/x-pack/plugins/alerting/server/alerts_client/lib/build_recovered_alert.test.ts index 03f538dc07ef3..73ff3e0a0f6e4 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/build_recovered_alert.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/build_recovered_alert.test.ts @@ -6,463 +6,552 @@ */ import { Alert as LegacyAlert } from '../../alert/alert'; import { buildRecoveredAlert } from './build_recovered_alert'; -import type { AlertRule } from '../types'; +import { + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + SPACE_IDS, + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_START, + ALERT_STATUS, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + EVENT_ACTION, + EVENT_KIND, + TAGS, + TIMESTAMP, + VERSION, + ALERT_TIME_RANGE, + ALERT_END, +} from '@kbn/rule-data-utils'; +import { + alertRule, + existingFlattenedActiveAlert, + existingExpandedActiveAlert, +} from './test_fixtures'; -const rule = { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', -}; -const alertRule: AlertRule = { - kibana: { - alert: { - rule, - }, - space_ids: ['default'], - }, -}; -const existingActiveAlert = { - '@timestamp': '2023-03-28T12:27:28.159Z', - event: { - action: 'active', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true, false], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: ['maint-x'], - start: '2023-03-28T12:27:28.159Z', - rule, - status: 'active', - time_range: { - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.1', - }, - tags: ['rule-', '-tags'], -}; +for (const flattened of [true, false]) { + const existingAlert = flattened ? existingFlattenedActiveAlert : existingExpandedActiveAlert; -describe('buildRecoveredAlert', () => { - test('should update active alert document with recovered status and info from legacy alert', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A'); - legacyAlert - .scheduleActions('default') - .replaceState({ end: '2023-03-30T12:27:28.159Z', duration: '36000000' }); + describe(`buildRecoveredAlert for ${flattened ? 'flattened' : 'expanded'} existing alert`, () => { + test('should return alert document with recovered status and info from legacy alert', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert.scheduleActions('default').replaceState({ + start: '2023-03-28T12:27:28.159Z', + end: '2023-03-30T12:27:28.159Z', + duration: '36000000', + }); - expect( - buildRecoveredAlert<{}, {}, {}, 'default', 'recovered'>({ - alert: existingActiveAlert, - legacyAlert, - rule: alertRule, - recoveryActionGroup: 'recovered', - timestamp: '2023-03-29T12:27:28.159Z', - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - event: { - action: 'close', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'recovered', - duration: { - us: '36000000', - }, - end: '2023-03-30T12:27:28.159Z', - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: [], - start: '2023-03-28T12:27:28.159Z', - rule, - status: 'recovered', - time_range: { - lte: '2023-03-30T12:27:28.159Z', - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], + expect( + buildRecoveredAlert<{}, {}, {}, 'default', 'recovered'>({ + // @ts-expect-error + alert: existingAlert, + legacyAlert, + rule: alertRule, + recoveryActionGroup: 'recovered', + timestamp: '2023-03-29T12:27:28.159Z', + kibanaVersion: '8.9.0', + }) + ).toEqual({ + ...alertRule, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'close', + [ALERT_ACTION_GROUP]: 'recovered', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'recovered', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_END]: '2023-03-30T12:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z', lte: '2023-03-30T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + uuid: 'abcdefg', + }, + }, + }), + }); }); - }); - test('should update active alert document with recovery status and updated rule data if rule definition has changed', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A'); - legacyAlert - .scheduleActions('default') - .replaceState({ end: '2023-03-30T12:27:28.159Z', duration: '36000000' }); - legacyAlert.setMaintenanceWindowIds(['maint-1', 'maint-321']); + test('should return alert document with recovery status and updated rule data if rule definition has changed', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert.scheduleActions('default').replaceState({ + start: '2023-03-28T12:27:28.159Z', + end: '2023-03-30T12:27:28.159Z', + duration: '36000000', + }); + legacyAlert.setMaintenanceWindowIds(['maint-1', 'maint-321']); - expect( - buildRecoveredAlert<{}, {}, {}, 'default', 'recovered'>({ - alert: existingActiveAlert, - legacyAlert, - recoveryActionGroup: 'NoLongerActive', - rule: { - kibana: { - alert: { - rule: { - ...rule, - name: 'updated-rule-name', - parameters: { - bar: false, + const updatedRule = { + ...alertRule, + [ALERT_RULE_NAME]: 'updated-rule-name', + [ALERT_RULE_PARAMETERS]: { bar: false }, + }; + + expect( + buildRecoveredAlert<{}, {}, {}, 'default', 'recovered'>({ + // @ts-expect-error + alert: existingAlert, + legacyAlert, + recoveryActionGroup: 'NoLongerActive', + rule: updatedRule, + timestamp: '2023-03-29T12:27:28.159Z', + kibanaVersion: '8.9.0', + }) + ).toEqual({ + ...updatedRule, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'close', + [ALERT_ACTION_GROUP]: 'NoLongerActive', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-1', 'maint-321'], + [ALERT_STATUS]: 'recovered', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_END]: '2023-03-30T12:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z', lte: '2023-03-30T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + uuid: 'abcdefg', }, }, - }, - space_ids: ['default'], - }, - }, - timestamp: '2023-03-29T12:27:28.159Z', - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - event: { - action: 'close', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'NoLongerActive', - duration: { - us: '36000000', - }, - end: '2023-03-30T12:27:28.159Z', - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: ['maint-1', 'maint-321'], - start: '2023-03-28T12:27:28.159Z', - rule: { - ...rule, - name: 'updated-rule-name', - parameters: { - bar: false, - }, - }, - status: 'recovered', - time_range: { - lte: '2023-03-30T12:27:28.159Z', - gte: '2023-03-28T12:27:28.159Z', - }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - tags: ['rule-', '-tags'], + }), + }); }); - }); - test('should update active alert document with updated payload if specified', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A'); - legacyAlert - .scheduleActions('default') - .replaceState({ end: '2023-03-30T12:27:28.159Z', duration: '36000000' }); - legacyAlert.setMaintenanceWindowIds(['maint-1', 'maint-321']); + test('should return alert document with updated payload if specified', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert.scheduleActions('default').replaceState({ + start: '2023-03-28T12:27:28.159Z', + end: '2023-03-30T12:27:28.159Z', + duration: '36000000', + }); + legacyAlert.setMaintenanceWindowIds(['maint-1', 'maint-321']); - expect( - buildRecoveredAlert< - { count: number; url: string; kibana?: { alert?: { nested_field?: number } } }, - {}, - {}, - 'default', - 'recovered' - >({ - alert: { - ...existingActiveAlert, - count: 1, - url: `https://url1`, - }, - legacyAlert, - recoveryActionGroup: 'NoLongerActive', - payload: { - count: 2, - url: `https://url2`, - kibana: { alert: { nested_field: 2 } }, - }, - rule: { - kibana: { - alert: { - rule: { - ...rule, - name: 'updated-rule-name', - parameters: { - bar: false, - }, + const alert = flattened + ? { + ...existingAlert, + count: 1, + url: `https://url1`, + 'kibana.alert.nested_field': 3, + } + : { + ...existingAlert, + count: 1, + url: `https://url1`, + kibana: { + // @ts-expect-error + ...existingAlert.kibana, + alert: { + // @ts-expect-error + ...existingAlert.kibana.alert, + nested_field: 3, }, }, - space_ids: ['default'], - }, - }, - timestamp: '2023-03-29T12:27:28.159Z', - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - count: 2, - event: { - action: 'close', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'NoLongerActive', - duration: { - us: '36000000', - }, - end: '2023-03-30T12:27:28.159Z', - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: ['maint-1', 'maint-321'], - nested_field: 2, - start: '2023-03-28T12:27:28.159Z', - rule: { - ...rule, - name: 'updated-rule-name', - parameters: { - bar: false, - }, - }, - status: 'recovered', - time_range: { - lte: '2023-03-30T12:27:28.159Z', - gte: '2023-03-28T12:27:28.159Z', + }; + + expect( + buildRecoveredAlert< + { count: number; url: string; 'kibana.alert.nested_field'?: number }, + {}, + {}, + 'default', + 'recovered' + >({ + // @ts-expect-error + alert, + legacyAlert, + recoveryActionGroup: 'NoLongerActive', + payload: { + count: 2, + url: `https://url2`, + 'kibana.alert.nested_field': 2, }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - url: `https://url2`, - tags: ['rule-', '-tags'], + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + kibanaVersion: '8.9.0', + }) + ).toEqual({ + ...alertRule, + count: 2, + url: `https://url2`, + 'kibana.alert.nested_field': 2, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'close', + [ALERT_ACTION_GROUP]: 'NoLongerActive', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-1', 'maint-321'], + [ALERT_STATUS]: 'recovered', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_END]: '2023-03-30T12:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z', lte: '2023-03-30T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + uuid: 'abcdefg', + }, + }, + }), + }); }); - }); - test('should merge and de-dupe tags from existing alert, reported recovery payload and rule tags', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A'); - legacyAlert - .scheduleActions('default') - .replaceState({ end: '2023-03-30T12:27:28.159Z', duration: '36000000' }); - legacyAlert.setMaintenanceWindowIds(['maint-1', 'maint-321']); + test('should merge and de-dupe tags from existing flattened alert, reported recovery payload and rule tags', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert.scheduleActions('default').replaceState({ + start: '2023-03-28T12:27:28.159Z', + end: '2023-03-30T12:27:28.159Z', + duration: '36000000', + }); + legacyAlert.setMaintenanceWindowIds(['maint-1', 'maint-321']); - expect( - buildRecoveredAlert< - { - count: number; - url: string; - kibana?: { alert?: { nested_field?: number } }; - tags?: string[]; - }, - {}, - {}, - 'default', - 'recovered' - >({ - alert: { - ...existingActiveAlert, - tags: ['active-alert-tag', 'rule-'], - count: 1, - url: `https://url1`, - }, - legacyAlert, - recoveryActionGroup: 'NoLongerActive', - payload: { - count: 2, - url: `https://url2`, - kibana: { alert: { nested_field: 2 } }, - tags: ['-tags', 'reported-recovery-tag'], - }, - rule: { - kibana: { - alert: { - rule: { - ...rule, - name: 'updated-rule-name', - parameters: { - bar: false, - }, + const alert = flattened + ? { + ...existingAlert, + count: 1, + url: `https://url1`, + tags: ['active-alert-tag', 'rule-'], + 'kibana.alert.nested_field': 3, + } + : { + ...existingAlert, + count: 1, + url: `https://url1`, + tags: ['active-alert-tag', 'rule-'], + kibana: { + // @ts-expect-error + ...existingAlert.kibana, + alert: { + // @ts-expect-error + ...existingAlert.kibana.alert, + nested_field: 3, }, }, - space_ids: ['default'], - }, - }, - timestamp: '2023-03-29T12:27:28.159Z', - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - count: 2, - event: { - action: 'close', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'NoLongerActive', - duration: { - us: '36000000', - }, - end: '2023-03-30T12:27:28.159Z', - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: ['maint-1', 'maint-321'], - nested_field: 2, - start: '2023-03-28T12:27:28.159Z', - rule: { - ...rule, - name: 'updated-rule-name', - parameters: { - bar: false, - }, + }; + + expect( + buildRecoveredAlert< + { + count: number; + url: string; + 'kibana.alert.nested_field'?: number; + tags?: string[]; }, - status: 'recovered', - time_range: { - lte: '2023-03-30T12:27:28.159Z', - gte: '2023-03-28T12:27:28.159Z', + {}, + {}, + 'default', + 'recovered' + >({ + // @ts-expect-error + alert, + legacyAlert, + recoveryActionGroup: 'NoLongerActive', + payload: { + count: 2, + url: `https://url2`, + 'kibana.alert.nested_field': 2, + tags: ['-tags', 'reported-recovery-tag'], }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - url: `https://url2`, - tags: ['-tags', 'reported-recovery-tag', 'active-alert-tag', 'rule-'], + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + kibanaVersion: '8.9.0', + }) + ).toEqual({ + ...alertRule, + count: 2, + url: `https://url2`, + 'kibana.alert.nested_field': 2, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'close', + [ALERT_ACTION_GROUP]: 'NoLongerActive', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-1', 'maint-321'], + [ALERT_STATUS]: 'recovered', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_END]: '2023-03-30T12:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z', lte: '2023-03-30T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['-tags', 'reported-recovery-tag', 'active-alert-tag', 'rule-'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + uuid: 'abcdefg', + }, + }, + }), + }); }); - }); - test('should update active alert document with updated payload if specified but not overwrite any framework fields', () => { - const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A'); - legacyAlert - .scheduleActions('default') - .replaceState({ end: '2023-03-30T12:27:28.159Z', duration: '36000000' }); - legacyAlert.setMaintenanceWindowIds(['maint-1', 'maint-321']); + test('should update flattened active alert document with updated payload if specified but not overwrite any framework fields', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert.scheduleActions('default').replaceState({ + start: '2023-03-28T12:27:28.159Z', + end: '2023-03-30T12:27:28.159Z', + duration: '36000000', + }); + legacyAlert.setMaintenanceWindowIds(['maint-1', 'maint-321']); - expect( - buildRecoveredAlert< - { - count: number; - url: string; - kibana?: { alert?: { action_group: string; nested_field?: number } }; - }, - {}, - {}, - 'default', - 'recovered' - >({ - alert: { - ...existingActiveAlert, - count: 1, - url: `https://url1`, - }, - legacyAlert, - recoveryActionGroup: 'NoLongerActive', - payload: { - count: 2, - url: `https://url2`, - kibana: { alert: { action_group: 'bad action group', nested_field: 2 } }, - }, - rule: { - kibana: { - alert: { - rule: { - ...rule, - name: 'updated-rule-name', - parameters: { - bar: false, - }, + const alert = flattened + ? { + ...existingAlert, + count: 1, + url: `https://url1`, + 'kibana.alert.nested_field': 3, + } + : { + ...existingAlert, + count: 1, + url: `https://url1`, + kibana: { + // @ts-expect-error + ...existingAlert.kibana, + alert: { + // @ts-expect-error + ...existingAlert.kibana.alert, + nested_field: 3, }, }, - space_ids: ['default'], - }, - }, - timestamp: '2023-03-29T12:27:28.159Z', - kibanaVersion: '8.9.0', - }) - ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', - count: 2, - event: { - action: 'close', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'NoLongerActive', - duration: { - us: '36000000', + }; + + expect( + buildRecoveredAlert< + { + count: number; + url: string; + [ALERT_ACTION_GROUP]: string; + 'kibana.alert.nested_field'?: number; }, - end: '2023-03-30T12:27:28.159Z', - flapping: false, - flapping_history: [], - instance: { - id: 'alert-A', + {}, + {}, + 'default', + 'recovered' + >({ + // @ts-expect-error + alert, + legacyAlert, + recoveryActionGroup: 'NoLongerActive', + payload: { + count: 2, + url: `https://url2`, + [ALERT_ACTION_GROUP]: 'bad action group', + 'kibana.alert.nested_field': 2, }, - maintenance_window_ids: ['maint-1', 'maint-321'], - nested_field: 2, - start: '2023-03-28T12:27:28.159Z', - rule: { - ...rule, - name: 'updated-rule-name', - parameters: { - bar: false, + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + kibanaVersion: '8.9.0', + }) + ).toEqual({ + ...alertRule, + count: 2, + url: `https://url2`, + 'kibana.alert.nested_field': 2, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'close', + [ALERT_ACTION_GROUP]: 'NoLongerActive', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-1', 'maint-321'], + [ALERT_STATUS]: 'recovered', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_END]: '2023-03-30T12:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z', lte: '2023-03-30T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + uuid: 'abcdefg', + }, + }, + }), + }); + }); + + test('should use workflow_status from payload if specified', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', { + meta: { uuid: 'abcdefg' }, + }); + legacyAlert.scheduleActions('warning').replaceState({ + start: '2023-03-28T12:27:28.159Z', + end: '2023-03-30T12:27:28.159Z', + duration: '36000000', + }); + + const alert = flattened + ? { + ...existingAlert, + count: 1, + url: `https://url1`, + 'kibana.alert.deeply.nested_field': 3, + } + : { + ...existingAlert, + count: 1, + url: `https://url1`, + kibana: { + // @ts-expect-error + ...existingAlert.kibana, + alert: { + // @ts-expect-error + ...existingAlert.kibana.alert, + deeply: { nested_field: 3 }, + }, }, + }; + + expect( + buildRecoveredAlert< + { + count: number; + url: string; + [ALERT_WORKFLOW_STATUS]: string; + 'kibana.alert.deeply.nested_field'?: number; }, - status: 'recovered', - time_range: { - lte: '2023-03-30T12:27:28.159Z', - gte: '2023-03-28T12:27:28.159Z', + {}, + {}, + 'error' | 'warning', + 'recovered' + >({ + // @ts-expect-error + alert, + legacyAlert, + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + kibanaVersion: '8.9.0', + recoveryActionGroup: 'NoLongerActive', + payload: { + count: 2, + url: `https://url2`, + [ALERT_WORKFLOW_STATUS]: 'custom_status', + 'kibana.alert.deeply.nested_field': 2, }, - uuid: 'abcdefg', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.9.0', - }, - url: `https://url2`, - tags: ['rule-', '-tags'], + }) + ).toEqual({ + ...alertRule, + count: 2, + url: `https://url2`, + 'kibana.alert.deeply.nested_field': 2, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'close', + [ALERT_ACTION_GROUP]: 'NoLongerActive', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'recovered', + [ALERT_WORKFLOW_STATUS]: 'custom_status', + [ALERT_DURATION]: '36000000', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_END]: '2023-03-30T12:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z', lte: '2023-03-30T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + ...(flattened + ? { + [EVENT_KIND]: 'signal', + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_UUID]: 'abcdefg', + } + : { + event: { + kind: 'signal', + }, + kibana: { + alert: { + instance: { id: 'alert-A' }, + uuid: 'abcdefg', + }, + }, + }), + }); }); }); -}); +} diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/build_recovered_alert.ts b/x-pack/plugins/alerting/server/alerts_client/lib/build_recovered_alert.ts index b283844acbc63..be6d7ff033dd1 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/build_recovered_alert.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/build_recovered_alert.ts @@ -6,11 +6,29 @@ */ import deepmerge from 'deepmerge'; import type { Alert } from '@kbn/alerts-as-data-utils'; +import { + ALERT_RULE_TAGS, + SPACE_IDS, + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_STATUS, + EVENT_ACTION, + TAGS, + TIMESTAMP, + VERSION, + ALERT_END, + ALERT_TIME_RANGE, + ALERT_START, +} from '@kbn/rule-data-utils'; import { DeepPartial } from '@kbn/utility-types'; import { Alert as LegacyAlert } from '../../alert/alert'; import { AlertInstanceContext, AlertInstanceState, RuleAlertData } from '../../types'; import type { AlertRule } from '../types'; import { stripFrameworkFields } from './strip_framework_fields'; +import { removeUnflattenedFieldsFromAlert, replaceRefreshableAlertFields } from './format_alert'; interface BuildRecoveredAlertOpts< AlertData extends RuleAlertData, @@ -55,65 +73,76 @@ export const buildRecoveredAlert = < RecoveryActionGroupId >): Alert & AlertData => { const cleanedPayload = stripFrameworkFields(payload); - return deepmerge.all( - [ - alert, - cleanedPayload, - { - // Update the timestamp to reflect latest update time - '@timestamp': timestamp, - event: { - action: 'close', - }, - kibana: { - alert: { - // Set the recovery action group - action_group: recoveryActionGroup, - // Set latest flapping state - flapping: legacyAlert.getFlapping(), - // Set latest flapping_history - flapping_history: legacyAlert.getFlappingHistory(), - // Set latest maintenance window IDs - maintenance_window_ids: legacyAlert.getMaintenanceWindowIds(), - // Set latest rule configuration - rule: rule.kibana?.alert.rule, - // Set status to 'recovered' - status: 'recovered', - // Set latest duration as recovered alerts should have updated duration - ...(legacyAlert.getState().duration - ? { duration: { us: legacyAlert.getState().duration } } - : {}), - // Set end time - ...(legacyAlert.getState().end - ? { - end: legacyAlert.getState().end, - time_range: { - // this should get merged with a time_range.gte - lte: legacyAlert.getState().end, - }, - } - : {}), - // Fields that are explicitly not updated: - // instance.id - // action_group - // uuid - recovered alerts should carry over previous UUID - // start - recovered alerts should keep the initial start time - // workflow_status - recovered alerts should keep the initial workflow_status + // Make sure that any alert fields that are updateable are flattened. + const refreshableAlertFields = replaceRefreshableAlertFields(alert); + + const alertUpdates = { + // Set latest rule configuration + ...rule, + // Update the timestamp to reflect latest update time + [TIMESTAMP]: timestamp, + [EVENT_ACTION]: 'close', + // Set the recovery action group + [ALERT_ACTION_GROUP]: recoveryActionGroup, + // Set latest flapping state + [ALERT_FLAPPING]: legacyAlert.getFlapping(), + // Set latest flapping_history + [ALERT_FLAPPING_HISTORY]: legacyAlert.getFlappingHistory(), + // Set latest maintenance window IDs + [ALERT_MAINTENANCE_WINDOW_IDS]: legacyAlert.getMaintenanceWindowIds(), + // Set status to 'recovered' + [ALERT_STATUS]: 'recovered', + // Set latest duration as recovered alerts should have updated duration + ...(legacyAlert.getState().duration + ? { [ALERT_DURATION]: legacyAlert.getState().duration } + : {}), + // Set end time + ...(legacyAlert.getState().end && legacyAlert.getState().start + ? { + [ALERT_START]: legacyAlert.getState().start, + [ALERT_END]: legacyAlert.getState().end, + [ALERT_TIME_RANGE]: { + gte: legacyAlert.getState().start, + lte: legacyAlert.getState().end, }, - space_ids: rule.kibana?.space_ids, - // Set latest kibana version - version: kibanaVersion, - }, - tags: Array.from( - new Set([ - ...((cleanedPayload?.tags as string[]) ?? []), - ...(alert.tags ?? []), - ...(rule.kibana?.alert.rule.tags ?? []), - ]) - ), - }, - ], - { arrayMerge: (_, sourceArray) => sourceArray } - ) as Alert & AlertData; + } + : {}), + + [SPACE_IDS]: rule[SPACE_IDS], + // Set latest kibana version + [VERSION]: kibanaVersion, + [TAGS]: Array.from( + new Set([ + ...((cleanedPayload?.tags as string[]) ?? []), + ...(alert.tags ?? []), + ...(rule[ALERT_RULE_TAGS] ?? []), + ]) + ), + }; + + // Clean the existing alert document so any nested fields that will be updated + // are removed, to avoid duplicate data. + // e.g. if the existing alert document has the field: + // { + // kibana: { + // alert: { + // field1: 'value1' + // } + // } + // } + // and the updated alert has the field + // { + // 'kibana.alert.field1': 'value2' + // } + // the expanded field from the existing alert is removed + const cleanedAlert = removeUnflattenedFieldsFromAlert(alert, { + ...cleanedPayload, + ...alertUpdates, + ...refreshableAlertFields, + }); + + return deepmerge.all([cleanedAlert, refreshableAlertFields, cleanedPayload, alertUpdates], { + arrayMerge: (_, sourceArray) => sourceArray, + }) as Alert & AlertData; }; diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/build_updated_recovered_alert.test.ts b/x-pack/plugins/alerting/server/alerts_client/lib/build_updated_recovered_alert.test.ts index e4793cecc71d3..1c646f08d7828 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/build_updated_recovered_alert.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/build_updated_recovered_alert.test.ts @@ -5,61 +5,81 @@ * 2.0. */ import { Alert as LegacyAlert } from '../../alert/alert'; -import { AlertRule } from '../types'; import { buildUpdatedRecoveredAlert } from './build_updated_recovered_alert'; +import { + SPACE_IDS, + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_START, + ALERT_STATUS, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + EVENT_ACTION, + EVENT_KIND, + TAGS, + TIMESTAMP, + VERSION, + ALERT_TIME_RANGE, + ALERT_END, +} from '@kbn/rule-data-utils'; +import { + alertRule, + existingFlattenedRecoveredAlert, + existingExpandedRecoveredAlert, +} from './test_fixtures'; -const rule = { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', -}; - -const alertRule: AlertRule = { - kibana: { - alert: { - rule, - }, - space_ids: ['default'], - }, -}; +describe('buildUpdatedRecoveredAlert', () => { + test('should update already recovered flattened alert document with updated flapping values and timestamp only', () => { + const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A'); + legacyAlert.scheduleActions('default'); + legacyAlert.setFlappingHistory([false, false, true, true]); + legacyAlert.setMaintenanceWindowIds(['maint-1', 'maint-321']); -const existingRecoveredAlert = { - '@timestamp': '2023-03-28T12:27:28.159Z', - kibana: { - alert: { - action_group: 'recovered', - duration: { - us: '0', - }, - end: '2023-03-28T12:27:28.159Z', - flapping: false, - flapping_history: [true, false, false], - instance: { - id: 'alert-A', - }, - maintenance_window_ids: ['maint-x'], - start: '2023-03-27T12:27:28.159Z', - rule, - status: 'recovered', - uuid: 'abcdefg', - }, - space_ids: ['default'], - }, -}; + expect( + buildUpdatedRecoveredAlert<{}>({ + alert: existingFlattenedRecoveredAlert, + legacyRawAlert: { + meta: { + flapping: true, + flappingHistory: [false, false, true, true], + maintenanceWindowIds: ['maint-1', 'maint-321'], + }, + state: { + start: '3023-03-27T12:27:28.159Z', + }, + }, + rule: alertRule, + timestamp: '2023-03-29T12:27:28.159Z', + }) + ).toEqual({ + ...alertRule, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'close', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'recovered', + [ALERT_DURATION]: '36000000', + [ALERT_START]: '2023-03-27T12:27:28.159Z', + [ALERT_END]: '2023-03-30T12:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-27T12:27:28.159Z', lte: '2023-03-30T12:27:28.159Z' }, + [ALERT_FLAPPING]: true, + [ALERT_FLAPPING_HISTORY]: [false, false, true, true], + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-x'], + [ALERT_STATUS]: 'recovered', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_UUID]: 'abcdefg', + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.8.1', + [TAGS]: ['rule-', '-tags'], + }); + }); -describe('buildUpdatedRecoveredAlert', () => { - test('should update already recovered alert document with updated flapping values and timestamp only', () => { + test('should update already recovered expanded alert document with updated flapping values and timestamp only', () => { const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A'); legacyAlert.scheduleActions('default'); legacyAlert.setFlappingHistory([false, false, true, true]); @@ -67,7 +87,8 @@ describe('buildUpdatedRecoveredAlert', () => { expect( buildUpdatedRecoveredAlert<{}>({ - alert: existingRecoveredAlert, + // @ts-expect-error + alert: existingExpandedRecoveredAlert, legacyRawAlert: { meta: { flapping: true, @@ -82,27 +103,38 @@ describe('buildUpdatedRecoveredAlert', () => { timestamp: '2023-03-29T12:27:28.159Z', }) ).toEqual({ - '@timestamp': '2023-03-29T12:27:28.159Z', + ...alertRule, + event: { + action: 'close', + kind: 'signal', + }, kibana: { alert: { action_group: 'recovered', duration: { - us: '0', + us: '36000000', }, - end: '2023-03-28T12:27:28.159Z', - flapping: true, - flapping_history: [false, false, true, true], + end: '2023-03-30T12:27:28.159Z', instance: { id: 'alert-A', }, maintenance_window_ids: ['maint-x'], - start: '2023-03-27T12:27:28.159Z', - rule, - status: 'recovered', + start: '2023-03-28T12:27:28.159Z', + time_range: { + gte: '2023-03-27T12:27:28.159Z', + lte: '2023-03-30T12:27:28.159Z', + }, uuid: 'abcdefg', }, - space_ids: ['default'], + version: '8.8.1', }, + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [ALERT_FLAPPING]: true, + [ALERT_FLAPPING_HISTORY]: [false, false, true, true], + [ALERT_STATUS]: 'recovered', + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [TAGS]: ['rule-', '-tags'], }); }); }); diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/build_updated_recovered_alert.ts b/x-pack/plugins/alerting/server/alerts_client/lib/build_updated_recovered_alert.ts index 8954bba332fc6..59c9a5cb35874 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/build_updated_recovered_alert.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/build_updated_recovered_alert.ts @@ -7,9 +7,11 @@ import deepmerge from 'deepmerge'; import type { Alert } from '@kbn/alerts-as-data-utils'; +import { ALERT_FLAPPING, ALERT_FLAPPING_HISTORY, TIMESTAMP } from '@kbn/rule-data-utils'; import { RawAlertInstance } from '@kbn/alerting-state-types'; import { RuleAlertData } from '../../types'; import { AlertRule } from '../types'; +import { removeUnflattenedFieldsFromAlert, replaceRefreshableAlertFields } from './format_alert'; interface BuildUpdatedRecoveredAlertOpts { alert: Alert & AlertData; @@ -29,24 +31,41 @@ export const buildUpdatedRecoveredAlert = ({ rule, timestamp, }: BuildUpdatedRecoveredAlertOpts): Alert & AlertData => { - return deepmerge.all( - [ - alert, - { - // Update the timestamp to reflect latest update time - '@timestamp': timestamp, - kibana: { - alert: { - // Set latest flapping state - flapping: legacyRawAlert.meta?.flapping, - // Set latest flapping history - flapping_history: legacyRawAlert.meta?.flappingHistory, - // Set latest rule configuration - rule: rule.kibana?.alert.rule, - }, - }, - }, - ], - { arrayMerge: (_, sourceArray) => sourceArray } - ) as Alert & AlertData; + // Make sure that any alert fields that are updateable are flattened. + const refreshableAlertFields = replaceRefreshableAlertFields(alert); + + const alertUpdates = { + // Set latest rule configuration + ...rule, + // Update the timestamp to reflect latest update time + [TIMESTAMP]: timestamp, + // Set latest flapping state + [ALERT_FLAPPING]: legacyRawAlert.meta?.flapping, + // Set latest flapping history + [ALERT_FLAPPING_HISTORY]: legacyRawAlert.meta?.flappingHistory, + }; + + // Clean the existing alert document so any nested fields that will be updated + // are removed, to avoid duplicate data. + // e.g. if the existing alert document has the field: + // { + // kibana: { + // alert: { + // field1: 'value1' + // } + // } + // } + // and the updated alert has the field + // { + // 'kibana.alert.field1': 'value2' + // } + // the expanded field from the existing alert is removed + const cleanedAlert = removeUnflattenedFieldsFromAlert(alert, { + ...alertUpdates, + ...refreshableAlertFields, + }); + + return deepmerge.all([cleanedAlert, refreshableAlertFields, alertUpdates], { + arrayMerge: (_, sourceArray) => sourceArray, + }) as Alert & AlertData; }; diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/format_alert.test.ts b/x-pack/plugins/alerting/server/alerts_client/lib/format_alert.test.ts new file mode 100644 index 0000000000000..a96cfb715d559 --- /dev/null +++ b/x-pack/plugins/alerting/server/alerts_client/lib/format_alert.test.ts @@ -0,0 +1,257 @@ +/* + * 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 { + expandFlattenedAlert, + compactObject, + removeUnflattenedFieldsFromAlert, +} from './format_alert'; +import { + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_REASON, + ALERT_RULE_CATEGORY, + ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + ALERT_RULE_PRODUCER, + ALERT_RULE_REVISION, + ALERT_RULE_TAGS, + ALERT_RULE_TYPE_ID, + ALERT_RULE_UUID, + ALERT_START, + ALERT_STATUS, + ALERT_TIME_RANGE, + ALERT_URL, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + EVENT_ACTION, + EVENT_KIND, + SPACE_IDS, + TAGS, + TIMESTAMP, + VERSION, +} from '@kbn/rule-data-utils'; + +describe('expandFlattenedAlert', () => { + test('should correctly expand flattened alert', () => { + expect( + expandFlattenedAlert({ + [TIMESTAMP]: '2023-03-29T12:27:28.159Z', + [EVENT_ACTION]: 'active', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'warning', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_UUID]: 'abcdefg', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_DURATION]: '36000000', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + }) + ).toEqual({ + '@timestamp': '2023-03-29T12:27:28.159Z', + event: { + action: 'active', + kind: 'signal', + }, + kibana: { + alert: { + action_group: 'warning', + duration: { + us: '36000000', + }, + flapping: false, + flapping_history: [], + instance: { + id: 'alert-A', + }, + maintenance_window_ids: [], + rule: { + category: 'My test rule', + consumer: 'bar', + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + name: 'rule-name', + parameters: { + bar: true, + }, + producer: 'alerts', + revision: 0, + rule_type_id: 'test.rule-type', + tags: ['rule-', '-tags'], + uuid: '1', + }, + start: '2023-03-28T12:27:28.159Z', + status: 'active', + time_range: { + gte: '2023-03-28T12:27:28.159Z', + }, + uuid: 'abcdefg', + workflow_status: 'open', + }, + space_ids: ['default'], + version: '8.9.0', + }, + tags: ['rule-', '-tags'], + }); + }); +}); + +describe('removeUnflattenedFieldsFromAlert', () => { + test('should correctly remove duplicate data from alert', () => { + expect( + removeUnflattenedFieldsFromAlert( + { + '@timestamp': '2023-03-29T12:27:28.159Z', + event: { + action: 'active', + kind: 'signal', + }, + kibana: { + alert: { + action_group: 'warning', + duration: { + us: '36000000', + }, + evaluation: { + conditions: 'matched query', + value: '123', + }, + flapping: false, + flapping_history: [], + instance: { + id: 'alert-A', + }, + maintenance_window_ids: [], + reason: 'because i said so', + rule: { + category: 'My test rule', + consumer: 'bar', + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + name: 'rule-name', + parameters: { + bar: true, + }, + producer: 'alerts', + revision: 0, + rule_type_id: 'test.rule-type', + tags: ['rule-', '-tags'], + uuid: '1', + }, + start: '2023-03-28T12:27:28.159Z', + status: 'active', + time_range: { + gte: '2023-03-28T12:27:28.159Z', + }, + title: 'this is an alert', + uuid: 'abcdefg', + url: 'https://alert.url/abcdefg', + workflow_status: 'open', + }, + space_ids: ['default'], + version: '8.9.0', + }, + tags: ['rule-', '-tags'], + }, + { + [TIMESTAMP]: '2023-03-30T12:27:28.159Z', + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'warning', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_DURATION]: '36000000', + [SPACE_IDS]: ['default'], + [VERSION]: '8.9.0', + [TAGS]: ['rule-', '-tags'], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_URL]: 'https://abc', + [ALERT_REASON]: 'because', + 'kibana.alert.evaluation.conditions': 'condition', + } + ) + ).toEqual({ + '@timestamp': '2023-03-29T12:27:28.159Z', + event: { + kind: 'signal', + }, + kibana: { + alert: { + evaluation: { + value: '123', + }, + instance: { + id: 'alert-A', + }, + start: '2023-03-28T12:27:28.159Z', + status: 'active', + time_range: { + gte: '2023-03-28T12:27:28.159Z', + }, + title: 'this is an alert', + uuid: 'abcdefg', + workflow_status: 'open', + }, + }, + tags: ['rule-', '-tags'], + }); + }); +}); + +describe('compactObject', () => { + test('should compact object as expected', () => { + expect(compactObject({ kibana: { alert: { rule: { execution: {} } }, rule: {} } })).toEqual({}); + expect( + compactObject({ + kibana: { + rule: 34, + testField: [], + alert: { rule: { execution: {}, nested_field: ['a', 'b'] } }, + }, + }) + ).toEqual({ + kibana: { rule: 34, testField: [], alert: { rule: { nested_field: ['a', 'b'] } } }, + }); + }); + expect(compactObject({ 'kibana.alert.rule.execution': {} })).toEqual({}); + expect( + compactObject({ 'kibana.alert.rule.execution': {}, 'kibana.alert.nested_field': ['a', 'b'] }) + ).toEqual({ 'kibana.alert.nested_field': ['a', 'b'] }); +}); diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/format_alert.ts b/x-pack/plugins/alerting/server/alerts_client/lib/format_alert.ts new file mode 100644 index 0000000000000..a81bdb35ce175 --- /dev/null +++ b/x-pack/plugins/alerting/server/alerts_client/lib/format_alert.ts @@ -0,0 +1,96 @@ +/* + * 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 { cloneDeep, get, isEmpty, merge, omit } from 'lodash'; +import type { Alert } from '@kbn/alerts-as-data-utils'; +import { RuleAlertData } from '../../types'; +import { REFRESH_FIELDS_ALL } from './alert_conflict_resolver'; + +const expandDottedField = (dottedFieldName: string, val: unknown): object => { + const parts = dottedFieldName.split('.'); + if (parts.length === 1) { + return { [parts[0]]: val }; + } else { + return { [parts[0]]: expandDottedField(parts.slice(1).join('.'), val) }; + } +}; + +export const expandFlattenedAlert = (alert: object) => { + return Object.entries(alert).reduce( + (acc, [key, val]) => merge(acc, expandDottedField(key, val)), + {} + ); +}; + +type Obj = Record; + +// Removes empty nested objects +export const compactObject = (obj: Obj) => { + return Object.keys(obj) + .filter((key: string) => { + // just filter out empty objects + // keep any primitives or arrays, even empty arrays + return ( + !!obj[key] && + (Array.isArray(obj[key]) || + typeof obj[key] !== 'object' || + (typeof obj[key] === 'object' && !isEmpty(obj[key]))) + ); + }) + .reduce((acc, curr) => { + if (typeof obj[curr] !== 'object' || Array.isArray(obj[curr])) { + acc[curr] = obj[curr]; + } else { + const compacted = compactObject(obj[curr] as Obj); + if (!isEmpty(compacted)) { + acc[curr] = compacted; + } + } + return acc; + }, {}); +}; + +/** + * If we're replacing field values in an unflattened alert + * with the flattened version, we want to remove the unflattened version + * to avoid duplicate data in the doc + */ + +export const removeUnflattenedFieldsFromAlert = ( + alert: Record, + flattenedData: object +) => { + // make a copy of the alert + let alertCopy = cloneDeep(alert); + + // for each flattened field in the flattened data object, + // check whether that path exists in the unflattened alert + // and omit it if it does + Object.keys(flattenedData).forEach((payloadKey: string) => { + const val = get(alertCopy, payloadKey, null); + if (null == alertCopy[payloadKey] && null != val) { + alertCopy = omit(alertCopy, payloadKey); + } + }); + return compactObject(alertCopy); +}; + +export const replaceRefreshableAlertFields = ( + alert: Alert & AlertData +) => { + // Make sure that any alert fields that are updateable are flattened. + return REFRESH_FIELDS_ALL.reduce>( + (acc: Record, currField) => { + const value = get(alert, currField); + if (null != value) { + acc[currField] = value; + } + return acc; + }, + {} + ); +}; diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts b/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts index bef787ad369de..9f335f8266b21 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts @@ -7,6 +7,19 @@ import { formatRule } from './format_rule'; import { UntypedNormalizedRuleType } from '../../rule_type_registry'; import { RecoveredActionGroup } from '../../types'; +import { + ALERT_RULE_CATEGORY, + ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + ALERT_RULE_PRODUCER, + ALERT_RULE_REVISION, + ALERT_RULE_TAGS, + ALERT_RULE_TYPE_ID, + ALERT_RULE_UUID, + SPACE_IDS, +} from '@kbn/rule-data-utils'; const ruleType: jest.Mocked = { id: 'test.rule-type', @@ -52,27 +65,17 @@ describe('formatRule', () => { ruleType, }) ).toEqual({ - kibana: { - alert: { - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test.rule-type', - tags: ['rule-', '-tags'], - uuid: '1', - }, - }, - space_ids: ['default'], - }, + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test.rule-type', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [SPACE_IDS]: ['default'], }); }); }); diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.ts b/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.ts index 70588fc4cb665..33281b918c2a9 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.ts @@ -4,6 +4,19 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { + ALERT_RULE_CATEGORY, + ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + ALERT_RULE_PRODUCER, + ALERT_RULE_REVISION, + ALERT_RULE_TAGS, + ALERT_RULE_TYPE_ID, + ALERT_RULE_UUID, + SPACE_IDS, +} from '@kbn/rule-data-utils'; import type { AlertRule, AlertRuleData } from '../types'; import { UntypedNormalizedRuleType } from '../../rule_type_registry'; @@ -14,24 +27,16 @@ interface FormatRuleOpts { export const formatRule = ({ rule, ruleType }: FormatRuleOpts): AlertRule => { return { - kibana: { - alert: { - rule: { - category: ruleType.name, - consumer: rule.consumer, - execution: { - uuid: rule.executionId, - }, - name: rule.name, - parameters: rule.parameters, - producer: ruleType.producer, - revision: rule.revision, - rule_type_id: ruleType.id, - tags: rule.tags, - uuid: rule.id, - }, - }, - space_ids: [rule.spaceId], - }, + [ALERT_RULE_CATEGORY]: ruleType.name, + [ALERT_RULE_CONSUMER]: rule.consumer, + [ALERT_RULE_EXECUTION_UUID]: rule.executionId, + [ALERT_RULE_NAME]: rule.name, + [ALERT_RULE_PARAMETERS]: rule.parameters, + [ALERT_RULE_PRODUCER]: ruleType.producer, + [ALERT_RULE_REVISION]: rule.revision, + [ALERT_RULE_TYPE_ID]: ruleType.id, + [ALERT_RULE_TAGS]: rule.tags, + [ALERT_RULE_UUID]: rule.id, + [SPACE_IDS]: [rule.spaceId], }; }; diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/get_summarized_alerts_query.ts b/x-pack/plugins/alerting/server/alerts_client/lib/get_summarized_alerts_query.ts index 302d244aa9153..917a6dad803d7 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/get_summarized_alerts_query.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/get_summarized_alerts_query.ts @@ -10,7 +10,6 @@ import { SearchRequest, SearchTotalHits, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { merge } from 'lodash'; import { ALERT_END, ALERT_INSTANCE_ID, @@ -33,6 +32,7 @@ import { } from '../types'; import { SummarizedAlertsChunk } from '../..'; import { FormatAlert } from '../../types'; +import { expandFlattenedAlert } from './format_alert'; const MAX_ALERT_DOCS_TO_RETURN = 100; enum AlertTypes { @@ -270,20 +270,6 @@ const getQueryByTimeRange = ({ }; }; -const expandFlattenedAlert = (alert: object) => { - return Object.entries(alert).reduce( - (acc, [key, val]) => merge(acc, expandDottedField(key, val)), - {} - ); -}; -const expandDottedField = (dottedFieldName: string, val: unknown): object => { - const parts = dottedFieldName.split('.'); - if (parts.length === 1) { - return { [parts[0]]: val }; - } else { - return { [parts[0]]: expandDottedField(parts.slice(1).join('.'), val) }; - } -}; const generateAlertsFilterDSL = (alertsFilter: AlertsFilter): QueryDslQueryContainer[] => { const filter: QueryDslQueryContainer[] = []; @@ -426,9 +412,4 @@ const getContinualAlertsQuery = ({ return queryBody; }; -export { - getHitsWithCount, - expandFlattenedAlert, - getLifecycleAlertsQueries, - getContinualAlertsQuery, -}; +export { getHitsWithCount, getLifecycleAlertsQueries, getContinualAlertsQuery }; diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/index.ts b/x-pack/plugins/alerting/server/alerts_client/lib/index.ts index a566c3be9ea7e..7225e87056e4f 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/index.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/index.ts @@ -14,5 +14,5 @@ export { getHitsWithCount, getLifecycleAlertsQueries, getContinualAlertsQuery, - expandFlattenedAlert, } from './get_summarized_alerts_query'; +export { expandFlattenedAlert } from './format_alert'; diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/strip_framework_fields.test.ts b/x-pack/plugins/alerting/server/alerts_client/lib/strip_framework_fields.test.ts index 5e61cbda2ac92..42e377d870ac3 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/strip_framework_fields.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/strip_framework_fields.test.ts @@ -16,6 +16,11 @@ describe('stripFrameworkFields', () => { expect(stripFrameworkFields(payload)).toEqual(payload); }); + test('should do nothing if flattened payload has no framework fields', () => { + const payload = { field1: 'test', 'kibana.alert.not_a_framework_field': 2 }; + expect(stripFrameworkFields(payload)).toEqual(payload); + }); + test(`should allow fields from the allowlist`, () => { const payload = { field1: 'test', @@ -27,6 +32,17 @@ describe('stripFrameworkFields', () => { expect(stripFrameworkFields(payload)).toEqual(payload); }); + test(`should allow fields from the allowlist in flattened payload`, () => { + const payload = { + field1: 'test', + 'kibana.alert.not_a_framework_field': 2, + 'kibana.alert.reason': 'because i said so', + 'kibana.alert.workflow_status': 'custom', + tags: ['taggity-tag'], + }; + expect(stripFrameworkFields(payload)).toEqual(payload); + }); + test(`should strip fields that the framework controls`, () => { const payload = { field1: 'test', @@ -95,4 +111,44 @@ describe('stripFrameworkFields', () => { }, }); }); + + test(`should strip flattened fields that the framework controls`, () => { + const payload = { + field1: 'test', + field2: [], + 'kibana.alert.action_group': 'invalid action group', + 'kibana.alert.not_a_framework_field1': 2, + 'kibana.alert.not_a_framework_field2': [], + 'kibana.alert.not_a_framework_field3.abc': 'xyz', + 'kibana.alert.instance.id': 'A', + 'kibana.alert.duration.us': '23543543534', + 'kibana.alert.case_ids': ['abcdefg'], + 'kibana.alert.start': 'datestring', + 'kibana.alert.status': 'bad', + 'kibana.alert.end': 'datestring', + 'kibana.alert.flapping': true, + 'kibana.alert.flapping_history': [true], + 'kibana.alert.maintenance_window_ids': ['xyz'], + 'kibana.alert.rule.category': 'My test rule', + 'kibana.alert.rule.consumer': 'bar', + 'kibana.alert.rule.execution.uuid': '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + 'kibana.alert.rule.name': 'rule-name', + 'kibana.alert.rule.parameters': { + bar: true, + }, + 'kibana.alert.rule.producer': 'alerts', + 'kibana.alert.rule.revision': 0, + 'kibana.alert.rule.rule_type_id': 'test.rule-type', + 'kibana.alert.rule.tags': ['rule-', '-tags'], + 'kibana.alert.rule.uuid': '1', + 'kibana.alert.uuid': 'uuid', + }; + expect(stripFrameworkFields(payload)).toEqual({ + field1: 'test', + field2: [], + 'kibana.alert.not_a_framework_field1': 2, + 'kibana.alert.not_a_framework_field2': [], + 'kibana.alert.not_a_framework_field3.abc': 'xyz', + }); + }); }); diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/test_fixtures.ts b/x-pack/plugins/alerting/server/alerts_client/lib/test_fixtures.ts new file mode 100644 index 0000000000000..1a8e2be1e16a4 --- /dev/null +++ b/x-pack/plugins/alerting/server/alerts_client/lib/test_fixtures.ts @@ -0,0 +1,118 @@ +/* + * 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 { + ALERT_RULE_CATEGORY, + ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + ALERT_RULE_PRODUCER, + ALERT_RULE_REVISION, + ALERT_RULE_TYPE_ID, + ALERT_RULE_TAGS, + ALERT_RULE_UUID, + SPACE_IDS, + ALERT_ACTION_GROUP, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_STATUS, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + EVENT_ACTION, + EVENT_KIND, + TAGS, + TIMESTAMP, + VERSION, + ALERT_DURATION, + ALERT_START, + ALERT_TIME_RANGE, + ALERT_END, +} from '@kbn/rule-data-utils'; +import { AlertRule } from '../types'; +import { expandFlattenedAlert } from './format_alert'; + +export const rule = { + category: 'My test rule', + consumer: 'bar', + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + name: 'rule-name', + parameters: { + bar: true, + }, + producer: 'alerts', + revision: 0, + rule_type_id: 'test.rule-type', + tags: ['rule-', '-tags'], + uuid: '1', +}; + +export const alertRule: AlertRule = { + [ALERT_RULE_CATEGORY]: rule.category, + [ALERT_RULE_CONSUMER]: rule.consumer, + [ALERT_RULE_EXECUTION_UUID]: rule.execution.uuid, + [ALERT_RULE_NAME]: rule.name, + [ALERT_RULE_PARAMETERS]: rule.parameters, + [ALERT_RULE_PRODUCER]: rule.producer, + [ALERT_RULE_REVISION]: rule.revision, + [ALERT_RULE_TYPE_ID]: rule.rule_type_id, + [ALERT_RULE_TAGS]: rule.tags, + [ALERT_RULE_UUID]: rule.uuid, + [SPACE_IDS]: ['default'], +}; + +export const existingFlattenedNewAlert = { + ...alertRule, + [TIMESTAMP]: '2023-03-28T12:27:28.159Z', + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'error', + [ALERT_DURATION]: '0', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true], + [ALERT_INSTANCE_ID]: 'alert-A', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_STATUS]: 'active', + [ALERT_START]: '2023-03-28T12:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' }, + [ALERT_UUID]: 'abcdefg', + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.8.1', + [TAGS]: ['rule-', '-tags'], +}; + +export const existingFlattenedActiveAlert = { + ...existingFlattenedNewAlert, + [TIMESTAMP]: '2023-03-28T12:28:28.159Z', + [EVENT_ACTION]: 'active', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_DURATION]: '3600', + [ALERT_FLAPPING_HISTORY]: [true, false], + [ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-x'], +}; + +export const existingFlattenedRecoveredAlert = { + ...existingFlattenedActiveAlert, + [TIMESTAMP]: '2023-03-28T12:29:28.159Z', + [EVENT_ACTION]: 'close', + [ALERT_ACTION_GROUP]: 'recovered', + [ALERT_DURATION]: '36000000', + [ALERT_END]: '2023-03-30T12:27:28.159Z', + [ALERT_TIME_RANGE]: { gte: '2023-03-27T12:27:28.159Z', lte: '2023-03-30T12:27:28.159Z' }, + [ALERT_FLAPPING_HISTORY]: [true, false, false, true], + [ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-x'], + [ALERT_STATUS]: 'recovered', +}; + +export const existingExpandedNewAlert = expandFlattenedAlert(existingFlattenedNewAlert); +export const existingExpandedActiveAlert = expandFlattenedAlert(existingFlattenedActiveAlert); +export const existingExpandedRecoveredAlert = expandFlattenedAlert(existingFlattenedRecoveredAlert); diff --git a/x-pack/plugins/alerting/server/alerts_client/types.ts b/x-pack/plugins/alerting/server/alerts_client/types.ts index 94adde2892623..0c48138615e43 100644 --- a/x-pack/plugins/alerting/server/alerts_client/types.ts +++ b/x-pack/plugins/alerting/server/alerts_client/types.ts @@ -8,6 +8,19 @@ import type { Alert } from '@kbn/alerts-as-data-utils'; import { DeepPartial } from '@kbn/utility-types'; import { SearchResponseBody } from '@elastic/elasticsearch/lib/api/types'; +import { + ALERT_RULE_CATEGORY, + ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + ALERT_RULE_PRODUCER, + ALERT_RULE_REVISION, + ALERT_RULE_TAGS, + ALERT_RULE_TYPE_ID, + ALERT_RULE_UUID, + SPACE_IDS, +} from '@kbn/rule-data-utils'; import { Alert as LegacyAlert } from '../alert/alert'; import { AlertInstanceContext, @@ -35,12 +48,17 @@ export interface AlertRuleData { } export interface AlertRule { - kibana?: { - alert: { - rule: Alert['kibana']['alert']['rule']; - }; - space_ids: Alert['kibana']['space_ids']; - }; + [ALERT_RULE_CATEGORY]: string; + [ALERT_RULE_CONSUMER]: string; + [ALERT_RULE_EXECUTION_UUID]: string; + [ALERT_RULE_NAME]: string; + [ALERT_RULE_PARAMETERS]: unknown; + [ALERT_RULE_PRODUCER]: string; + [ALERT_RULE_REVISION]: number; + [ALERT_RULE_TYPE_ID]: string; + [ALERT_RULE_TAGS]: string[]; + [ALERT_RULE_UUID]: string; + [SPACE_IDS]: string[]; } export interface IAlertsClient< diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts index 24a47357aa0be..b8c587f18e22e 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts @@ -68,6 +68,35 @@ import { AlertsService } from '../alerts_service'; import { ReplaySubject } from 'rxjs'; import { IAlertsClient } from '../alerts_client/types'; import { getDataStreamAdapter } from '../alerts_service/lib/data_stream_adapter'; +import { + TIMESTAMP, + EVENT_ACTION, + EVENT_KIND, + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_RULE_CATEGORY, + ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + ALERT_RULE_PRODUCER, + ALERT_RULE_REVISION, + ALERT_RULE_TYPE_ID, + ALERT_RULE_TAGS, + ALERT_RULE_UUID, + ALERT_START, + ALERT_STATUS, + ALERT_TIME_RANGE, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + SPACE_IDS, + TAGS, + VERSION, +} from '@kbn/rule-data-utils'; jest.mock('uuid', () => ({ v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', @@ -494,53 +523,35 @@ describe('Task Runner', () => { }, // new alert doc { - '@timestamp': DATE_1970, - event: { - action: 'open', - kind: 'signal', - }, - kibana: { - alert: { - action_group: 'default', - duration: { - us: '0', - }, - flapping: false, - flapping_history: [true], - instance: { - id: '1', - }, - maintenance_window_ids: [], - rule: { - category: 'My test rule', - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - name: 'rule-name', - parameters: { - bar: true, - }, - producer: 'alerts', - revision: 0, - rule_type_id: 'test', - tags: ['rule-', '-tags'], - uuid: '1', - }, - start: DATE_1970, - status: 'active', - time_range: { - gte: DATE_1970, - }, - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - workflow_status: 'open', - }, - space_ids: ['default'], - version: '8.8.0', - }, + [TIMESTAMP]: DATE_1970, numericField: 27, textField: 'foo', - tags: ['rule-', '-tags'], + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [ALERT_ACTION_GROUP]: 'default', + [ALERT_DURATION]: '0', + [ALERT_FLAPPING]: false, + [ALERT_FLAPPING_HISTORY]: [true], + [ALERT_INSTANCE_ID]: '1', + [ALERT_MAINTENANCE_WINDOW_IDS]: [], + [ALERT_RULE_CATEGORY]: 'My test rule', + [ALERT_RULE_CONSUMER]: 'bar', + [ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_RULE_NAME]: 'rule-name', + [ALERT_RULE_PARAMETERS]: { bar: true }, + [ALERT_RULE_PRODUCER]: 'alerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: 'test', + [ALERT_RULE_TAGS]: ['rule-', '-tags'], + [ALERT_RULE_UUID]: '1', + [ALERT_START]: DATE_1970, + [ALERT_STATUS]: 'active', + [ALERT_TIME_RANGE]: { gte: DATE_1970 }, + [ALERT_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['default'], + [VERSION]: '8.8.0', + [TAGS]: ['rule-', '-tags'], }, ], }); diff --git a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts index d3730d22e8e51..8e0abc16821fe 100644 --- a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts +++ b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts @@ -19,7 +19,6 @@ import { IRuleTypeAlerts, RuleExecutorOptions } from '@kbn/alerting-plugin/serve import { ALERT_NAMESPACE, ALERT_REASON, ALERT_URL } from '@kbn/rule-data-utils'; import { MlAnomalyDetectionAlert } from '@kbn/alerts-as-data-utils'; import { ES_FIELD_TYPES } from '@kbn/field-types'; -import { expandFlattenedAlert } from '@kbn/alerting-plugin/server/alerts_client/lib'; import { ML_ALERT_TYPES } from '../../../common/constants/alerts'; import { PLUGIN_ID } from '../../../common/constants/app'; import { MINIMUM_FULL_LICENSE } from '../../../common/license'; @@ -266,7 +265,7 @@ export function registerAnomalyDetectionAlertType({ id: name, actionGroup: ANOMALY_SCORE_MATCH_GROUP_ID, context, - payload: expandFlattenedAlert({ + payload: { [ALERT_URL]: payload[ALERT_URL], [ALERT_REASON]: payload[ALERT_REASON], [ALERT_ANOMALY_DETECTION_JOB_ID]: payload.job_id, @@ -275,7 +274,7 @@ export function registerAnomalyDetectionAlertType({ [ALERT_ANOMALY_TIMESTAMP]: payload.anomaly_timestamp, [ALERT_TOP_RECORDS]: payload.top_records, [ALERT_TOP_INFLUENCERS]: payload.top_influencers, - }), + }, }); } @@ -286,11 +285,11 @@ export function registerAnomalyDetectionAlertType({ alertsClient.setAlertData({ id: alertId, context, - payload: expandFlattenedAlert({ + payload: { [ALERT_URL]: payload[ALERT_URL], [ALERT_REASON]: payload[ALERT_REASON], [ALERT_ANOMALY_DETECTION_JOB_ID]: payload.job_id, - }), + }, }); } } diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.test.ts index 43f84acf65e78..63f5024067cea 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.test.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.test.ts @@ -303,22 +303,18 @@ describe('es_query executor', () => { latestTimestamp: undefined, }, payload: { - kibana: { - alert: { - url: 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', - reason: `rule 'test-rule-name' is active: + 'kibana.alert.evaluation.conditions': + 'Number of matching documents is greater than or equal to 200', + 'kibana.alert.evaluation.value': '491', + 'kibana.alert.reason': `rule 'test-rule-name' is active: - Value: 491 - Conditions Met: Number of matching documents is greater than or equal to 200 over 5m - Timestamp: ${new Date(mockNow).toISOString()} - Link: https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id`, - title: "rule 'test-rule-name' matched query", - evaluation: { - conditions: 'Number of matching documents is greater than or equal to 200', - value: 491, - }, - }, - }, + 'kibana.alert.title': "rule 'test-rule-name' matched query", + 'kibana.alert.url': + 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', }, }); expect(mockSetLimitReached).toHaveBeenCalledTimes(1); @@ -389,23 +385,18 @@ describe('es_query executor', () => { latestTimestamp: undefined, }, payload: { - kibana: { - alert: { - url: 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', - reason: `rule 'test-rule-name' is active: + 'kibana.alert.evaluation.conditions': + 'Number of matching documents for group "host-1" is greater than or equal to 200', + 'kibana.alert.evaluation.value': '291', + 'kibana.alert.reason': `rule 'test-rule-name' is active: - Value: 291 - Conditions Met: Number of matching documents for group "host-1" is greater than or equal to 200 over 5m - Timestamp: ${new Date(mockNow).toISOString()} - Link: https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id`, - title: "rule 'test-rule-name' matched query for group host-1", - evaluation: { - conditions: - 'Number of matching documents for group "host-1" is greater than or equal to 200', - value: 291, - }, - }, - }, + 'kibana.alert.title': "rule 'test-rule-name' matched query for group host-1", + 'kibana.alert.url': + 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', }, }); expect(mockReport).toHaveBeenNthCalledWith(2, { @@ -432,23 +423,18 @@ describe('es_query executor', () => { latestTimestamp: undefined, }, payload: { - kibana: { - alert: { - url: 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', - reason: `rule 'test-rule-name' is active: + 'kibana.alert.evaluation.conditions': + 'Number of matching documents for group "host-2" is greater than or equal to 200', + 'kibana.alert.evaluation.value': '477', + 'kibana.alert.reason': `rule 'test-rule-name' is active: - Value: 477 - Conditions Met: Number of matching documents for group "host-2" is greater than or equal to 200 over 5m - Timestamp: ${new Date(mockNow).toISOString()} - Link: https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id`, - title: "rule 'test-rule-name' matched query for group host-2", - evaluation: { - conditions: - 'Number of matching documents for group "host-2" is greater than or equal to 200', - value: 477, - }, - }, - }, + 'kibana.alert.title': "rule 'test-rule-name' matched query for group host-2", + 'kibana.alert.url': + 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', }, }); expect(mockReport).toHaveBeenNthCalledWith(3, { @@ -475,23 +461,18 @@ describe('es_query executor', () => { latestTimestamp: undefined, }, payload: { - kibana: { - alert: { - url: 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', - reason: `rule 'test-rule-name' is active: + 'kibana.alert.evaluation.conditions': + 'Number of matching documents for group "host-3" is greater than or equal to 200', + 'kibana.alert.evaluation.value': '999', + 'kibana.alert.reason': `rule 'test-rule-name' is active: - Value: 999 -- Conditions Met: Number of matching documents for group \"host-3\" is greater than or equal to 200 over 5m +- Conditions Met: Number of matching documents for group "host-3" is greater than or equal to 200 over 5m - Timestamp: ${new Date(mockNow).toISOString()} - Link: https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id`, - title: "rule 'test-rule-name' matched query for group host-3", - evaluation: { - conditions: - 'Number of matching documents for group "host-3" is greater than or equal to 200', - value: 999, - }, - }, - }, + 'kibana.alert.title': "rule 'test-rule-name' matched query for group host-3", + 'kibana.alert.url': + 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', }, }); expect(mockSetLimitReached).toHaveBeenCalledTimes(1); @@ -543,22 +524,17 @@ describe('es_query executor', () => { }, id: 'query matched', payload: { - kibana: { - alert: { - evaluation: { - conditions: 'Query matched documents', - value: 198, - }, - reason: `rule 'test-rule-name' is active: + 'kibana.alert.evaluation.conditions': 'Query matched documents', + 'kibana.alert.evaluation.value': '198', + 'kibana.alert.reason': `rule 'test-rule-name' is active: - Value: 198 - Conditions Met: Query matched documents over 5m - Timestamp: ${new Date(mockNow).toISOString()} - Link: https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id`, - title: "rule 'test-rule-name' matched query", - url: 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', - }, - }, + 'kibana.alert.title': "rule 'test-rule-name' matched query", + 'kibana.alert.url': + 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', }, state: { dateEnd: new Date(mockNow).toISOString(), @@ -664,22 +640,18 @@ describe('es_query executor', () => { value: 0, }, payload: { - kibana: { - alert: { - evaluation: { - conditions: 'Number of matching documents is NOT greater than or equal to 500', - value: 0, - }, - reason: `rule 'test-rule-name' is recovered: + 'kibana.alert.evaluation.conditions': + 'Number of matching documents is NOT greater than or equal to 500', + 'kibana.alert.evaluation.value': '0', + 'kibana.alert.reason': `rule 'test-rule-name' is recovered: - Value: 0 - Conditions Met: Number of matching documents is NOT greater than or equal to 500 over 5m - Timestamp: ${new Date(mockNow).toISOString()} - Link: https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id`, - title: "rule 'test-rule-name' recovered", - url: 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', - }, - }, + 'kibana.alert.title': "rule 'test-rule-name' recovered", + 'kibana.alert.url': + 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', }, }); expect(mockSetLimitReached).toHaveBeenCalledTimes(1); @@ -737,23 +709,18 @@ describe('es_query executor', () => { value: 0, }, payload: { - kibana: { - alert: { - evaluation: { - conditions: - 'Number of matching documents for group "host-1" is NOT greater than or equal to 200', - value: 0, - }, - reason: `rule 'test-rule-name' is recovered: + 'kibana.alert.evaluation.conditions': + 'Number of matching documents for group "host-1" is NOT greater than or equal to 200', + 'kibana.alert.evaluation.value': '0', + 'kibana.alert.reason': `rule 'test-rule-name' is recovered: - Value: 0 - Conditions Met: Number of matching documents for group \"host-1\" is NOT greater than or equal to 200 over 5m - Timestamp: ${new Date(mockNow).toISOString()} - Link: https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id`, - title: "rule 'test-rule-name' recovered", - url: 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', - }, - }, + 'kibana.alert.title': "rule 'test-rule-name' recovered", + 'kibana.alert.url': + 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', }, }); expect(mockSetAlertData).toHaveBeenNthCalledWith(2, { @@ -773,23 +740,18 @@ describe('es_query executor', () => { value: 0, }, payload: { - kibana: { - alert: { - evaluation: { - conditions: - 'Number of matching documents for group "host-2" is NOT greater than or equal to 200', - value: 0, - }, - reason: `rule 'test-rule-name' is recovered: + 'kibana.alert.evaluation.conditions': + 'Number of matching documents for group "host-2" is NOT greater than or equal to 200', + 'kibana.alert.evaluation.value': '0', + 'kibana.alert.reason': `rule 'test-rule-name' is recovered: - Value: 0 - Conditions Met: Number of matching documents for group \"host-2\" is NOT greater than or equal to 200 over 5m - Timestamp: ${new Date(mockNow).toISOString()} - Link: https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id`, - title: "rule 'test-rule-name' recovered", - url: 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', - }, - }, + 'kibana.alert.title': "rule 'test-rule-name' recovered", + 'kibana.alert.url': + 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', }, }); expect(mockSetLimitReached).toHaveBeenCalledTimes(1); @@ -842,22 +804,17 @@ describe('es_query executor', () => { value: 0, }, payload: { - kibana: { - alert: { - evaluation: { - conditions: 'Query did NOT match documents', - value: 0, - }, - reason: `rule 'test-rule-name' is recovered: + 'kibana.alert.evaluation.conditions': 'Query did NOT match documents', + 'kibana.alert.evaluation.value': '0', + 'kibana.alert.reason': `rule 'test-rule-name' is recovered: - Value: 0 - Conditions Met: Query did NOT match documents over 5m - Timestamp: ${new Date(mockNow).toISOString()} - Link: https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id`, - title: "rule 'test-rule-name' recovered", - url: 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', - }, - }, + 'kibana.alert.title': "rule 'test-rule-name' recovered", + 'kibana.alert.url': + 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id', }, }); expect(mockSetLimitReached).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.ts index c048bfd36cefb..e6366caf53130 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.ts @@ -11,7 +11,6 @@ import { parseDuration } from '@kbn/alerting-plugin/server'; import { isGroupAggregation, UngroupedGroupId } from '@kbn/triggers-actions-ui-plugin/common'; import { ALERT_EVALUATION_VALUE, ALERT_REASON, ALERT_URL } from '@kbn/rule-data-utils'; -import { expandFlattenedAlert } from '@kbn/alerting-plugin/server/alerts_client/lib'; import { ComparatorFns } from '../../../common'; import { addMessages, @@ -148,13 +147,13 @@ export async function executor(core: CoreSetup, options: ExecutorOptions { actionGroup: 'query matched', id: 'query matched', payload: expect.objectContaining({ - kibana: { - alert: { - url: expect.any(String), - reason: expect.any(String), - title: "rule 'rule-name' matched query", - evaluation: { - conditions: 'Number of matching documents is greater than or equal to 3', - value: 3, - }, - }, - }, + 'kibana.alert.evaluation.conditions': + 'Number of matching documents is greater than or equal to 3', + 'kibana.alert.evaluation.value': '3', + 'kibana.alert.reason': expect.any(String), + 'kibana.alert.title': "rule 'rule-name' matched query", + 'kibana.alert.url': expect.any(String), }), }) ); @@ -836,17 +831,11 @@ describe('ruleType', () => { actionGroup: 'query matched', id: 'query matched', payload: expect.objectContaining({ - kibana: { - alert: { - url: expect.any(String), - reason: expect.any(String), - title: "rule 'rule-name' matched query", - evaluation: { - conditions: 'Query matched documents', - value: 3, - }, - }, - }, + 'kibana.alert.evaluation.conditions': 'Query matched documents', + 'kibana.alert.evaluation.value': '3', + 'kibana.alert.reason': expect.any(String), + 'kibana.alert.title': "rule 'rule-name' matched query", + 'kibana.alert.url': expect.any(String), }), }) ); diff --git a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts index 1e0aab0bb7930..0d0c7a9f49b33 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts @@ -242,17 +242,14 @@ describe('ruleType', () => { }, id: 'all documents', payload: { - kibana: { - alert: { - evaluation: { conditions: 'foo is less than 1', value: 0 }, - reason: `alert '${ruleName}' is active for group 'all documents': + 'kibana.alert.evaluation.conditions': 'foo is less than 1', + 'kibana.alert.evaluation.value': '0', + 'kibana.alert.reason': `alert '${ruleName}' is active for group 'all documents': - Value: 0 - Conditions Met: foo is less than 1 over 5m - Timestamp: 1970-01-01T00:00:00.000Z`, - title: `alert ${ruleName} group all documents met threshold`, - }, - }, + 'kibana.alert.title': `alert ${ruleName} group all documents met threshold`, }, state: {}, }); diff --git a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.ts b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.ts index 336346d8ed6f7..e4411ad50638a 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.ts @@ -18,7 +18,6 @@ import { ALERT_REASON, STACK_ALERTS_FEATURE_ID, } from '@kbn/rule-data-utils'; -import { expandFlattenedAlert } from '@kbn/alerting-plugin/server/alerts_client/lib'; import { ALERT_EVALUATION_CONDITIONS, ALERT_TITLE, STACK_ALERTS_AAD_CONFIG } from '..'; import { ComparatorFns, getComparatorScript, getHumanReadableComparator } from '../../../common'; import { ActionContext, BaseActionContext, addMessages } from './action_context'; @@ -322,12 +321,12 @@ export function getRuleType( actionGroup: ActionGroupId, state: {}, context: actionContext, - payload: expandFlattenedAlert({ + payload: { [ALERT_REASON]: actionContext.message, [ALERT_TITLE]: actionContext.title, [ALERT_EVALUATION_CONDITIONS]: actionContext.conditions, - [ALERT_EVALUATION_VALUE]: actionContext.value, - }), + [ALERT_EVALUATION_VALUE]: `${actionContext.value}`, + }, }); logger.debug(`scheduled actionGroup: ${JSON.stringify(actionContext)}`); } @@ -350,12 +349,12 @@ export function getRuleType( alertsClient?.setAlertData({ id: alertId, context: recoveryContext, - payload: expandFlattenedAlert({ + payload: { [ALERT_REASON]: recoveryContext.message, [ALERT_TITLE]: recoveryContext.title, [ALERT_EVALUATION_CONDITIONS]: recoveryContext.conditions, - [ALERT_EVALUATION_VALUE]: recoveryContext.value, - }), + [ALERT_EVALUATION_VALUE]: `${recoveryContext.value}`, + }, }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/ml_rule_types/anomaly_detection/alert.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/ml_rule_types/anomaly_detection/alert.ts index 941f663bb6284..e36d22a6c85af 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/ml_rule_types/anomaly_detection/alert.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/ml_rule_types/anomaly_detection/alert.ts @@ -152,9 +152,8 @@ export default function alertTests({ getService }: FtrProviderContext) { const aadDocs = await waitForAAD(1); for (const doc of aadDocs) { - const { job_id: jobId, url } = doc._source.kibana.alert; - expect(jobId).to.be(AD_JOB_ID); - expect(url).to.contain( + expect(doc._source['kibana.alert.job_id']).to.be(AD_JOB_ID); + expect(doc._source['kibana.alert.url']).to.contain( '/s/space1/app/ml/explorer/?_g=(ml%3A(jobIds%3A!(rt-anomaly-mean-value))' ); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/esql_only.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/esql_only.ts index eee79e38a2dac..fc548df7a3426 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/esql_only.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/esql_only.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { ALERT_REASON, ALERT_URL } from '@kbn/rule-data-utils'; import { Spaces } from '../../../../../scenarios'; import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; import { getUrlPrefix, ObjectRemover } from '../../../../../../common/lib'; @@ -97,12 +98,13 @@ export default function ruleTests({ getService }: FtrProviderContext) { const aadDocs = await getAllAADDocs(1); - const alertDoc = aadDocs.body.hits.hits[0]._source.kibana.alert; - expect(alertDoc.reason).to.match(messagePattern); - expect(alertDoc.title).to.be("rule 'always fire' matched query"); - expect(alertDoc.evaluation.conditions).to.be('Query matched documents'); - expect(alertDoc.evaluation.value).greaterThan(0); - expect(alertDoc.url).to.contain('/s/space1/app/'); + const alertDoc = aadDocs.body.hits.hits[0]._source; + expect(alertDoc[ALERT_REASON]).to.match(messagePattern); + expect(alertDoc['kibana.alert.title']).to.be("rule 'always fire' matched query"); + expect(alertDoc['kibana.alert.evaluation.conditions']).to.be('Query matched documents'); + const value = parseInt(alertDoc['kibana.alert.evaluation.value'], 10); + expect(value).greaterThan(0); + expect(alertDoc[ALERT_URL]).to.contain('/s/space1/app/'); }); it('runs correctly: use epoch millis - threshold on hit count < >', async () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/rule.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/rule.ts index 25ca53c52e429..0e0ad5cc9bb68 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/rule.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/rule.ts @@ -9,6 +9,7 @@ import expect from '@kbn/expect'; import { ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers'; +import { ALERT_REASON, ALERT_URL } from '@kbn/rule-data-utils'; import { Spaces } from '../../../../../scenarios'; import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; import { getUrlPrefix, ObjectRemover } from '../../../../../../common/lib'; @@ -162,14 +163,15 @@ export default function ruleTests({ getService }: FtrProviderContext) { const aadDocs = await getAllAADDocs(1); - const alertDoc = aadDocs.body.hits.hits[0]._source.kibana.alert; - expect(alertDoc.reason).to.match(messagePattern); - expect(alertDoc.title).to.be("rule 'always fire' matched query"); - expect(alertDoc.evaluation.conditions).to.be( + const alertDoc = aadDocs.body.hits.hits[0]._source; + expect(alertDoc[ALERT_REASON]).to.match(messagePattern); + expect(alertDoc['kibana.alert.title']).to.be("rule 'always fire' matched query"); + expect(alertDoc['kibana.alert.evaluation.conditions']).to.be( 'Number of matching documents is greater than -1' ); - expect(alertDoc.evaluation.value).greaterThan(0); - expect(alertDoc.url).to.contain('/s/space1/app/'); + const value = parseInt(alertDoc['kibana.alert.evaluation.value'], 10); + expect(value).greaterThan(0); + expect(alertDoc[ALERT_URL]).to.contain('/s/space1/app/'); }) ); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/index_threshold/alert.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/index_threshold/alert.ts index 3de5e761b2622..f7e2dd9ec983c 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/index_threshold/alert.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/index_threshold/alert.ts @@ -10,6 +10,7 @@ import expect from '@kbn/expect'; import { ESTestIndexTool, ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers'; import { STACK_AAD_INDEX_NAME } from '@kbn/stack-alerts-plugin/server/rule_types'; +import { ALERT_REASON } from '@kbn/rule-data-utils'; import { Spaces } from '../../../../../scenarios'; import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; import { getUrlPrefix, ObjectRemover, getEventLog } from '../../../../../../common/lib'; @@ -111,12 +112,17 @@ export default function ruleTests({ getService }: FtrProviderContext) { const aadDocs = await esTestIndexToolAAD.getAll(1); + const alertDoc = aadDocs.body.hits.hits[0]._source; // @ts-ignore - const alertDoc = aadDocs.body.hits.hits[0]._source.kibana.alert; - expect(alertDoc.reason).to.match(messagePattern); - expect(alertDoc.title).to.be('alert always fire group all documents met threshold'); - expect(alertDoc.evaluation.conditions).to.be('count is greater than -1'); - expect(alertDoc.evaluation.value).greaterThan(0); + expect(alertDoc[ALERT_REASON]).to.match(messagePattern); + // @ts-ignore + expect(alertDoc['kibana.alert.title']).to.be( + 'alert always fire group all documents met threshold' + ); + // @ts-ignore + expect(alertDoc['kibana.alert.evaluation.conditions']).to.be('count is greater than -1'); + // @ts-ignore + expect(alertDoc['kibana.alert.evaluation.value']).greaterThan(0); }); it('runs correctly: count grouped <= =>', async () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data.ts index 13f3c5d445916..83dfff9c08d08 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data.ts @@ -10,6 +10,31 @@ import { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IValidatedEvent } from '@kbn/event-log-plugin/server'; import type { Alert } from '@kbn/alerts-as-data-utils'; import { omit } from 'lodash'; +import { + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_END, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_RULE_CATEGORY, + ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + ALERT_RULE_PRODUCER, + ALERT_RULE_TAGS, + ALERT_RULE_TYPE_ID, + ALERT_RULE_UUID, + ALERT_START, + ALERT_STATUS, + ALERT_TIME_RANGE, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + EVENT_ACTION, + EVENT_KIND, + SPACE_IDS, +} from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { Spaces } from '../../../../scenarios'; import { @@ -97,57 +122,57 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F const source: PatternFiringAlert = alertDocsRun1[i]._source!; // Each doc should have active status and default action group id - expect(source.kibana.alert.action_group).to.equal('default'); + expect(source[ALERT_ACTION_GROUP]).to.equal('default'); // patternIndex should be 0 for the first run expect(source.patternIndex).to.equal(0); // alert UUID should equal doc id - expect(source.kibana.alert.uuid).to.equal(alertDocsRun1[i]._id); + expect(source[ALERT_UUID]).to.equal(alertDocsRun1[i]._id); // duration should be '0' since this is a new alert - expect(source.kibana.alert.duration?.us).to.equal('0'); + expect(source[ALERT_DURATION]).to.equal('0'); // start should be defined - expect(source.kibana.alert.start).to.match(timestampPattern); + expect(source[ALERT_START]).to.match(timestampPattern); // time_range.gte should be same as start - expect(source.kibana.alert.time_range?.gte).to.equal(source.kibana.alert.start); + expect(source[ALERT_TIME_RANGE]?.gte).to.equal(source[ALERT_START]); // timestamp should be defined expect(source['@timestamp']).to.match(timestampPattern); // status should be active - expect(source.kibana.alert.status).to.equal('active'); + expect(source[ALERT_STATUS]).to.equal('active'); // flapping information for new alert - expect(source.kibana.alert.flapping).to.equal(false); - expect(source.kibana.alert.flapping_history).to.eql([true]); + expect(source[ALERT_FLAPPING]).to.equal(false); + expect(source[ALERT_FLAPPING_HISTORY]).to.eql([true]); // workflow status should be 'open' - expect(source.kibana.alert.workflow_status).to.equal('open'); + expect(source[ALERT_WORKFLOW_STATUS]).to.equal('open'); // event.action should be 'open' - expect(source.event?.action).to.equal('open'); + expect(source[EVENT_ACTION]).to.equal('open'); // event.kind should be 'signal' - expect(source.event?.kind).to.equal('signal'); + expect(source[EVENT_KIND]).to.equal('signal'); // tags should equal rule tags because rule type doesn't set any tags expect(source.tags).to.eql(['foo']); } let alertDoc: SearchHit | undefined = alertDocsRun1.find( - (doc) => doc._source!.kibana.alert.instance.id === 'alertA' + (doc) => doc._source![ALERT_INSTANCE_ID] === 'alertA' ); const alertADocRun1 = alertDoc!._source!; expect(alertADocRun1.instancePattern).to.eql(pattern.alertA); - alertDoc = alertDocsRun1.find((doc) => doc._source!.kibana.alert.instance.id === 'alertB'); + alertDoc = alertDocsRun1.find((doc) => doc._source![ALERT_INSTANCE_ID] === 'alertB'); const alertBDocRun1 = alertDoc!._source!; expect(alertBDocRun1.instancePattern).to.eql(pattern.alertB); - alertDoc = alertDocsRun1.find((doc) => doc._source!.kibana.alert.instance.id === 'alertC'); + alertDoc = alertDocsRun1.find((doc) => doc._source![ALERT_INSTANCE_ID] === 'alertC'); const alertCDocRun1 = alertDoc!._source!; expect(alertCDocRun1.instancePattern).to.eql(pattern.alertC); @@ -182,130 +207,118 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F const source: PatternFiringAlert = alertDocsRun2[i]._source!; // alert UUID should equal doc id - expect(source.kibana.alert.uuid).to.equal(alertDocsRun2[i]._id); + expect(source[ALERT_UUID]).to.equal(alertDocsRun2[i]._id); // duration should be greater than 0 since these are not new alerts - const durationAsNumber = Number(source.kibana.alert.duration?.us); + const durationAsNumber = Number(source[ALERT_DURATION]); expect(durationAsNumber).to.be.greaterThan(0); } // alertA, run2 // status is still active; duration is updated; no end time - alertDoc = alertDocsRun2.find((doc) => doc._source!.kibana.alert.instance.id === 'alertA'); + alertDoc = alertDocsRun2.find((doc) => doc._source![ALERT_INSTANCE_ID] === 'alertA'); const alertADocRun2 = alertDoc!._source!; expect(alertADocRun2.instancePattern).to.eql(pattern.alertA); // uuid is the same - expect(alertADocRun2.kibana.alert.uuid).to.equal(alertADocRun1.kibana.alert.uuid); + expect(alertADocRun2[ALERT_UUID]).to.equal(alertADocRun1[ALERT_UUID]); // patternIndex should be 1 for the second run expect(alertADocRun2.patternIndex).to.equal(1); - expect(alertADocRun2.kibana.alert.action_group).to.equal('default'); + expect(alertADocRun2[ALERT_ACTION_GROUP]).to.equal('default'); // start time should be defined and the same as prior run - expect(alertADocRun2.kibana.alert.start).to.match(timestampPattern); - expect(alertADocRun2.kibana.alert.start).to.equal(alertADocRun1.kibana.alert.start); + expect(alertADocRun2[ALERT_START]).to.match(timestampPattern); + expect(alertADocRun2[ALERT_START]).to.equal(alertADocRun1[ALERT_START]); // timestamp should be defined and not the same as prior run expect(alertADocRun2['@timestamp']).to.match(timestampPattern); expect(alertADocRun2['@timestamp']).not.to.equal(alertADocRun1['@timestamp']); // status should still be active - expect(alertADocRun2.kibana.alert.status).to.equal('active'); + expect(alertADocRun2[ALERT_STATUS]).to.equal('active'); // flapping false, flapping history updated with additional entry - expect(alertADocRun2.kibana.alert.flapping).to.equal(false); - expect(alertADocRun2.kibana.alert.flapping_history).to.eql([ - ...alertADocRun1.kibana.alert.flapping_history!, + expect(alertADocRun2[ALERT_FLAPPING]).to.equal(false); + expect(alertADocRun2[ALERT_FLAPPING_HISTORY]).to.eql([ + ...alertADocRun1[ALERT_FLAPPING_HISTORY]!, false, ]); // event.action set to active - expect(alertADocRun2.event?.action).to.eql('active'); + expect(alertADocRun2[EVENT_ACTION]).to.eql('active'); expect(alertADocRun2.tags).to.eql(['foo']); // these values should be the same as previous run - expect(alertADocRun2.event?.kind).to.eql(alertADocRun1.event?.kind); - expect(alertADocRun2.kibana.alert.workflow_status).to.eql( - alertADocRun1.kibana.alert.workflow_status - ); - expect(alertADocRun2.kibana.alert.time_range?.gte).to.equal( - alertADocRun1.kibana.alert.time_range?.gte - ); + expect(alertADocRun2[EVENT_KIND]).to.eql(alertADocRun1[EVENT_KIND]); + expect(alertADocRun2[ALERT_WORKFLOW_STATUS]).to.eql(alertADocRun1[ALERT_WORKFLOW_STATUS]); + expect(alertADocRun2[ALERT_TIME_RANGE]?.gte).to.equal(alertADocRun1[ALERT_TIME_RANGE]?.gte); // alertB, run 2 // status is updated to recovered, duration is updated, end time is set - alertDoc = alertDocsRun2.find((doc) => doc._source!.kibana.alert.instance.id === 'alertB'); + alertDoc = alertDocsRun2.find((doc) => doc._source![ALERT_INSTANCE_ID] === 'alertB'); const alertBDocRun2 = alertDoc!._source!; // action group should be set to recovered - expect(alertBDocRun2.kibana.alert.action_group).to.be('recovered'); + expect(alertBDocRun2[ALERT_ACTION_GROUP]).to.be('recovered'); // rule type AAD payload should be set to recovery values expect(alertBDocRun2.instancePattern).to.eql([]); expect(alertBDocRun2.patternIndex).to.eql(-1); // uuid is the same - expect(alertBDocRun2.kibana.alert.uuid).to.equal(alertBDocRun1.kibana.alert.uuid); + expect(alertBDocRun2[ALERT_UUID]).to.equal(alertBDocRun1[ALERT_UUID]); // start time should be defined and the same as before - expect(alertBDocRun2.kibana.alert.start).to.match(timestampPattern); - expect(alertBDocRun2.kibana.alert.start).to.equal(alertBDocRun1.kibana.alert.start); + expect(alertBDocRun2[ALERT_START]).to.match(timestampPattern); + expect(alertBDocRun2[ALERT_START]).to.equal(alertBDocRun1[ALERT_START]); // timestamp should be defined and not the same as prior run expect(alertBDocRun2['@timestamp']).to.match(timestampPattern); expect(alertBDocRun2['@timestamp']).not.to.equal(alertBDocRun1['@timestamp']); // end time should be defined - expect(alertBDocRun2.kibana.alert.end).to.match(timestampPattern); + expect(alertBDocRun2[ALERT_END]).to.match(timestampPattern); // status should be set to recovered - expect(alertBDocRun2.kibana.alert.status).to.equal('recovered'); + expect(alertBDocRun2[ALERT_STATUS]).to.equal('recovered'); // flapping false, flapping history updated with additional entry - expect(alertBDocRun2.kibana.alert.flapping).to.equal(false); - expect(alertBDocRun2.kibana.alert.flapping_history).to.eql([ - ...alertBDocRun1.kibana.alert.flapping_history!, + expect(alertBDocRun2[ALERT_FLAPPING]).to.equal(false); + expect(alertBDocRun2[ALERT_FLAPPING_HISTORY]).to.eql([ + ...alertBDocRun1[ALERT_FLAPPING_HISTORY]!, true, ]); // event.action set to close - expect(alertBDocRun2.event?.action).to.eql('close'); + expect(alertBDocRun2[EVENT_ACTION]).to.eql('close'); expect(alertBDocRun2.tags).to.eql(['foo']); // these values should be the same as previous run - expect(alertBDocRun2.event?.kind).to.eql(alertBDocRun1.event?.kind); - expect(alertBDocRun2.kibana.alert.workflow_status).to.eql( - alertBDocRun1.kibana.alert.workflow_status - ); - expect(alertBDocRun2.kibana.alert.time_range?.gte).to.equal( - alertBDocRun1.kibana.alert.time_range?.gte - ); + expect(alertBDocRun2[EVENT_KIND]).to.eql(alertBDocRun1[EVENT_KIND]); + expect(alertBDocRun2[ALERT_WORKFLOW_STATUS]).to.eql(alertBDocRun1[ALERT_WORKFLOW_STATUS]); + expect(alertBDocRun2[ALERT_TIME_RANGE]?.gte).to.equal(alertBDocRun1[ALERT_TIME_RANGE]?.gte); // time_range.lte should be set to end time - expect(alertBDocRun2.kibana.alert.time_range?.lte).to.equal(alertBDocRun2.kibana.alert.end); + expect(alertBDocRun2[ALERT_TIME_RANGE]?.lte).to.equal(alertBDocRun2[ALERT_END]); // alertC, run 2 // status is updated to recovered, duration is updated, end time is set - alertDoc = alertDocsRun2.find((doc) => doc._source!.kibana.alert.instance.id === 'alertC'); + alertDoc = alertDocsRun2.find((doc) => doc._source![ALERT_INSTANCE_ID] === 'alertC'); const alertCDocRun2 = alertDoc!._source!; // action group should be set to recovered - expect(alertCDocRun2.kibana.alert.action_group).to.be('recovered'); + expect(alertCDocRun2[ALERT_ACTION_GROUP]).to.be('recovered'); // rule type AAD payload should be set to recovery values expect(alertCDocRun2.instancePattern).to.eql([]); expect(alertCDocRun2.patternIndex).to.eql(-1); // uuid is the same - expect(alertCDocRun2.kibana.alert.uuid).to.equal(alertCDocRun1.kibana.alert.uuid); + expect(alertCDocRun2[ALERT_UUID]).to.equal(alertCDocRun1[ALERT_UUID]); // start time should be defined and the same as before - expect(alertCDocRun2.kibana.alert.start).to.match(timestampPattern); - expect(alertCDocRun2.kibana.alert.start).to.equal(alertCDocRun1.kibana.alert.start); + expect(alertCDocRun2[ALERT_START]).to.match(timestampPattern); + expect(alertCDocRun2[ALERT_START]).to.equal(alertCDocRun1[ALERT_START]); // timestamp should be defined and not the same as prior run expect(alertCDocRun2['@timestamp']).to.match(timestampPattern); expect(alertCDocRun2['@timestamp']).not.to.equal(alertCDocRun1['@timestamp']); // end time should be defined - expect(alertCDocRun2.kibana.alert.end).to.match(timestampPattern); + expect(alertCDocRun2[ALERT_END]).to.match(timestampPattern); // status should be set to recovered - expect(alertCDocRun2.kibana.alert.status).to.equal('recovered'); + expect(alertCDocRun2[ALERT_STATUS]).to.equal('recovered'); // flapping false, flapping history updated with additional entry - expect(alertCDocRun2.kibana.alert.flapping).to.equal(false); - expect(alertCDocRun2.kibana.alert.flapping_history).to.eql([ - ...alertCDocRun1.kibana.alert.flapping_history!, + expect(alertCDocRun2[ALERT_FLAPPING]).to.equal(false); + expect(alertCDocRun2[ALERT_FLAPPING_HISTORY]).to.eql([ + ...alertCDocRun1[ALERT_FLAPPING_HISTORY]!, true, ]); // event.action set to close - expect(alertCDocRun2.event?.action).to.eql('close'); + expect(alertCDocRun2[EVENT_ACTION]).to.eql('close'); expect(alertCDocRun2.tags).to.eql(['foo']); // these values should be the same as previous run - expect(alertCDocRun2.event?.kind).to.eql(alertADocRun1.event?.kind); - expect(alertCDocRun2.kibana.alert.workflow_status).to.eql( - alertCDocRun1.kibana.alert.workflow_status - ); - expect(alertCDocRun2.kibana.alert.time_range?.gte).to.equal( - alertCDocRun1.kibana.alert.time_range?.gte - ); + expect(alertCDocRun2[EVENT_KIND]).to.eql(alertADocRun1[EVENT_KIND]); + expect(alertCDocRun2[ALERT_WORKFLOW_STATUS]).to.eql(alertCDocRun1[ALERT_WORKFLOW_STATUS]); + expect(alertCDocRun2[ALERT_TIME_RANGE]?.gte).to.equal(alertCDocRun1[ALERT_TIME_RANGE]?.gte); // time_range.lte should be set to end time - expect(alertCDocRun2.kibana.alert.time_range?.lte).to.equal(alertCDocRun2.kibana.alert.end); + expect(alertCDocRun2[ALERT_TIME_RANGE]?.lte).to.equal(alertCDocRun2[ALERT_END]); // -------------------------- // RUN 3 - 1 re-active (alertC), 1 still recovered (alertB), 1 ongoing (alertA) @@ -340,97 +353,93 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F // alertA, run3 // status is still active; duration is updated; no end time - alertDoc = alertDocsRun3.find((doc) => doc._source!.kibana.alert.instance.id === 'alertA'); + alertDoc = alertDocsRun3.find((doc) => doc._source![ALERT_INSTANCE_ID] === 'alertA'); const alertADocRun3 = alertDoc!._source!; expect(alertADocRun3.instancePattern).to.eql(pattern.alertA); // uuid is the same as previous runs - expect(alertADocRun3.kibana.alert.uuid).to.equal(alertADocRun2.kibana.alert.uuid); - expect(alertADocRun3.kibana.alert.uuid).to.equal(alertADocRun1.kibana.alert.uuid); + expect(alertADocRun3[ALERT_UUID]).to.equal(alertADocRun2[ALERT_UUID]); + expect(alertADocRun3[ALERT_UUID]).to.equal(alertADocRun1[ALERT_UUID]); // patternIndex should be 2 for the third run expect(alertADocRun3.patternIndex).to.equal(2); - expect(alertADocRun3.kibana.alert.action_group).to.equal('default'); + expect(alertADocRun3[ALERT_ACTION_GROUP]).to.equal('default'); // start time should be defined and the same as prior runs - expect(alertADocRun3.kibana.alert.start).to.match(timestampPattern); - expect(alertADocRun3.kibana.alert.start).to.equal(alertADocRun2.kibana.alert.start); - expect(alertADocRun3.kibana.alert.start).to.equal(alertADocRun1.kibana.alert.start); + expect(alertADocRun3[ALERT_START]).to.match(timestampPattern); + expect(alertADocRun3[ALERT_START]).to.equal(alertADocRun2[ALERT_START]); + expect(alertADocRun3[ALERT_START]).to.equal(alertADocRun1[ALERT_START]); // timestamp should be defined and not the same as prior run expect(alertADocRun3['@timestamp']).to.match(timestampPattern); expect(alertADocRun3['@timestamp']).not.to.equal(alertADocRun2['@timestamp']); // status should still be active - expect(alertADocRun3.kibana.alert.status).to.equal('active'); + expect(alertADocRun3[ALERT_STATUS]).to.equal('active'); // flapping false, flapping history updated with additional entry - expect(alertADocRun3.kibana.alert.flapping).to.equal(false); - expect(alertADocRun3.kibana.alert.flapping_history).to.eql([ - ...alertADocRun2.kibana.alert.flapping_history!, + expect(alertADocRun3[ALERT_FLAPPING]).to.equal(false); + expect(alertADocRun3[ALERT_FLAPPING_HISTORY]).to.eql([ + ...alertADocRun2[ALERT_FLAPPING_HISTORY]!, false, ]); // event.action should still to active - expect(alertADocRun3.event?.action).to.eql('active'); + expect(alertADocRun3[EVENT_ACTION]).to.eql('active'); expect(alertADocRun3.tags).to.eql(['foo']); // these values should be the same as previous run - expect(alertADocRun3.event?.kind).to.eql(alertADocRun2.event?.kind); - expect(alertADocRun3.kibana.alert.workflow_status).to.eql( - alertADocRun2.kibana.alert.workflow_status - ); - expect(alertADocRun3.kibana.alert.time_range?.gte).to.equal( - alertADocRun2.kibana.alert.time_range?.gte - ); + expect(alertADocRun3[EVENT_KIND]).to.eql(alertADocRun2[EVENT_KIND]); + expect(alertADocRun3[ALERT_WORKFLOW_STATUS]).to.eql(alertADocRun2[ALERT_WORKFLOW_STATUS]); + expect(alertADocRun3[ALERT_TIME_RANGE]?.gte).to.equal(alertADocRun2[ALERT_TIME_RANGE]?.gte); // alertB doc should be unchanged from prior run because it is still recovered // but its flapping history should be updated - alertDoc = alertDocsRun3.find((doc) => doc._source!.kibana.alert.instance.id === 'alertB'); + alertDoc = alertDocsRun3.find((doc) => doc._source![ALERT_INSTANCE_ID] === 'alertB'); const alertBDocRun3 = alertDoc!._source!; expect(omit(alertBDocRun3, fieldsToOmitInComparison)).to.eql( omit(alertBDocRun2, fieldsToOmitInComparison) ); // execution uuid should be current one - expect(alertBDocRun3.kibana.alert.rule.execution?.uuid).to.equal(executionUuid); + expect(alertBDocRun3[ALERT_RULE_EXECUTION_UUID]).to.equal(executionUuid); // flapping history should be history from prior run with additional entry - expect(alertBDocRun3.kibana.alert.flapping_history).to.eql([ - ...alertBDocRun2.kibana.alert.flapping_history!, + expect(alertBDocRun3[ALERT_FLAPPING_HISTORY]).to.eql([ + ...alertBDocRun2[ALERT_FLAPPING_HISTORY]!, false, ]); // alertC should have 2 docs const alertCDocs = alertDocsRun3.filter( - (doc) => doc._source!.kibana.alert.instance.id === 'alertC' + (doc) => doc._source![ALERT_INSTANCE_ID] === 'alertC' ); // alertC recovered doc should be exactly the same as the alertC doc from prior run const recoveredAlertCDoc = alertCDocs.find( - (doc) => doc._source!.kibana.alert.rule.execution?.uuid !== executionUuid + (doc) => doc._source![ALERT_RULE_EXECUTION_UUID] !== executionUuid )!._source!; expect(recoveredAlertCDoc).to.eql(alertCDocRun2); // alertC doc from current execution const alertCDocRun3 = alertCDocs.find( - (doc) => doc._source!.kibana.alert.rule.execution?.uuid === executionUuid + (doc) => doc._source![ALERT_RULE_EXECUTION_UUID] === executionUuid )!._source!; expect(alertCDocRun3.instancePattern).to.eql(pattern.alertC); // uuid is the different from prior run] - expect(alertCDocRun3.kibana.alert.uuid).not.to.equal(alertCDocRun2.kibana.alert.uuid); - expect(alertCDocRun3.kibana.alert.action_group).to.equal('default'); + expect(alertCDocRun3[ALERT_UUID]).not.to.equal(alertCDocRun2[ALERT_UUID]); + expect(alertCDocRun3[ALERT_ACTION_GROUP]).to.equal('default'); // patternIndex should be 2 for the third run expect(alertCDocRun3.patternIndex).to.equal(2); // start time should be defined and different from the prior run - expect(alertCDocRun3.kibana.alert.start).to.match(timestampPattern); - expect(alertCDocRun3.kibana.alert.start).not.to.equal(alertCDocRun2.kibana.alert.start); + expect(alertCDocRun3[ALERT_START]).to.match(timestampPattern); + expect(alertCDocRun3[ALERT_START]).not.to.equal(alertCDocRun2[ALERT_START]); // timestamp should be defined and not the same as prior run expect(alertCDocRun3['@timestamp']).to.match(timestampPattern); // duration should be '0' since this is a new alert - expect(alertCDocRun3.kibana.alert.duration?.us).to.equal('0'); + expect(alertCDocRun3[ALERT_DURATION]).to.equal('0'); // flapping false, flapping history should be history from prior run with additional entry - expect(alertCDocRun3.kibana.alert.flapping).to.equal(false); - expect(alertCDocRun3.kibana.alert.flapping_history).to.eql([ - ...alertCDocRun2.kibana.alert.flapping_history!, + expect(alertCDocRun3[ALERT_FLAPPING]).to.equal(false); + expect(alertCDocRun3[ALERT_FLAPPING_HISTORY]).to.eql([ + ...alertCDocRun2[ALERT_FLAPPING_HISTORY]!, true, ]); // event.action should be 'open' - expect(alertCDocRun3.event?.action).to.eql('open'); + expect(alertCDocRun3[EVENT_ACTION]).to.eql('open'); expect(alertCDocRun3.tags).to.eql(['foo']); // these values should be the same as previous run - expect(alertCDocRun3.event?.kind).to.eql('signal'); - expect(alertCDocRun3.kibana.alert.workflow_status).to.eql('open'); - expect(alertCDocRun3.kibana.alert.time_range?.gte).to.equal(alertCDocRun3.kibana.alert.start); + expect(alertCDocRun3[EVENT_KIND]).to.eql('signal'); + expect(alertCDocRun3[ALERT_WORKFLOW_STATUS]).to.eql('open'); + expect(alertCDocRun3[ALERT_TIME_RANGE]?.gte).to.equal(alertCDocRun3[ALERT_START]); }); }); @@ -444,20 +453,20 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F const source: PatternFiringAlert = alertDocs[i]._source!; // Each doc should have a copy of the rule data - expect(source.kibana.alert.rule.category).to.equal( + expect(source[ALERT_RULE_CATEGORY]).to.equal( 'Test: Firing on a Pattern and writing Alerts as Data' ); - expect(source.kibana.alert.rule.consumer).to.equal('alertsFixture'); - expect(source.kibana.alert.rule.name).to.equal('abc'); - expect(source.kibana.alert.rule.producer).to.equal('alertsFixture'); - expect(source.kibana.alert.rule.tags).to.eql(['foo']); - expect(source.kibana.alert.rule.rule_type_id).to.equal('test.patternFiringAad'); - expect(source.kibana.alert.rule.uuid).to.equal(ruleId); - expect(source.kibana.alert.rule.parameters).to.eql(ruleParameters); - expect(source.kibana.space_ids).to.eql(['space1']); + expect(source[ALERT_RULE_CONSUMER]).to.equal('alertsFixture'); + expect(source[ALERT_RULE_NAME]).to.equal('abc'); + expect(source[ALERT_RULE_PRODUCER]).to.equal('alertsFixture'); + expect(source[ALERT_RULE_TAGS]).to.eql(['foo']); + expect(source[ALERT_RULE_TYPE_ID]).to.equal('test.patternFiringAad'); + expect(source[ALERT_RULE_UUID]).to.equal(ruleId); + expect(source[ALERT_RULE_PARAMETERS]).to.eql(ruleParameters); + expect(source[SPACE_IDS]).to.eql(['space1']); if (executionUuid) { - expect(source.kibana.alert.rule.execution?.uuid).to.equal(executionUuid); + expect(source[ALERT_RULE_EXECUTION_UUID]).to.equal(executionUuid); } } } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_conflicts.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_conflicts.ts index c0243dbd482fd..ea53cbe33c98f 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_conflicts.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_conflicts.ts @@ -13,6 +13,14 @@ import { ESTestIndexTool } from '@kbn/alerting-api-integration-helpers'; import { basename } from 'node:path'; import { v4 as uuidv4 } from 'uuid'; import { get, omit } from 'lodash'; +import { + ALERT_ACTION_GROUP, + ALERT_CASE_IDS, + ALERT_INSTANCE_ID, + ALERT_STATUS, + ALERT_WORKFLOW_STATUS, + ALERT_WORKFLOW_TAGS, +} from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { Spaces } from '../../../../scenarios'; import { getTestRuleData, getUrlPrefix, ObjectRemover } from '../../../../../common/lib'; @@ -21,7 +29,7 @@ type AlertDoc = Alert & { runCount: number }; // sort results of a search of alert docs by alert instance id function sortAlertDocsByInstanceId(a: SearchHit, b: SearchHit) { - return a._source!.kibana.alert.instance.id.localeCompare(b._source!.kibana.alert.instance.id); + return a._source![ALERT_INSTANCE_ID].localeCompare(b._source![ALERT_INSTANCE_ID]); } // eslint-disable-next-line import/no-default-export @@ -250,16 +258,12 @@ async function adHocUpdate(es: Client, index: string, id: string) { // we'll do the adhoc updates with this data const DocUpdate = { runCount: -1, // rule-specific field, will be overwritten by rule execution - kibana: { - alert: { - action_group: 'not-the-default', // will be overwritten by rule execution - // below are all fields that will NOT be overwritten by rule execution - workflow_status: 'a-ok!', - workflow_tags: ['fee', 'fi', 'fo', 'fum'], - case_ids: ['123', '456', '789'], - status: 'untracked', - }, - }, + [ALERT_ACTION_GROUP]: 'not-the-default', // will be overwritten by rule execution + // below are all fields that will NOT be overwritten by rule execution + [ALERT_WORKFLOW_STATUS]: 'a-ok!', + [ALERT_WORKFLOW_TAGS]: ['fee', 'fi', 'fo', 'fum'], + [ALERT_CASE_IDS]: ['123', '456', '789'], + [ALERT_STATUS]: 'untracked', }; const SkipFields = [ diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_flapping.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_flapping.ts index 3bff92d2470c9..f10047e8a25b2 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_flapping.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_flapping.ts @@ -9,6 +9,7 @@ import expect from '@kbn/expect'; import { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { Alert } from '@kbn/alerts-as-data-utils'; import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; +import { ALERT_FLAPPING, ALERT_FLAPPING_HISTORY } from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { Spaces } from '../../../../scenarios'; import { @@ -102,14 +103,14 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F // Newest alert doc is first // Flapping history for newest alert doc should match flapping history in state - expect(alertDocs[0]._source!.kibana.alert.flapping_history).to.eql( + expect(alertDocs[0]._source![ALERT_FLAPPING_HISTORY]).to.eql( state.alertRecoveredInstances.alertA.meta.flappingHistory ); // Flapping value for alert doc should be false while flapping value for state should be true // This is because we write out the alert doc BEFORE calculating the latest flapping state and // persisting into task state - expect(alertDocs[0]._source!.kibana.alert.flapping).to.equal(false); + expect(alertDocs[0]._source![ALERT_FLAPPING]).to.equal(false); expect(state.alertRecoveredInstances.alertA.meta.flapping).to.equal(true); // Run the rule 6 more times @@ -132,12 +133,12 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F // Newest alert doc is first // Flapping history for newest alert doc should match flapping history in state - expect(alertDocs[0]._source!.kibana.alert.flapping_history).to.eql( + expect(alertDocs[0]._source![ALERT_FLAPPING_HISTORY]).to.eql( state.alertInstances.alertA.meta.flappingHistory ); // Flapping value for alert doc and task state should be true because alert is flapping - expect(alertDocs[0]._source!.kibana.alert.flapping).to.equal(true); + expect(alertDocs[0]._source![ALERT_FLAPPING]).to.equal(true); expect(state.alertInstances.alertA.meta.flapping).to.equal(true); // Run the rule 7 more times @@ -160,13 +161,13 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F // Newest alert doc is first // Flapping history for newest alert doc should match flapping history in state - expect(alertDocs[0]._source!.kibana.alert.flapping_history).to.eql( + expect(alertDocs[0]._source![ALERT_FLAPPING_HISTORY]).to.eql( state.alertRecoveredInstances.alertA.meta.flappingHistory ); // Flapping value for alert doc and task state should be false because alert was active for long // enough to reset the flapping state - expect(alertDocs[0]._source!.kibana.alert.flapping).to.equal(false); + expect(alertDocs[0]._source![ALERT_FLAPPING]).to.equal(false); expect(state.alertRecoveredInstances.alertA.meta.flapping).to.equal(false); }); @@ -231,14 +232,14 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F // Newest alert doc is first // Flapping history for newest alert doc should match flapping history in state - expect(alertDocs[0]._source!.kibana.alert.flapping_history).to.eql( + expect(alertDocs[0]._source![ALERT_FLAPPING_HISTORY]).to.eql( state.alertRecoveredInstances.alertA.meta.flappingHistory ); // Flapping value for alert doc should be false while flapping value for state should be true // This is because we write out the alert doc BEFORE calculating the latest flapping state and // persisting into task state - expect(alertDocs[0]._source!.kibana.alert.flapping).to.equal(false); + expect(alertDocs[0]._source![ALERT_FLAPPING]).to.equal(false); expect(state.alertRecoveredInstances.alertA.meta.flapping).to.equal(true); // Run the rule 6 more times @@ -261,12 +262,12 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F // Newest alert doc is first // Flapping history for newest alert doc should match flapping history in state - expect(alertDocs[0]._source!.kibana.alert.flapping_history).to.eql( + expect(alertDocs[0]._source![ALERT_FLAPPING_HISTORY]).to.eql( state.alertInstances.alertA.meta.flappingHistory ); // Flapping value for alert doc and task state should be true because alert is flapping - expect(alertDocs[0]._source!.kibana.alert.flapping).to.equal(true); + expect(alertDocs[0]._source![ALERT_FLAPPING]).to.equal(true); expect(state.alertInstances.alertA.meta.flapping).to.equal(true); // Run the rule 3 more times @@ -289,12 +290,12 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F // Newest alert doc is first // Flapping history for newest alert doc should match flapping history in state - expect(alertDocs[0]._source!.kibana.alert.flapping_history).to.eql( + expect(alertDocs[0]._source![ALERT_FLAPPING_HISTORY]).to.eql( state.alertRecoveredInstances.alertA.meta.flappingHistory ); // Flapping value for alert doc and task state should be true because alert recovered while flapping - expect(alertDocs[0]._source!.kibana.alert.flapping).to.equal(true); + expect(alertDocs[0]._source![ALERT_FLAPPING]).to.equal(true); expect(state.alertRecoveredInstances.alertA.meta.flapping).to.equal(true); }); @@ -351,7 +352,7 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F expect(alertDocs.length).to.equal(2); // Alert is recovered and flapping - expect(alertDocs[0]._source!.kibana.alert.flapping).to.equal(true); + expect(alertDocs[0]._source![ALERT_FLAPPING]).to.equal(true); expect(state.alertRecoveredInstances.alertA.meta.flapping).to.equal(true); }); @@ -417,12 +418,12 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F // Newest alert doc is first // Flapping history for newest alert doc should match flapping history in state - expect(alertDocs[0]._source!.kibana.alert.flapping_history).to.eql( + expect(alertDocs[0]._source![ALERT_FLAPPING_HISTORY]).to.eql( state.alertRecoveredInstances.alertA.meta.flappingHistory ); // Alert shouldn't be flapping because the status change threshold hasn't been exceeded - expect(alertDocs[0]._source!.kibana.alert.flapping).to.equal(false); + expect(alertDocs[0]._source![ALERT_FLAPPING]).to.equal(false); expect(state.alertRecoveredInstances.alertA.meta.flapping).to.equal(false); // Run the rule 1 more time @@ -445,14 +446,14 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F // Newest alert doc is first // Flapping history for newest alert doc should match flapping history in state - expect(alertDocs[0]._source!.kibana.alert.flapping_history).to.eql( + expect(alertDocs[0]._source![ALERT_FLAPPING_HISTORY]).to.eql( state.alertInstances.alertA.meta.flappingHistory ); // Flapping value for alert doc should be false while flapping value for state should be true // This is because we write out the alert doc BEFORE calculating the latest flapping state and // persisting into task state - expect(alertDocs[0]._source!.kibana.alert.flapping).to.equal(false); + expect(alertDocs[0]._source![ALERT_FLAPPING]).to.equal(false); expect(state.alertInstances.alertA.meta.flapping).to.equal(true); // Run the rule 6 more times @@ -475,14 +476,14 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F // Newest alert doc is first // Flapping history for newest alert doc should match flapping history in state - expect(alertDocs[0]._source!.kibana.alert.flapping_history).to.eql( + expect(alertDocs[0]._source![ALERT_FLAPPING_HISTORY]).to.eql( state.alertInstances.alertA.meta.flappingHistory ); // Flapping value for alert doc should be true while flapping value for state should be false // This is because we write out the alert doc BEFORE calculating the latest flapping state and // persisting into task state - expect(alertDocs[0]._source!.kibana.alert.flapping).to.equal(true); + expect(alertDocs[0]._source![ALERT_FLAPPING]).to.equal(true); expect(state.alertInstances.alertA.meta.flapping).to.equal(false); // Run the rule 3 more times @@ -505,12 +506,12 @@ export default function createAlertsAsDataInstallResourcesTest({ getService }: F // Newest alert doc is first // Flapping history for newest alert doc should match flapping history in state - expect(alertDocs[0]._source!.kibana.alert.flapping_history).to.eql( + expect(alertDocs[0]._source![ALERT_FLAPPING_HISTORY]).to.eql( state.alertInstances.alertA.meta.flappingHistory ); // Flapping value for alert doc and task state should be true because lookback threshold exceeded - expect(alertDocs[0]._source!.kibana.alert.flapping).to.equal(false); + expect(alertDocs[0]._source![ALERT_FLAPPING]).to.equal(false); expect(state.alertInstances.alertA.meta.flapping).to.equal(false); }); }); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/alert_documents.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/alert_documents.ts index 799d87f65e10f..10e5e4d0c88e1 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/alert_documents.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/alert_documents.ts @@ -7,6 +7,36 @@ import expect from '@kbn/expect'; import { unset } from 'lodash'; +import { + ALERT_ACTION_GROUP, + ALERT_DURATION, + ALERT_FLAPPING, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, + ALERT_REASON, + ALERT_RULE_CATEGORY, + ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + ALERT_RULE_PRODUCER, + ALERT_RULE_REVISION, + ALERT_RULE_TAGS, + ALERT_RULE_TYPE_ID, + ALERT_RULE_UUID, + ALERT_START, + ALERT_STATUS, + ALERT_TIME_RANGE, + ALERT_URL, + ALERT_UUID, + ALERT_WORKFLOW_STATUS, + EVENT_ACTION, + EVENT_KIND, + SPACE_IDS, + TAGS, + VERSION, +} from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { createEsQueryRule } from './helpers/alerting_api_helper'; import { waitForAlertInIndex, waitForNumRuleRuns } from './helpers/alerting_wait_for_helpers'; @@ -70,18 +100,17 @@ export default function ({ getService }: FtrProviderContext) { expect(new Date(hits1['@timestamp'])).to.be.a(Date); // should be open, first time, but also seen sometimes active; timing? - expect(OPEN_OR_ACTIVE.has(hits1.event.action)).to.be(true); - expect(hits1.kibana.alert.flapping_history).to.be.an(Array); - expect(hits1.kibana.alert.maintenance_window_ids).to.be.an(Array); - expect(typeof hits1.kibana.alert.reason).to.be('string'); - expect(typeof hits1.kibana.alert.rule.execution.uuid).to.be('string'); - expect(typeof hits1.kibana.alert.duration).to.be('object'); - expect(new Date(hits1.kibana.alert.start)).to.be.a(Date); - expect(typeof hits1.kibana.alert.time_range).to.be('object'); - expect(typeof hits1.kibana.alert.uuid).to.be('string'); - expect(typeof hits1.kibana.alert.url).to.be('string'); - expect(typeof hits1.kibana.alert.duration.us).to.be('string'); - expect(typeof hits1.kibana.version).to.be('string'); + expect(OPEN_OR_ACTIVE.has(hits1[EVENT_ACTION])).to.be(true); + expect(hits1[ALERT_FLAPPING_HISTORY]).to.be.an(Array); + expect(hits1[ALERT_MAINTENANCE_WINDOW_IDS]).to.be.an(Array); + expect(typeof hits1[ALERT_REASON]).to.be('string'); + expect(typeof hits1[ALERT_RULE_EXECUTION_UUID]).to.be('string'); + expect(typeof hits1[ALERT_DURATION]).to.be('string'); + expect(new Date(hits1[ALERT_START])).to.be.a(Date); + expect(typeof hits1[ALERT_TIME_RANGE]).to.be('object'); + expect(typeof hits1[ALERT_UUID]).to.be('string'); + expect(typeof hits1[ALERT_URL]).to.be('string'); + expect(typeof hits1[VERSION]).to.be('string'); // remove fields we aren't going to compare directly const fields = [ @@ -105,51 +134,39 @@ export default function ({ getService }: FtrProviderContext) { } const expected = { - event: { - kind: 'signal', - }, - tags: [], - kibana: { - space_ids: ['default'], - alert: { - title: "rule 'always fire' matched query", - evaluation: { - conditions: 'Number of matching documents is greater than -1', - value: 0, - }, - action_group: 'query matched', - flapping: false, - duration: {}, - instance: { id: 'query matched' }, - status: 'active', - workflow_status: 'open', - rule: { - category: 'Elasticsearch query', - consumer: 'alerts', - name: 'always fire', - execution: {}, - parameters: { - size: 100, - thresholdComparator: '>', - threshold: [-1], - index: ['alert-test-data'], - timeField: 'date', - esQuery: '{"query":{"match_all":{}}}', - timeWindowSize: 20, - timeWindowUnit: 's', - excludeHitsFromPreviousRun: true, - aggType: 'count', - groupBy: 'all', - searchType: 'esQuery', - }, - producer: 'stackAlerts', - revision: 0, - rule_type_id: '.es-query', - tags: [], - uuid: ruleId, - }, - }, + [EVENT_KIND]: 'signal', + [TAGS]: [], + [SPACE_IDS]: ['default'], + ['kibana.alert.title']: "rule 'always fire' matched query", + ['kibana.alert.evaluation.conditions']: 'Number of matching documents is greater than -1', + ['kibana.alert.evaluation.value']: '0', + [ALERT_ACTION_GROUP]: 'query matched', + [ALERT_FLAPPING]: false, + [ALERT_INSTANCE_ID]: 'query matched', + [ALERT_STATUS]: 'active', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_RULE_CATEGORY]: 'Elasticsearch query', + [ALERT_RULE_CONSUMER]: 'alerts', + [ALERT_RULE_NAME]: 'always fire', + [ALERT_RULE_PARAMETERS]: { + size: 100, + thresholdComparator: '>', + threshold: [-1], + index: ['alert-test-data'], + timeField: 'date', + esQuery: '{"query":{"match_all":{}}}', + timeWindowSize: 20, + timeWindowUnit: 's', + excludeHitsFromPreviousRun: true, + aggType: 'count', + groupBy: 'all', + searchType: 'esQuery', }, + [ALERT_RULE_PRODUCER]: 'stackAlerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: '.es-query', + [ALERT_RULE_TAGS]: [], + [ALERT_RULE_UUID]: ruleId, }; expect(hits1).to.eql(expected); @@ -216,10 +233,10 @@ export default function ({ getService }: FtrProviderContext) { const hits2 = alResp2.hits.hits[0]._source as Record; expect(hits2['@timestamp']).to.be.greaterThan(hits1['@timestamp']); - expect(OPEN_OR_ACTIVE.has(hits1?.event?.action)).to.be(true); - expect(hits2?.event?.action).to.be('active'); - expect(parseInt(hits1?.kibana?.alert?.duration?.us, 10)).to.not.be.lessThan(0); - expect(hits2?.kibana?.alert?.duration?.us).not.to.be('0'); + expect(OPEN_OR_ACTIVE.has(hits1[EVENT_ACTION])).to.be(true); + expect(hits2[EVENT_ACTION]).to.be('active'); + expect(parseInt(hits1[ALERT_DURATION], 10)).to.not.be.lessThan(0); + expect(hits2[ALERT_DURATION]).not.to.be('0'); // remove fields we know will be different const fields = [ diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts index 63e34eefae79d..7daddff58ce76 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts @@ -6,6 +6,25 @@ */ import expect from '@kbn/expect'; +import { + EVENT_KIND, + ALERT_ACTION_GROUP, + ALERT_FLAPPING, + ALERT_INSTANCE_ID, + ALERT_RULE_CATEGORY, + ALERT_RULE_CONSUMER, + ALERT_RULE_NAME, + ALERT_RULE_PARAMETERS, + ALERT_RULE_PRODUCER, + ALERT_RULE_REVISION, + ALERT_RULE_TYPE_ID, + ALERT_RULE_TAGS, + ALERT_RULE_UUID, + ALERT_STATUS, + ALERT_WORKFLOW_STATUS, + SPACE_IDS, + TAGS, +} from '@kbn/rule-data-utils'; import { omit, padStart } from 'lodash'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { createIndexConnector, createEsQueryRule } from './helpers/alerting_api_helper'; @@ -141,51 +160,39 @@ export default function ({ getService }: FtrProviderContext) { const alertDocument = resp2.hits.hits[0]._source as Record; expect(omit(alertDocument, fields)).to.eql({ - event: { - kind: 'signal', - }, - tags: [], - kibana: { - space_ids: ['default'], - alert: { - title: "rule 'always fire' matched query", - evaluation: { - conditions: 'Number of matching documents is greater than -1', - value: 0, - }, - action_group: 'query matched', - flapping: false, - duration: {}, - instance: { id: 'query matched' }, - status: 'active', - workflow_status: 'open', - rule: { - category: 'Elasticsearch query', - consumer: 'alerts', - name: 'always fire', - execution: {}, - parameters: { - size: 100, - thresholdComparator: '>', - threshold: [-1], - index: ['alert-test-data'], - timeField: 'date', - esQuery: '{\n "query":{\n "match_all" : {}\n }\n}', - timeWindowSize: 20, - timeWindowUnit: 's', - excludeHitsFromPreviousRun: true, - aggType: 'count', - groupBy: 'all', - searchType: 'esQuery', - }, - producer: 'stackAlerts', - revision: 0, - rule_type_id: '.es-query', - tags: [], - uuid: ruleId, - }, - }, + [EVENT_KIND]: 'signal', + ['kibana.alert.title']: "rule 'always fire' matched query", + ['kibana.alert.evaluation.conditions']: 'Number of matching documents is greater than -1', + ['kibana.alert.evaluation.value']: '0', + [ALERT_ACTION_GROUP]: 'query matched', + [ALERT_FLAPPING]: false, + [ALERT_INSTANCE_ID]: 'query matched', + [ALERT_STATUS]: 'active', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_RULE_CATEGORY]: 'Elasticsearch query', + [ALERT_RULE_CONSUMER]: 'alerts', + [ALERT_RULE_NAME]: 'always fire', + [ALERT_RULE_PARAMETERS]: { + size: 100, + thresholdComparator: '>', + threshold: [-1], + index: ['alert-test-data'], + timeField: 'date', + esQuery: '{\n "query":{\n "match_all" : {}\n }\n}', + timeWindowSize: 20, + timeWindowUnit: 's', + excludeHitsFromPreviousRun: true, + aggType: 'count', + groupBy: 'all', + searchType: 'esQuery', }, + [ALERT_RULE_PRODUCER]: 'stackAlerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: '.es-query', + [ALERT_RULE_TAGS]: [], + [ALERT_RULE_UUID]: ruleId, + [SPACE_IDS]: ['default'], + [TAGS]: [], }); }); @@ -273,51 +280,39 @@ export default function ({ getService }: FtrProviderContext) { const alertDocument = resp2.hits.hits[0]._source as Record; expect(omit(alertDocument, fields)).to.eql({ - event: { - kind: 'signal', - }, - tags: [], - kibana: { - space_ids: ['default'], - alert: { - title: "rule 'always fire' matched query", - evaluation: { - conditions: 'Number of matching documents is greater than -1', - value: 0, - }, - action_group: 'query matched', - flapping: false, - duration: {}, - instance: { id: 'query matched' }, - status: 'active', - workflow_status: 'open', - rule: { - category: 'Elasticsearch query', - consumer: 'alerts', - name: 'always fire', - execution: {}, - parameters: { - size: 100, - thresholdComparator: '>', - threshold: [-1], - index: ['alert-test-data'], - timeField: 'date', - esQuery: '{\n "query":{\n "match_all" : {}\n }\n}', - timeWindowSize: 20, - timeWindowUnit: 's', - excludeHitsFromPreviousRun: true, - aggType: 'count', - groupBy: 'all', - searchType: 'esQuery', - }, - producer: 'stackAlerts', - revision: 0, - rule_type_id: '.es-query', - tags: [], - uuid: ruleId, - }, - }, + [EVENT_KIND]: 'signal', + ['kibana.alert.title']: "rule 'always fire' matched query", + ['kibana.alert.evaluation.conditions']: 'Number of matching documents is greater than -1', + ['kibana.alert.evaluation.value']: '0', + [ALERT_ACTION_GROUP]: 'query matched', + [ALERT_FLAPPING]: false, + [ALERT_INSTANCE_ID]: 'query matched', + [ALERT_STATUS]: 'active', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_RULE_CATEGORY]: 'Elasticsearch query', + [ALERT_RULE_CONSUMER]: 'alerts', + [ALERT_RULE_NAME]: 'always fire', + [ALERT_RULE_PARAMETERS]: { + size: 100, + thresholdComparator: '>', + threshold: [-1], + index: ['alert-test-data'], + timeField: 'date', + esQuery: '{\n "query":{\n "match_all" : {}\n }\n}', + timeWindowSize: 20, + timeWindowUnit: 's', + excludeHitsFromPreviousRun: true, + aggType: 'count', + groupBy: 'all', + searchType: 'esQuery', }, + [ALERT_RULE_PRODUCER]: 'stackAlerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: '.es-query', + [ALERT_RULE_TAGS]: [], + [ALERT_RULE_UUID]: ruleId, + [SPACE_IDS]: ['default'], + [TAGS]: [], }); }); @@ -492,51 +487,39 @@ export default function ({ getService }: FtrProviderContext) { const alertDocument = resp2.hits.hits[0]._source as Record; expect(omit(alertDocument, fields)).to.eql({ - event: { - kind: 'signal', - }, - tags: [], - kibana: { - space_ids: ['default'], - alert: { - title: "rule 'always fire' matched query", - evaluation: { - conditions: 'Number of matching documents is greater than -1', - value: 0, - }, - action_group: 'query matched', - flapping: false, - duration: {}, - instance: { id: 'query matched' }, - status: 'active', - workflow_status: 'open', - rule: { - category: 'Elasticsearch query', - consumer: 'alerts', - name: 'always fire', - execution: {}, - parameters: { - size: 100, - thresholdComparator: '>', - threshold: [-1], - index: ['alert-test-data'], - timeField: 'date', - esQuery: '{\n "query":{\n "match_all" : {}\n }\n}', - timeWindowSize: 20, - timeWindowUnit: 's', - excludeHitsFromPreviousRun: true, - aggType: 'count', - groupBy: 'all', - searchType: 'esQuery', - }, - producer: 'stackAlerts', - revision: 0, - rule_type_id: '.es-query', - tags: [], - uuid: ruleId, - }, - }, + [EVENT_KIND]: 'signal', + ['kibana.alert.title']: "rule 'always fire' matched query", + ['kibana.alert.evaluation.conditions']: 'Number of matching documents is greater than -1', + ['kibana.alert.evaluation.value']: '0', + [ALERT_ACTION_GROUP]: 'query matched', + [ALERT_FLAPPING]: false, + [ALERT_INSTANCE_ID]: 'query matched', + [ALERT_STATUS]: 'active', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_RULE_CATEGORY]: 'Elasticsearch query', + [ALERT_RULE_CONSUMER]: 'alerts', + [ALERT_RULE_NAME]: 'always fire', + [ALERT_RULE_PARAMETERS]: { + size: 100, + thresholdComparator: '>', + threshold: [-1], + index: ['alert-test-data'], + timeField: 'date', + esQuery: '{\n "query":{\n "match_all" : {}\n }\n}', + timeWindowSize: 20, + timeWindowUnit: 's', + excludeHitsFromPreviousRun: true, + aggType: 'count', + groupBy: 'all', + searchType: 'esQuery', }, + [ALERT_RULE_PRODUCER]: 'stackAlerts', + [ALERT_RULE_REVISION]: 0, + [ALERT_RULE_TYPE_ID]: '.es-query', + [ALERT_RULE_TAGS]: [], + [ALERT_RULE_UUID]: ruleId, + [SPACE_IDS]: ['default'], + [TAGS]: [], }); }); });