Skip to content
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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
4d3cb69
[WIP] Add alerts component
jennypavlova Jul 4, 2023
0be47f1
Apply styles
jennypavlova Jul 4, 2023
2286446
Extract and reuse createAlertsEsQuery
jennypavlova Jul 5, 2023
c3a4ee2
Add alerts title and link - wip
jennypavlova Jul 7, 2023
5792ae9
Merge branch 'main' of github.com:elastic/kibana into 160371-infra-ui…
jennypavlova Jul 7, 2023
f2e43b8
Add alerts section to the existing overview content
jennypavlova Jul 7, 2023
1baf4df
Merge branch 'main' of github.com:elastic/kibana into 160371-infra-ui…
jennypavlova Jul 10, 2023
172ad3e
Alerts link wip
jennypavlova Jul 10, 2023
560e5ac
Merge branch 'main' of github.com:elastic/kibana into 160371-infra-ui…
jennypavlova Jul 11, 2023
47b1cc0
Alert links
jennypavlova Jul 11, 2023
ed0a16d
Alerts tooltip and title
jennypavlova Jul 11, 2023
6987965
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Jul 11, 2023
ad641c5
Fix translation
jennypavlova Jul 11, 2023
8513038
Merge branch 'main' of github.com:elastic/kibana into 160371-infra-ui…
jennypavlova Jul 11, 2023
9ce67ff
Merge branch '160371-infra-ui-add-alerts-to-asset-details-flyout' of …
jennypavlova Jul 11, 2023
283e943
Fix tooltip on scroll
jennypavlova Jul 12, 2023
b5df10c
Extract and organize constants
jennypavlova Jul 12, 2023
32791d9
Move constants to alerts common
jennypavlova Jul 12, 2023
e2f1a92
Move useSummaryTimeRange to alerts common
jennypavlova Jul 12, 2023
dfbb729
Merge branch 'main' into 160371-infra-ui-add-alerts-to-asset-details-…
jennypavlova Jul 12, 2023
d97a2b1
Workaround for storybook
jennypavlova Jul 12, 2023
4d63061
Merge branch 'main' into 160371-infra-ui-add-alerts-to-asset-details-…
jennypavlova Jul 12, 2023
1b139f4
CR changes
jennypavlova Jul 13, 2023
0f7b0da
Move AlertsEsQuery type to alerts common
jennypavlova Jul 13, 2023
514e2f5
Remove no data and loading mock from story
jennypavlova Jul 13, 2023
48841cf
Change h6 to h5
jennypavlova Jul 13, 2023
9f589be
Change section separator
jennypavlova Jul 13, 2023
d2d056d
Merge branch 'main' into 160371-infra-ui-add-alerts-to-asset-details-…
jennypavlova Jul 13, 2023
6b12771
Add functional tests for alerts section
jennypavlova Jul 14, 2023
d44a07f
Change title formatting
jennypavlova Jul 14, 2023
38a72b3
Merge branch 'main' into 160371-infra-ui-add-alerts-to-asset-details-…
jennypavlova Jul 14, 2023
7844470
Use smaller spinner in alerts summary in case of hideChart
jennypavlova Jul 14, 2023
ca66f86
Move useSummaryTimeRange to observability plugin
jennypavlova Jul 17, 2023
4359ea5
Use AlertStatus type from observability
jennypavlova Jul 17, 2023
77fb994
Merge branch 'main' of github.com:elastic/kibana into 160371-infra-ui…
jennypavlova Jul 17, 2023
eaee044
Fix dateRange type after merge
jennypavlova Jul 17, 2023
ed3c7f4
Merge branch 'main' into 160371-infra-ui-add-alerts-to-asset-details-…
jennypavlova Jul 17, 2023
ce1803e
Merge branch 'main' into 160371-infra-ui-add-alerts-to-asset-details-…
jennypavlova Jul 17, 2023
2741ba5
Rename hideChart to isLoadingWithoutChart
jennypavlova Jul 18, 2023
9e724c6
Merge branch 'main' into 160371-infra-ui-add-alerts-to-asset-details-…
jennypavlova Jul 18, 2023
c4a0048
Add prop to AlertSummaryWidgetLoader
jennypavlova Jul 18, 2023
6b24ae1
Merge branch 'main' into 160371-infra-ui-add-alerts-to-asset-details-…
jennypavlova Jul 18, 2023
ce1d761
Merge branch 'main' into 160371-infra-ui-add-alerts-to-asset-details-…
jennypavlova Jul 18, 2023
81c0fa6
Merge branch 'main' into 160371-infra-ui-add-alerts-to-asset-details-…
jennypavlova Jul 18, 2023
37e8fca
Merge branch 'main' into 160371-infra-ui-add-alerts-to-asset-details-…
jennypavlova Jul 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const AlertFlyout = ({ options, nodeType, filter, visible, setVisible }:
const { triggersActionsUI } = useContext(TriggerActionsContext);

const { inventoryPrefill } = useAlertPrefillContext();
const { customMetrics } = inventoryPrefill;
const { customMetrics = [] } = inventoryPrefill ?? {};
const onCloseFlyout = useCallback(() => setVisible(false), [setVisible]);
const AddAlertFlyout = useMemo(
() =>
Expand Down
57 changes: 57 additions & 0 deletions x-pack/plugins/infra/public/common/alerts/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

* 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';
Copy link
Contributor

@CoenWarmer CoenWarmer Jul 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying nit, but shouldn't the URL be alerting.html or alternatively create-rules.html?

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 alerts.html is an equally good option IMO

Copy link
Member Author

Choose a reason for hiding this comment

The 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 alerts.html but I am getting 404 ( same for https://www.elastic.co/guide/en/observability/current/create-rules.html) Can you please share the full URL?
CC: @roshan-elastic Do you think that will make sense for our users? I used the documentation url you shared in Figma :)

Copy link
Contributor

@CoenWarmer CoenWarmer Jul 17, 2023

Choose a reason for hiding this comment

The 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.

Copy link
Member Author

Choose a reason for hiding this comment

The 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;
18 changes: 18 additions & 0 deletions x-pack/plugins/infra/public/common/alerts/types.ts
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
Expand Up @@ -11,6 +11,7 @@ export {
processesChartHttpResponse,
type ProcessesHttpMocks,
} from './processes';
export { alertsSummaryHttpResponse, type AlertsSummaryHttpMocks } from './alerts';
export { anomaliesHttpResponse, type AnomaliesHttpMocks } from './anomalies';
export { snapshotAPItHttpResponse, type SnapshotAPIHttpMocks } from './snapshot_api';
export { getLogEntries } from './log_entries';
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import type { HttpStart, HttpHandler } from '@kbn/core/public';
import type { Parameters } from '@storybook/react';
import { INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH } from '../../../../../common/http_api/infra_ml';
import {
alertsSummaryHttpResponse,
anomaliesHttpResponse,
metadataHttpResponse,
processesChartHttpResponse,
processesHttpResponse,
snapshotAPItHttpResponse,
type AlertsSummaryHttpMocks,
type AnomaliesHttpMocks,
type MetadataResponseMocks,
type ProcessesHttpMocks,
Expand Down Expand Up @@ -43,6 +45,14 @@ export const getHttp = (params: Parameters): HttpStart => {
return Promise.resolve({});
}
}) as HttpHandler,
post: (async (path: string) => {
switch (path) {
case '/internal/rac/alerts/_alert_summary':
return alertsSummaryHttpResponse[params.mock as AlertsSummaryHttpMocks]();
default:
return Promise.resolve({});
}
}) as HttpHandler,
} as unknown as HttpStart;

