diff --git a/x-pack/plugins/observability_solution/observability/common/utils/alerting/get_related_alerts_query.test.ts b/x-pack/plugins/observability_solution/observability/common/utils/alerting/get_related_alerts_query.test.ts index b7b8d138f471a..d5e6cd09dab00 100644 --- a/x-pack/plugins/observability_solution/observability/common/utils/alerting/get_related_alerts_query.test.ts +++ b/x-pack/plugins/observability_solution/observability/common/utils/alerting/get_related_alerts_query.test.ts @@ -5,25 +5,56 @@ * 2.0. */ -import { getRelatedAlertKuery } from './get_related_alerts_query'; +import { + getRelatedAlertKuery, + SERVICE_NAME, + MONITOR_ID, + OBSERVER_NAME, + HOST, + KUBERNETES_POD, + DOCKER_CONTAINER, + EC2_INSTANCE, + S3_BUCKETS, + RDS_DATABASES, + SQS_QUEUES, +} from './get_related_alerts_query'; import { fromKueryExpression } from '@kbn/es-query'; describe('getRelatedAlertKuery', () => { - const tags = ['tag1:v', 'tag2']; + const tags = ['tag1:v', 'tag2', 'apm']; const groups = [ { field: 'group1Field', value: 'group1Value' }, { field: 'group2Field', value: 'group2:Value' }, ]; + const ruleId = 'ruleUuid'; + const sharedFields = [ + { name: SERVICE_NAME, value: `my-${SERVICE_NAME}` }, + { name: MONITOR_ID, value: `my-${MONITOR_ID}` }, + { name: OBSERVER_NAME, value: `my-${OBSERVER_NAME}` }, + { name: HOST, value: `my-${HOST}` }, + { name: KUBERNETES_POD, value: `my-${KUBERNETES_POD}` }, + { name: DOCKER_CONTAINER, value: `my-${DOCKER_CONTAINER}` }, + { name: EC2_INSTANCE, value: `my-${EC2_INSTANCE}` }, + { name: S3_BUCKETS, value: `my-${S3_BUCKETS}` }, + { name: RDS_DATABASES, value: `my-${RDS_DATABASES}` }, + { name: SQS_QUEUES, value: `my-${SQS_QUEUES}` }, + ]; const tagsKuery = '(tags: "tag1:v" or tags: "tag2")'; const groupsKuery = '(group1Field: "group1Value" or kibana.alert.group.value: "group1Value") or (group2Field: "group2:Value" or kibana.alert.group.value: "group2:Value")'; + const ruleKuery = '(kibana.alert.rule.uuid: "ruleUuid")'; + const sharedFieldsKuery = + `(service.name: "my-service.name") or (monitor.id: "my-monitor.id") or (observer.name: "my-observer.name")` + + ` or (host.name: "my-host.name") or (kubernetes.pod.uid: "my-kubernetes.pod.uid") or (container.id: "my-container.id")` + + ` or (cloud.instance.id: "my-cloud.instance.id") or (aws.s3.bucket.name: "my-aws.s3.bucket.name")` + + ` or (aws.rds.db_instance.arn: "my-aws.rds.db_instance.arn") or (aws.sqs.queue.name: "my-aws.sqs.queue.name")`; it('should generate correct query with no tags or groups', () => { expect(getRelatedAlertKuery()).toBeUndefined(); }); it('should generate correct query for tags', () => { - const kuery = getRelatedAlertKuery(tags); + const kuery = getRelatedAlertKuery({ tags }); expect(kuery).toEqual(tagsKuery); // Should be able to parse keury without throwing error @@ -31,7 +62,7 @@ describe('getRelatedAlertKuery', () => { }); it('should generate correct query for groups', () => { - const kuery = getRelatedAlertKuery(undefined, groups); + const kuery = getRelatedAlertKuery({ groups }); expect(kuery).toEqual(groupsKuery); // Should be able to parse keury without throwing error @@ -39,10 +70,49 @@ describe('getRelatedAlertKuery', () => { }); it('should generate correct query for tags and groups', () => { - const kuery = getRelatedAlertKuery(tags, groups); + const kuery = getRelatedAlertKuery({ tags, groups }); expect(kuery).toEqual(`${tagsKuery} or ${groupsKuery}`); // Should be able to parse keury without throwing error fromKueryExpression(kuery!); }); + + it('should generate correct query for tags, groups and ruleId', () => { + const kuery = getRelatedAlertKuery({ tags, groups, ruleId }); + expect(kuery).toEqual(`${tagsKuery} or ${groupsKuery} or ${ruleKuery}`); + + // Should be able to parse keury without throwing error + fromKueryExpression(kuery!); + }); + + it('should generate correct query for sharedFields', () => { + const kuery = getRelatedAlertKuery({ sharedFields }); + expect(kuery).toEqual(sharedFieldsKuery); + + // Should be able to parse keury without throwing error + fromKueryExpression(kuery!); + }); + + it('should generate correct query when all the fields are provided', () => { + const kuery = getRelatedAlertKuery({ tags, groups, ruleId, sharedFields }); + expect(kuery).toEqual(`${tagsKuery} or ${groupsKuery} or ${sharedFieldsKuery} or ${ruleKuery}`); + + // Should be able to parse keury without throwing error + fromKueryExpression(kuery!); + }); + + it('should not include service.name twice', () => { + const serviceNameGroups = [{ field: 'service.name', value: 'myServiceName' }]; + const serviceNameSharedFields = [{ name: SERVICE_NAME, value: `my-${SERVICE_NAME}` }]; + const kuery = getRelatedAlertKuery({ + groups: serviceNameGroups, + sharedFields: serviceNameSharedFields, + }); + expect(kuery).toEqual( + `(service.name: "myServiceName" or kibana.alert.group.value: "myServiceName")` + ); + + // Should be able to parse keury without throwing error + fromKueryExpression(kuery!); + }); }); diff --git a/x-pack/plugins/observability_solution/observability/common/utils/alerting/get_related_alerts_query.ts b/x-pack/plugins/observability_solution/observability/common/utils/alerting/get_related_alerts_query.ts index cb2ad27bc8981..bd5be2b7822b0 100644 --- a/x-pack/plugins/observability_solution/observability/common/utils/alerting/get_related_alerts_query.ts +++ b/x-pack/plugins/observability_solution/observability/common/utils/alerting/get_related_alerts_query.ts @@ -5,28 +5,102 @@ * 2.0. */ +import { ALERT_RULE_UUID } from '@kbn/rule-data-utils'; import type { Group } from '../../typings'; export interface Query { query: string; language: string; } +export interface Field { + name: string; + value: string; +} +interface Props { + tags?: string[]; + groups?: Group[]; + ruleId?: string; + sharedFields?: Field[]; +} + +// APM rules +export const SERVICE_NAME = 'service.name'; +// Synthetics rules +export const MONITOR_ID = 'monitor.id'; +// - location +export const OBSERVER_NAME = 'observer.name'; +// Inventory rule +export const HOST = 'host.name'; +export const KUBERNETES_POD = 'kubernetes.pod.uid'; +export const DOCKER_CONTAINER = 'container.id'; +export const EC2_INSTANCE = 'cloud.instance.id'; +export const S3_BUCKETS = 'aws.s3.bucket.name'; +export const RDS_DATABASES = 'aws.rds.db_instance.arn'; +export const SQS_QUEUES = 'aws.sqs.queue.name'; + +const ALL_SHARED_FIELDS = [ + SERVICE_NAME, + MONITOR_ID, + OBSERVER_NAME, + HOST, + KUBERNETES_POD, + DOCKER_CONTAINER, + EC2_INSTANCE, + S3_BUCKETS, + RDS_DATABASES, + SQS_QUEUES, +]; + +interface AlertFields { + [key: string]: any; +} + +export const getSharedFields = (alertFields: AlertFields = {}) => { + const matchedFields: Field[] = []; + ALL_SHARED_FIELDS.forEach((source) => { + Object.keys(alertFields).forEach((field) => { + if (source === field) { + const fieldValue = alertFields[field]; + matchedFields.push({ + name: source, + value: Array.isArray(fieldValue) ? fieldValue[0] : fieldValue, + }); + } + }); + }); + + return matchedFields; +}; + +const EXCLUDE_TAGS = ['apm']; -export const getRelatedAlertKuery = (tags?: string[], groups?: Group[]): string | undefined => { - const tagKueries: string[] = - tags?.map((tag) => { - return `tags: "${tag}"`; - }) ?? []; +export const getRelatedAlertKuery = ({ tags, groups, ruleId, sharedFields }: Props = {}): + | string + | undefined => { + const tagKueries = + tags + ?.filter((tag) => !EXCLUDE_TAGS.includes(tag)) + .map((tag) => { + return `tags: "${tag}"`; + }) ?? []; const groupKueries = (groups && groups.map(({ field, value }) => { return `(${field}: "${value}" or kibana.alert.group.value: "${value}")`; })) ?? []; + const ruleKueries = (ruleId && [`(${ALERT_RULE_UUID}: "${ruleId}")`]) ?? []; + const groupFields = groups?.map((group) => group.field) ?? []; + const sharedFieldsKueries = + sharedFields + ?.filter((field) => !groupFields.includes(field.name)) + .map((field) => { + return `(${field.name}: "${field.value}")`; + }) ?? []; const tagKueriesStr = tagKueries.length > 0 ? [`(${tagKueries.join(' or ')})`] : []; const groupKueriesStr = groupKueries.length > 0 ? [`${groupKueries.join(' or ')}`] : []; - const kueries = [...tagKueriesStr, ...groupKueriesStr]; + const kueries = [...tagKueriesStr, ...groupKueriesStr, ...sharedFieldsKueries, ...ruleKueries]; return kueries.length ? kueries.join(' or ') : undefined; }; diff --git a/x-pack/plugins/observability_solution/observability/common/utils/alerting/types.ts b/x-pack/plugins/observability_solution/observability/common/utils/alerting/types.ts new file mode 100644 index 0000000000000..ac68b45514bd2 --- /dev/null +++ b/x-pack/plugins/observability_solution/observability/common/utils/alerting/types.ts @@ -0,0 +1,14 @@ +/* + * 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_GROUP, TAGS } from '@kbn/rule-data-utils'; +import { Group } from '../../typings'; + +export interface ObservabilityFields { + [ALERT_GROUP]?: Group[]; + [TAGS]?: string[]; +} diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_table/alerts/get_alerts_page_table_configuration.tsx b/x-pack/plugins/observability_solution/observability/public/components/alerts_table/alerts/get_alerts_page_table_configuration.tsx index 30c912b510743..8282d90752d7c 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/alerts_table/alerts/get_alerts_page_table_configuration.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/alerts_table/alerts/get_alerts_page_table_configuration.tsx @@ -34,7 +34,8 @@ export const getAlertsPageTableConfiguration = ( config: ConfigSchema, dataViews: DataViewsServicePublic, http: HttpSetup, - notifications: NotificationsStart + notifications: NotificationsStart, + id?: string ): AlertsTableConfigurationRegistry => { const renderCustomActionsRow = (props: RenderCustomActionsRowArgs) => { return ( @@ -46,7 +47,7 @@ export const getAlertsPageTableConfiguration = ( ); }; return { - id: ALERTS_PAGE_ALERTS_TABLE_CONFIG_ID, + id: id ?? ALERTS_PAGE_ALERTS_TABLE_CONFIG_ID, cases: { featureId: casesFeatureId, owner: [observabilityFeatureId] }, columns: getColumns({ showRuleName: true }), getRenderCellValue, @@ -66,7 +67,7 @@ export const getAlertsPageTableConfiguration = ( }, ruleTypeIds: observabilityRuleTypeRegistry.list(), usePersistentControls: getPersistentControlsHook({ - groupingId: ALERTS_PAGE_ALERTS_TABLE_CONFIG_ID, + groupingId: id ?? ALERTS_PAGE_ALERTS_TABLE_CONFIG_ID, featureIds: observabilityAlertFeatureIds, services: { dataViews, diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_table/register_alerts_table_configuration.tsx b/x-pack/plugins/observability_solution/observability/public/components/alerts_table/register_alerts_table_configuration.tsx index de687c4dd7944..bc18c54d22ee3 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/alerts_table/register_alerts_table_configuration.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/alerts_table/register_alerts_table_configuration.tsx @@ -9,6 +9,7 @@ import { AlertTableConfigRegistry } from '@kbn/triggers-actions-ui-plugin/public import type { DataViewsServicePublic } from '@kbn/data-views-plugin/public/types'; import { HttpSetup } from '@kbn/core-http-browser'; import { NotificationsStart } from '@kbn/core-notifications-browser'; +import { RELATED_ALERTS_TABLE_CONFIG_ID } from '../../constants'; import type { ConfigSchema } from '../../plugin'; import { ObservabilityRuleTypeRegistry } from '../..'; import { getAlertsPageTableConfiguration } from './alerts/get_alerts_page_table_configuration'; @@ -41,6 +42,17 @@ export const registerAlertsTableConfiguration = ( ); alertTableConfigRegistry.register(alertsPageAlertsTableConfig); + // Alert details page + const alertDetailsPageAlertsTableConfig = getAlertsPageTableConfiguration( + observabilityRuleTypeRegistry, + config, + dataViews, + http, + notifications, + RELATED_ALERTS_TABLE_CONFIG_ID + ); + alertTableConfigRegistry.register(alertDetailsPageAlertsTableConfig); + // Rule details page const ruleDetailsAlertsTableConfig = getRuleDetailsTableConfiguration( observabilityRuleTypeRegistry, diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx index de74fe2ec14b9..f45a353be9a61 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx @@ -76,7 +76,6 @@ jest.mock('../../../../utils/kibana_react', () => ({ describe('AlertDetailsAppSection', () => { const queryClient = new QueryClient(); - const mockedSetRelatedAlertsKuery = jest.fn(); const renderComponent = ( alert: Partial = {}, @@ -88,7 +87,6 @@ describe('AlertDetailsAppSection', () => { @@ -118,15 +116,6 @@ describe('AlertDetailsAppSection', () => { expect(mockedRuleConditionChart.mock.calls[0]).toMatchSnapshot(); }); - it('should set relatedAlertsKuery', async () => { - renderComponent(); - - expect(mockedSetRelatedAlertsKuery).toBeCalledTimes(1); - expect(mockedSetRelatedAlertsKuery).toHaveBeenLastCalledWith( - '(tags: "tag 1" or tags: "tag 2") or (host.name: "host-1" or kibana.alert.group.value: "host-1")' - ); - }); - it('should render title on condition charts', async () => { const result = renderComponent(); diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index 7885301650ecf..b474f246988b6 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -33,7 +33,6 @@ import moment from 'moment'; import { LOGS_EXPLORER_LOCATOR_ID, LogsExplorerLocatorParams } from '@kbn/deeplinks-observability'; import { TimeRange } from '@kbn/es-query'; import { getGroupFilters } from '../../../../../common/custom_threshold_rule/helpers/get_group'; -import { getRelatedAlertKuery } from '../../../../../common/utils/alerting/get_related_alerts_query'; import { useLicense } from '../../../../hooks/use_license'; import { useKibana } from '../../../../utils/kibana_react'; import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter'; @@ -49,15 +48,10 @@ import { generateChartTitleAndTooltip } from './helpers/generate_chart_title_and interface AppSectionProps { alert: CustomThresholdAlert; rule: CustomThresholdRule; - setRelatedAlertsKuery: React.Dispatch>; } // eslint-disable-next-line import/no-default-export -export default function AlertDetailsAppSection({ - alert, - rule, - setRelatedAlertsKuery, -}: AppSectionProps) { +export default function AlertDetailsAppSection({ alert, rule }: AppSectionProps) { const services = useKibana().services; const { charts, @@ -79,7 +73,6 @@ export default function AlertDetailsAppSection({ const alertStart = alert.fields[ALERT_START]; const alertEnd = alert.fields[ALERT_END]; const groups = alert.fields[ALERT_GROUP]; - const tags = alert.fields.tags; const chartTitleAndTooltip: Array<{ title: string; tooltip: string }> = []; @@ -112,10 +105,6 @@ export default function AlertDetailsAppSection({ const annotations: EventAnnotationConfig[] = []; annotations.push(alertStartAnnotation, alertRangeAnnotation); - useEffect(() => { - setRelatedAlertsKuery(getRelatedAlertKuery(tags, groups)); - }, [groups, setRelatedAlertsKuery, tags]); - useEffect(() => { setTimeRange(getPaddedAlertTimeRange(alertStart!, alertEnd)); }, [alertStart, alertEnd]); diff --git a/x-pack/plugins/observability_solution/observability/public/components/experimental_badge.tsx b/x-pack/plugins/observability_solution/observability/public/components/experimental_badge.tsx index 19d48b449f691..399e2b783eaaa 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/experimental_badge.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/experimental_badge.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiBetaBadge } from '@elastic/eui'; +import { EuiBetaBadge, EuiBetaBadgeProps } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; @@ -23,7 +23,9 @@ export function ExperimentalBadge() { ); } -export function BetaBadge() { +export function BetaBadge( + badgeProps: Partial> +) { return ( ); } diff --git a/x-pack/plugins/observability_solution/observability/public/constants.ts b/x-pack/plugins/observability_solution/observability/public/constants.ts index 7af5d9380f6cc..b7a1ecea9c3f8 100644 --- a/x-pack/plugins/observability_solution/observability/public/constants.ts +++ b/x-pack/plugins/observability_solution/observability/public/constants.ts @@ -9,6 +9,7 @@ export const DEFAULT_INTERVAL = '60s'; export const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm'; export const ALERTS_PAGE_ALERTS_TABLE_CONFIG_ID = `alerts-page-alerts-table`; +export const RELATED_ALERTS_TABLE_CONFIG_ID = `related-alerts-table`; export const RULE_DETAILS_ALERTS_TABLE_CONFIG_ID = `rule-details-alerts-table`; export const SEARCH_BAR_URL_STORAGE_KEY = 'searchBarParams'; diff --git a/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx b/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx index 6997e60e0a5af..8f5acee54f57e 100644 --- a/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx +++ b/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx @@ -6,8 +6,9 @@ */ import React, { useEffect, useState } from 'react'; -import { i18n } from '@kbn/i18n'; import { useHistory, useLocation, useParams } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { EuiEmptyPrompt, EuiPanel, @@ -32,11 +33,12 @@ import dedent from 'dedent'; import { AlertFieldsTable } from '@kbn/alerts-ui-shared'; import { css } from '@emotion/react'; import { omit } from 'lodash'; +import { BetaBadge } from '../../components/experimental_badge'; +import { RelatedAlerts } from './components/related_alerts'; import { AlertDetailsSource } from './types'; import { SourceBar } from './components'; import { StatusBar } from './components/status_bar'; import { observabilityFeatureId } from '../../../common'; -import { RelatedAlerts } from './components/related_alerts'; import { useKibana } from '../../utils/kibana_react'; import { useFetchRule } from '../../hooks/use_fetch_rule'; import { usePluginContext } from '../../hooks/use_plugin_context'; @@ -109,7 +111,6 @@ export function AlertDetails() { const { euiTheme } = useEuiTheme(); const [sources, setSources] = useState(); - const [relatedAlertsKuery, setRelatedAlertsKuery] = useState(); const [activeTabId, setActiveTabId] = useState(() => { const searchParams = new URLSearchParams(search); const urlTabId = searchParams.get(ALERT_DETAILS_TAB_URL_STORAGE_KEY); @@ -225,7 +226,6 @@ export function AlertDetails() { rule={rule} timeZone={timeZone} setSources={setSources} - setRelatedAlertsKuery={setRelatedAlertsKuery} /> + +   + + + ), 'data-test-subj': 'relatedAlertsTab', - content: , - }); - } + content: , + }, + ]; return ( \ No newline at end of file diff --git a/x-pack/plugins/observability_solution/observability/public/pages/alert_details/components/related_alerts.tsx b/x-pack/plugins/observability_solution/observability/public/pages/alert_details/components/related_alerts.tsx index 944481e60fbe5..6ccebc34e4722 100644 --- a/x-pack/plugins/observability_solution/observability/public/pages/alert_details/components/related_alerts.tsx +++ b/x-pack/plugins/observability_solution/observability/public/pages/alert_details/components/related_alerts.tsx @@ -6,13 +6,34 @@ */ import React, { useState, useRef, useEffect } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiImage, + EuiPanel, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util'; -import { ALERT_END, ALERT_START, ALERT_UUID } from '@kbn/rule-data-utils'; +import { + ALERT_END, + ALERT_GROUP, + ALERT_RULE_UUID, + ALERT_START, + ALERT_UUID, + TAGS, +} from '@kbn/rule-data-utils'; import { BoolQuery, Filter, type Query } from '@kbn/es-query'; import { AlertsGrouping } from '@kbn/alerts-grouping'; +import { ObservabilityFields } from '../../../../common/utils/alerting/types'; import { observabilityAlertFeatureIds } from '../../../../common/constants'; +import { + getRelatedAlertKuery, + getSharedFields, +} from '../../../../common/utils/alerting/get_related_alerts_query'; import { TopAlert } from '../../..'; import { AlertSearchBarContainerState, @@ -30,25 +51,25 @@ import { Provider, useAlertSearchBarStateContainer, } from '../../../components/alert_search_bar/containers'; -import { ALERTS_PAGE_ALERTS_TABLE_CONFIG_ID, SEARCH_BAR_URL_STORAGE_KEY } from '../../../constants'; +import { RELATED_ALERTS_TABLE_CONFIG_ID, SEARCH_BAR_URL_STORAGE_KEY } from '../../../constants'; import { usePluginContext } from '../../../hooks/use_plugin_context'; import { useKibana } from '../../../utils/kibana_react'; import { buildEsQuery } from '../../../utils/build_es_query'; import { mergeBoolQueries } from '../../alerts/helpers/merge_bool_queries'; +import icon from './assets/illustration_product_no_results_magnifying_glass.svg'; const ALERTS_PER_PAGE = 50; const RELATED_ALERTS_SEARCH_BAR_ID = 'related-alerts-search-bar-o11y'; const ALERTS_TABLE_ID = 'xpack.observability.related.alerts.table'; interface Props { - alert: TopAlert; - kuery: string; + alert?: TopAlert; } const defaultState: AlertSearchBarContainerState = { ...DEFAULT_STATE, status: 'active' }; const DEFAULT_FILTERS: Filter[] = []; -export function InternalRelatedAlerts({ alert, kuery }: Props) { +export function InternalRelatedAlerts({ alert }: Props) { const kibanaServices = useKibana().services; const { http, @@ -62,9 +83,14 @@ export function InternalRelatedAlerts({ alert, kuery }: Props) { }); const [esQuery, setEsQuery] = useState<{ bool: BoolQuery }>(); - const alertStart = alert.fields[ALERT_START]; - const alertEnd = alert.fields[ALERT_END]; - const alertId = alert.fields[ALERT_UUID]; + const alertStart = alert?.fields[ALERT_START]; + const alertEnd = alert?.fields[ALERT_END]; + const alertId = alert?.fields[ALERT_UUID]; + const tags = alert?.fields[TAGS]; + const groups = alert?.fields[ALERT_GROUP]; + const ruleId = alert?.fields[ALERT_RULE_UUID]; + const sharedFields = getSharedFields(alert?.fields); + const kuery = getRelatedAlertKuery({ tags, groups, ruleId, sharedFields }); const defaultQuery = useRef([ { query: `not kibana.alert.uuid: ${alertId}`, language: 'kuery' }, @@ -79,6 +105,8 @@ export function InternalRelatedAlerts({ alert, kuery }: Props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [alertStart, alertEnd]); + if (!kuery || !alert) return ; + return ( @@ -103,7 +131,7 @@ export function InternalRelatedAlerts({ alert, kuery }: Props) { to={alertSearchBarStateProps.rangeTo} globalFilters={alertSearchBarStateProps.filters ?? DEFAULT_FILTERS} globalQuery={{ query: alertSearchBarStateProps.kuery, language: 'kuery' }} - groupingId={ALERTS_PAGE_ALERTS_TABLE_CONFIG_ID} + groupingId={RELATED_ALERTS_TABLE_CONFIG_ID} defaultGroupingOptions={DEFAULT_GROUPING_OPTIONS} getAggregationsByGroupingField={getAggregationsByGroupingField} renderGroupPanel={renderGroupPanel} @@ -122,7 +150,7 @@ export function InternalRelatedAlerts({ alert, kuery }: Props) { + + + + + + + +

+ +

+
+

+ +

+
+
+ + + +
+
+
+
+ + ); +} + export function RelatedAlerts(props: Props) { return (