-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Infra UI] Add alerts to asset details flyout #161677
Changes from all commits
4d3cb69
0be47f1
2286446
c3a4ee2
5792ae9
f2e43b8
1baf4df
172ad3e
560e5ac
47b1cc0
ed0a16d
6987965
ad641c5
8513038
9ce67ff
283e943
b5df10c
32791d9
e2f1a92
dfbb729
d97a2b1
4d63061
1b139f4
0f7b0da
514e2f5
48841cf
9f589be
d2d056d
6b12771
d44a07f
38a72b3
7844470
ca66f86
4359ea5
77fb994
eaee044
ed3c7f4
ce1803e
2741ba5
9e724c6
c4a0048
6b24ae1
ce1d761
81c0fa6
37e8fca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* 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 { i18n } from '@kbn/i18n'; | ||
import { ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED } from '@kbn/rule-data-utils'; | ||
import type { AlertStatusFilter } from './types'; | ||
|
||
export const ALERT_STATUS_ALL = 'all'; | ||
|
||
export const ALL_ALERTS: AlertStatusFilter = { | ||
status: ALERT_STATUS_ALL, | ||
label: i18n.translate('xpack.infra.hostsViewPage.tabs.alerts.alertStatusFilter.showAll', { | ||
defaultMessage: 'Show all', | ||
}), | ||
}; | ||
|
||
export const ACTIVE_ALERTS: AlertStatusFilter = { | ||
status: ALERT_STATUS_ACTIVE, | ||
query: { | ||
term: { | ||
[ALERT_STATUS]: { | ||
value: ALERT_STATUS_ACTIVE, | ||
}, | ||
}, | ||
}, | ||
label: i18n.translate('xpack.infra.hostsViewPage.tabs.alerts.alertStatusFilter.active', { | ||
defaultMessage: 'Active', | ||
}), | ||
}; | ||
|
||
export const RECOVERED_ALERTS: AlertStatusFilter = { | ||
status: ALERT_STATUS_RECOVERED, | ||
query: { | ||
term: { | ||
[ALERT_STATUS]: { | ||
value: ALERT_STATUS_RECOVERED, | ||
}, | ||
}, | ||
}, | ||
label: i18n.translate('xpack.infra.hostsViewPage.tabs.alerts.alertStatusFilter.recovered', { | ||
defaultMessage: 'Recovered', | ||
}), | ||
}; | ||
|
||
export const ALERT_STATUS_QUERY = { | ||
[ACTIVE_ALERTS.status]: ACTIVE_ALERTS.query, | ||
[RECOVERED_ALERTS.status]: RECOVERED_ALERTS.query, | ||
}; | ||
|
||
export const ALERTS_DOC_HREF = | ||
'https://www.elastic.co/guide/en/observability/current/create-alerts.html'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Annoying nit, but shouldn't the URL be Reason being: a user doesn't really create alerts; a user can create rules, which in turn generate alerts. The page title is 'Alerting', so There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @CoenWarmer Thanks for the input here: I tried https://www.elastic.co/guide/en/observability/current/alerting.html or with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jennypavlova I meant to suggest the URL of the page on the Elastic website should perhaps be changed. It makes sense the suggestions I gave return a 404, as they don't exist (yet). I realize this is a nit, so not blocking the PR for it, but I think it's worth having a think about. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, sorry I got confused that it should be changed here and not inside the docs webpage. It makes sense, feel free to change it (ping the team who should do that) and I can add a follow-up PR to change it here 🙂 |
||
|
||
export const ALERTS_PATH = '/app/observability/alerts'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* 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 { getTime } from '@kbn/data-plugin/common'; | ||
import { ALERT_TIME_RANGE } from '@kbn/rule-data-utils'; | ||
import { buildEsQuery, Filter, type TimeRange } from '@kbn/es-query'; | ||
import type { AlertStatus } from '@kbn/observability-plugin/common/typings'; | ||
import { ALERT_STATUS_QUERY } from './constants'; | ||
import { buildCombinedHostsFilter } from '../../utils/filters/build'; | ||
import type { AlertsEsQuery } from './types'; | ||
|
||
export const createAlertsEsQuery = ({ | ||
dateRange, | ||
hostNodeNames, | ||
status, | ||
}: { | ||
dateRange: TimeRange; | ||
hostNodeNames: string[]; | ||
status?: AlertStatus; | ||
}): AlertsEsQuery => { | ||
const alertStatusFilter = createAlertStatusFilter(status); | ||
|
||
const dateFilter = createDateFilter(dateRange); | ||
const hostsFilter = buildCombinedHostsFilter({ | ||
field: 'host.name', | ||
values: hostNodeNames, | ||
}); | ||
|
||
const filters = [alertStatusFilter, dateFilter, hostsFilter].filter(Boolean) as Filter[]; | ||
|
||
return buildEsQuery(undefined, [], filters); | ||
}; | ||
|
||
const createDateFilter = (date: TimeRange) => | ||
getTime(undefined, date, { fieldName: ALERT_TIME_RANGE }); | ||
|
||
const createAlertStatusFilter = (status: AlertStatus = 'all'): Filter | null => | ||
ALERT_STATUS_QUERY[status] ? { query: ALERT_STATUS_QUERY[status], meta: {} } : null; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* 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 type { BoolQuery, Filter } from '@kbn/es-query'; | ||
import type { AlertStatus } from '@kbn/observability-plugin/common/typings'; | ||
export interface AlertStatusFilter { | ||
status: AlertStatus; | ||
query?: Filter['query']; | ||
label: string; | ||
} | ||
|
||
export interface AlertsEsQuery { | ||
bool: BoolQuery; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
const summaryResponse = { | ||
activeAlertCount: 3, | ||
recoveredAlertCount: 3, | ||
activeAlerts: [ | ||
{ | ||
key_as_string: '1689172920000', | ||
key: 1689172920000, | ||
doc_count: 3, | ||
}, | ||
{ | ||
key_as_string: '1689172980000', | ||
key: 1689172980000, | ||
doc_count: 3, | ||
}, | ||
], | ||
recoveredAlerts: [ | ||
{ | ||
key_as_string: '2023-07-12T14:42:00.000Z', | ||
key: 1689172920000, | ||
doc_count: 3, | ||
}, | ||
{ | ||
key_as_string: '2023-07-12T14:43:00.000Z', | ||
key: 1689172980000, | ||
doc_count: 3, | ||
}, | ||
], | ||
}; | ||
|
||
export const alertsSummaryHttpResponse = { | ||
default: () => Promise.resolve({ ...summaryResponse }), | ||
}; | ||
|
||
export type AlertsSummaryHttpMocks = keyof typeof alertsSummaryHttpResponse; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* 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 React from 'react'; | ||
import { EuiText, EuiLink } from '@elastic/eui'; | ||
import { FormattedMessage } from '@kbn/i18n-react'; | ||
import { ALERTS_DOC_HREF } from '../../../common/alerts/constants'; | ||
import { LinkToAlertsHomePage } from '../links/link_to_alerts_page'; | ||
|
||
export const AlertsTooltipContent = React.memo(() => { | ||
const onClick = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => { | ||
e.stopPropagation(); | ||
}; | ||
|
||
return ( | ||
<EuiText size="xs" onClick={onClick}> | ||
<p> | ||
<FormattedMessage | ||
id="xpack.infra.assetDetails.alerts.tooltip.alertsLabel" | ||
defaultMessage="Showing alerts for this host. You can create and manage alerts in {alerts}" | ||
values={{ | ||
alerts: <LinkToAlertsHomePage />, | ||
}} | ||
/> | ||
</p> | ||
<p> | ||
<FormattedMessage | ||
id="xpack.infra.assetDetails.alerts.tooltip.documentationLabel" | ||
defaultMessage="See {documentation} for more information" | ||
values={{ | ||
documentation: ( | ||
<EuiLink | ||
data-test-subj="assetDetailsTooltipDocumentationLink" | ||
href={ALERTS_DOC_HREF} | ||
target="_blank" | ||
> | ||
<FormattedMessage | ||
id="xpack.infra.assetDetails.alerts.tooltip.documentationLink" | ||
defaultMessage="documentation" | ||
/> | ||
</EuiLink> | ||
), | ||
}} | ||
/> | ||
</p> | ||
</EuiText> | ||
); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* 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 React from 'react'; | ||
import { encode } from '@kbn/rison'; | ||
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; | ||
import { FormattedMessage } from '@kbn/i18n-react'; | ||
import { EuiButtonEmpty, EuiLink } from '@elastic/eui'; | ||
import type { TimeRange } from '@kbn/es-query'; | ||
import { ALERTS_PATH } from '../../../common/alerts/constants'; | ||
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; | ||
|
||
export interface LinkToAlertsPageProps { | ||
nodeName: string; | ||
queryField: string; | ||
dateRange: TimeRange; | ||
} | ||
|
||
export const LinkToAlertsPage = ({ nodeName, queryField, dateRange }: LinkToAlertsPageProps) => { | ||
const { services } = useKibanaContextForPlugin(); | ||
const { http } = services; | ||
|
||
const linkToAlertsPage = http.basePath.prepend( | ||
`${ALERTS_PATH}?_a=${encode({ | ||
kuery: `${queryField}:"${nodeName}"`, | ||
rangeFrom: dateRange.from, | ||
rangeTo: dateRange.to, | ||
status: 'all', | ||
})}` | ||
); | ||
|
||
return ( | ||
<RedirectAppLinks coreStart={services}> | ||
<EuiButtonEmpty | ||
data-test-subj="assetDetails-flyout-alerts-link" | ||
size="xs" | ||
iconSide="right" | ||
iconType="sortRight" | ||
flush="both" | ||
href={linkToAlertsPage} | ||
> | ||
<FormattedMessage | ||
id="xpack.infra.assetDetails.flyout.AlertsPageLinkLabel" | ||
defaultMessage="Show all" | ||
/> | ||
</EuiButtonEmpty> | ||
</RedirectAppLinks> | ||
); | ||
}; | ||
|
||
export const LinkToAlertsHomePage = () => { | ||
const { services } = useKibanaContextForPlugin(); | ||
const { http } = services; | ||
|
||
const linkToAlertsPage = http.basePath.prepend(ALERTS_PATH); | ||
|
||
return ( | ||
<RedirectAppLinks coreStart={services}> | ||
<EuiLink data-test-subj="assetDetailsTooltipDocumentationLink" href={linkToAlertsPage}> | ||
<FormattedMessage | ||
id="xpack.infra.assetDetails.table.tooltip.alertsLink" | ||
defaultMessage="alerts." | ||
/> | ||
</EuiLink> | ||
</RedirectAppLinks> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