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]: [], }); }); });