return http;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React from 'react';
import React, { JSXElementConstructor, ReactElement } from 'react';
import { I18nProvider } from '@kbn/i18n-react';
import {
KibanaContextProvider,
Expand All @@ -18,6 +18,9 @@ import { useParameter } from '@storybook/addons';
import type { DeepPartial } from 'utility-types';
import type { LocatorPublic } from '@kbn/share-plugin/public';
import type { IKibanaSearchRequest, ISearchOptions } from '@kbn/data-plugin/public';
import { AlertSummaryWidget } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alert_summary_widget/alert_summary_widget';
import type { Theme } from '@elastic/charts/dist/utils/themes/theme';
import type { AlertSummaryWidgetProps } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alert_summary_widget';
import type { PluginKibanaContextValue } from '../../../hooks/use_kibana';
import { SourceProvider } from '../../../containers/metrics_source';
import { getHttp } from './context/http';
Expand Down Expand Up @@ -66,6 +69,20 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => {
return Promise.resolve([]);
},
},
uiSettings: {
get: () => ({ key: 'mock', defaultOverride: undefined } as any),
},
triggersActionsUi: {
getAlertSummaryWidget: AlertSummaryWidget as (
props: AlertSummaryWidgetProps
) => ReactElement<AlertSummaryWidgetProps, string | JSXElementConstructor<any>>,
},
charts: {
theme: {
useChartsTheme: () => ({} as Theme),
useChartsBaseTheme: () => ({} as Theme),
},
},
settings: {
client: {
get$: (key: string) => of(getSettings(key)),
Expand Down
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
Expand Up @@ -8,11 +8,11 @@ import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiButtonEmpty } from '@elastic/eui';

export interface LinkToAlertsRule {
export interface LinkToAlertsRuleProps {
onClick?: () => void;
}

export const LinkToAlertsRule = ({ onClick }: LinkToAlertsRule) => {
export const LinkToAlertsRule = ({ onClick }: LinkToAlertsRuleProps) => {
return (
<EuiButtonEmpty
data-test-subj="infraNodeContextPopoverCreateInventoryRuleButton"
Expand All @@ -23,8 +23,8 @@ export const LinkToAlertsRule = ({ onClick }: LinkToAlertsRule) => {
iconType="bell"
>
<FormattedMessage
id="xpack.infra.infra.nodeDetails.createAlertLink"
defaultMessage="Create inventory rule"
id="xpack.infra.infra.assetDetails.alerts.createAlertLink"
defaultMessage="Create rule"
/>
</EuiButtonEmpty>
);
Expand Down
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>
);
};
Loading