Skip to content

Commit

Permalink
[RAM] Implement global alerts page (elastic#175143)
Browse files Browse the repository at this point in the history
Adds a global alerts page to the Stack management section to allow users
to browse alerts across different solutions/apps through a unified UX.

Refs elastic#166709
Closes elastic#173647
Closes elastic#173650
Closes elastic#173648
  • Loading branch information
umbopepato authored and CoenWarmer committed Feb 15, 2024
1 parent 1e7dcd5 commit 963799f
Show file tree
Hide file tree
Showing 64 changed files with 5,403 additions and 541 deletions.
1 change: 1 addition & 0 deletions packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const AlertConsumers = {
ML: 'ml',
STACK_ALERTS: 'stackAlerts',
EXAMPLE: 'AlertingExample',
MONITORING: 'monitoring',
} as const;
export type AlertConsumers = typeof AlertConsumers[keyof typeof AlertConsumers];
export type STATUS_VALUES = 'open' | 'acknowledged' | 'closed' | 'in-progress'; // TODO: remove 'in-progress' after migration to 'acknowledged'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import React from 'react';
import { action } from '@storybook/addon-actions';
import type { DataViewBase, Query } from '@kbn/es-query';
import { DataViewBase, Query } from '@kbn/es-query';
import { storiesOf } from '@storybook/react';
import { I18nProvider } from '@kbn/i18n-react';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
Expand Down Expand Up @@ -711,4 +711,39 @@ storiesOf('SearchBar', module)
submitButtonStyle: 'full',
renderQueryInputAppend: () => <EuiButton onClick={() => {}}>Append</EuiButton>,
})
)
.add('with additional query bar menu items', () =>
wrapSearchBarInContext({
showFilterBar: true,
additionalQueryBarMenuItems: {
items: [
{
name: 'Observability rule types',
icon: 'logoObservability',
},
{
name: 'Security rule types',
icon: 'logoSecurity',
},
{
name: 'Status',
panel: 'status-panel',
},
],
panels: [
{
id: 'status-panel',
title: 'Status',
items: [
{
name: 'Active',
},
{
name: 'Inactive',
},
],
},
],
},
})
);
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ describe('Querybar Menu component', () => {
],
}),
},
additionalQueryBarMenuItems: {},
queryBarMenuRef: React.createRef(),
};
});
Expand Down Expand Up @@ -344,4 +345,65 @@ describe('Querybar Menu component', () => {

expect(component.find('[data-test-subj="filter-sets-removeAllFilters"]').length).toBeFalsy();
});

it('should render additional menu items', async () => {
const newProps: QueryBarMenuProps = {
...props,
openQueryBarMenu: true,
showFilterBar: true,
additionalQueryBarMenuItems: {
items: [
{
name: 'Test additional query bar menu item',
'data-test-subj': 'additional-query-bar-menu-item',
},
],
},
};
const component = mount(wrapQueryBarMenuComponentInContext(newProps, 'kuery'));

expect(component.find('[data-test-subj="additional-query-bar-menu-item"]').length).toBeTruthy();
});

it('should render additional menu panels', async () => {
const newProps: QueryBarMenuProps = {
...props,
openQueryBarMenu: true,
showFilterBar: true,
additionalQueryBarMenuItems: {
items: [
{
name: 'Go to nested menu',
'data-test-subj': 'additional-query-bar-menu-panel-link',
panel: 'panel-1',
},
],
panels: [
{
id: 'panel-1',
title: 'Grouped additional query bar menu items',
items: [
{
name: 'Test additional query bar menu item',
'data-test-subj': 'additional-query-bar-nested-menu-item',
},
],
},
],
},
};
const component = mount(wrapQueryBarMenuComponentInContext(newProps, 'kuery'));

component
.find('[data-test-subj="additional-query-bar-menu-panel-link"]')
.first()
.simulate('click');

await waitFor(() => {
component.update();
expect(
component.find('[data-test-subj="additional-query-bar-nested-menu-item"]').length
).toBeTruthy();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ import type { DataView } from '@kbn/data-views-plugin/public';
import type { SavedQueryService, SavedQuery, SavedQueryTimeFilter } from '@kbn/data-plugin/public';
import { euiThemeVars } from '@kbn/ui-theme';
import {
QueryBarMenuPanel,
useQueryBarMenuPanels,
QueryBarMenuPanelsProps,
QueryBarMenuPanel,
AdditionalQueryBarMenuItems,
} from './query_bar_menu_panels';
import { FilterEditorWrapper } from './filter_editor_wrapper';
import {
Expand Down Expand Up @@ -66,6 +67,7 @@ export interface QueryBarMenuProps extends WithCloseFilterEditorConfirmModalProp
hiddenPanelOptions?: QueryBarMenuPanelsProps['hiddenPanelOptions'];
onFiltersUpdated?: (filters: Filter[]) => void;
filters?: Filter[];
additionalQueryBarMenuItems: AdditionalQueryBarMenuItems;
query?: Query;
savedQuery?: SavedQuery;
onClearSavedQuery?: () => void;
Expand Down Expand Up @@ -100,6 +102,7 @@ function QueryBarMenuComponent({
toggleFilterBarMenuPopover,
onFiltersUpdated,
filters,
additionalQueryBarMenuItems,
query,
savedQuery,
onClearSavedQuery,
Expand Down Expand Up @@ -164,6 +167,7 @@ function QueryBarMenuComponent({

const panels = useQueryBarMenuPanels({
filters,
additionalQueryBarMenuItems,
savedQuery,
language,
dateRangeFrom,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import {
toggleFilterNegated,
pinFilter,
unpinFilter,
compareFilters,
COMPARE_ALL_OPTIONS,
compareFilters,
} from '@kbn/es-query';
import { i18n } from '@kbn/i18n';
import { METRIC_TYPE } from '@kbn/analytics';
Expand Down Expand Up @@ -140,6 +140,10 @@ export const strings = {
i18n.translate('unifiedSearch.filter.options.filterLanguageLabel', {
defaultMessage: 'Filter language',
}),
getQuickFiltersLabel: () =>
i18n.translate('unifiedSearch.filter.options.quickFiltersLabel', {
defaultMessage: 'Quick filters',
}),
};

export enum QueryBarMenuPanel {
Expand All @@ -151,8 +155,14 @@ export enum QueryBarMenuPanel {
selectLanguage = 'selectLanguage',
}

export interface AdditionalQueryBarMenuItems {
items?: EuiContextMenuPanelItemDescriptor[];
panels?: EuiContextMenuPanelDescriptor[];
}

export interface QueryBarMenuPanelsProps {
filters?: Filter[];
additionalQueryBarMenuItems: AdditionalQueryBarMenuItems;
savedQuery?: SavedQuery;
language: string;
dateRangeFrom?: string;
Expand Down Expand Up @@ -180,6 +190,7 @@ export interface QueryBarMenuPanelsProps {

export function useQueryBarMenuPanels({
filters,
additionalQueryBarMenuItems,
savedQuery,
language,
dateRangeFrom,
Expand Down Expand Up @@ -412,6 +423,11 @@ export function useQueryBarMenuPanels({
{ isSeparator: true }
);
}

if (showFilterBar && additionalQueryBarMenuItems.items?.length) {
items.push(...[...additionalQueryBarMenuItems.items, { isSeparator: true } as const]);
}

// saved queries actions are only shown when the showQueryInput and showFilterBar is true
if (showQueryInput && showFilterBar) {
items.push(...queryAndFiltersRelatedPanels);
Expand Down Expand Up @@ -555,7 +571,14 @@ export function useQueryBarMenuPanels({
/>
),
},
];
{
id: 4,
title: strings.getLoadCurrentFilterSetLabel(),
width: 400,
content: <div>{manageFilterSetComponent}</div>,
},
...(additionalQueryBarMenuItems.panels ?? []),
] as EuiContextMenuPanelDescriptor[];

if (hiddenPanelOptions && hiddenPanelOptions.length > 0) {
panels = panels.map((panel) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ export function createSearchBar({
dataViewPickerOverride={props.dataViewPickerOverride}
isClearable={props.isClearable}
placeholder={props.placeholder}
additionalQueryBarMenuItems={props.additionalQueryBarMenuItems}
{...overrideDefaultBehaviors(props)}
dataViewPickerComponentProps={props.dataViewPickerComponentProps}
textBasedLanguageModeErrors={props.textBasedLanguageModeErrors}
Expand Down
4 changes: 4 additions & 0 deletions src/plugins/unified_search/public/search_bar/search_bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type { SavedQueryAttributes } from '@kbn/data-plugin/common';
import { DataView } from '@kbn/data-views-plugin/public';

import { i18n } from '@kbn/i18n';
import { AdditionalQueryBarMenuItems } from '../query_string_input/query_bar_menu_panels';
import type { IUnifiedSearchPluginServices } from '../types';
import { SavedQueryMeta, SaveQueryForm } from '../saved_query_form';
import { SavedQueryManagementList } from '../saved_query_management';
Expand Down Expand Up @@ -64,6 +65,7 @@ export interface SearchBarOwnProps<QT extends AggregateQuery | Query = Query> {
showDatePicker?: boolean;
showAutoRefreshOnly?: boolean;
filters?: Filter[];
additionalQueryBarMenuItems?: AdditionalQueryBarMenuItems;
filtersForSuggestions?: Filter[];
hiddenFilterPanelOptions?: QueryBarMenuProps['hiddenPanelOptions'];
prependFilterBar?: React.ReactNode;
Expand Down Expand Up @@ -155,6 +157,7 @@ class SearchBarUI<QT extends (Query | AggregateQuery) | Query = Query> extends C
showSubmitButton: true,
showAutoRefreshOnly: false,
filtersForSuggestions: [],
additionalQueryBarMenuItems: [],
};

private services = this.props.kibana.services;
Expand Down Expand Up @@ -530,6 +533,7 @@ class SearchBarUI<QT extends (Query | AggregateQuery) | Query = Query> extends C
openQueryBarMenu={this.state.openQueryBarMenu}
onFiltersUpdated={this.props.onFiltersUpdated}
filters={this.props.filters}
additionalQueryBarMenuItems={this.props.additionalQueryBarMenuItems ?? {}}
hiddenPanelOptions={this.props.hiddenFilterPanelOptions}
query={this.state.query as Query}
savedQuery={this.props.savedQuery}
Expand Down
3 changes: 0 additions & 3 deletions x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -40216,7 +40216,6 @@
"xpack.triggersActionsUI.home.logsTabTitle": "Logs",
"xpack.triggersActionsUI.home.rulesTabTitle": "Règles",
"xpack.triggersActionsUI.home.sectionDescription": "Détectez les conditions à l'aide de règles.",
"xpack.triggersActionsUI.home.TabTitle": "Alertes (utilisation interne uniquement)",
"xpack.triggersActionsUI.inspect.modal.closeTitle": "Fermer",
"xpack.triggersActionsUI.inspect.modal.indexPatternDescription": "Modèle d'indexation qui se connecte aux index Elasticsearch. Ces index peuvent être configurés dans Kibana > Paramètres avancés.",
"xpack.triggersActionsUI.inspect.modal.indexPatternLabel": "Modèle d'indexation",
Expand Down Expand Up @@ -40354,9 +40353,7 @@
"xpack.triggersActionsUI.sections.alertsSummaryWidget.errorBody": "Une erreur s'est produite lors du chargement du récapitulatif des alertes. Contactez votre administrateur pour obtenir de l'aide.",
"xpack.triggersActionsUI.sections.alertsSummaryWidget.errorTitle": "Impossible de charger le récapitulatif des alertes",
"xpack.triggersActionsUI.sections.alertsSummaryWidget.title": "Activité des alertes",
"xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.name": "Nom",
"xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.paginationLabel": "Navigation dans les alertes",
"xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.reason": "Raison",
"xpack.triggersActionsUI.sections.alertsTable.column.actions": "Actions",
"xpack.triggersActionsUI.sections.alertsTable.title": "Tableau d’alertes",
"xpack.triggersActionsUI.sections.confirmConnectorEditClose.cancelButtonLabel": "Annuler",
Expand Down
3 changes: 0 additions & 3 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -40208,7 +40208,6 @@
"xpack.triggersActionsUI.home.logsTabTitle": "ログ",
"xpack.triggersActionsUI.home.rulesTabTitle": "ルール",
"xpack.triggersActionsUI.home.sectionDescription": "ルールを使用する条件を検出します。",
"xpack.triggersActionsUI.home.TabTitle": "アラート(内部使用のみ)",
"xpack.triggersActionsUI.inspect.modal.closeTitle": "閉じる",
"xpack.triggersActionsUI.inspect.modal.indexPatternDescription": "Elasticsearchインデックスに接続したインデックスパターンです。これらのインデックスは Kibana > 高度な設定で構成できます。",
"xpack.triggersActionsUI.inspect.modal.indexPatternLabel": "インデックスパターン",
Expand Down Expand Up @@ -40346,9 +40345,7 @@
"xpack.triggersActionsUI.sections.alertsSummaryWidget.errorBody": "アラート概要の読み込みエラーが発生しました。ヘルプについては、管理者にお問い合わせください。",
"xpack.triggersActionsUI.sections.alertsSummaryWidget.errorTitle": "アラート概要を読み込めません",
"xpack.triggersActionsUI.sections.alertsSummaryWidget.title": "アラートアクティビティ",
"xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.name": "名前",
"xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.paginationLabel": "アラートナビゲーション",
"xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.reason": "理由",
"xpack.triggersActionsUI.sections.alertsTable.column.actions": "アクション",
"xpack.triggersActionsUI.sections.alertsTable.title": "アラートテーブル",
"xpack.triggersActionsUI.sections.confirmConnectorEditClose.cancelButtonLabel": "キャンセル",
Expand Down
3 changes: 0 additions & 3 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -40188,7 +40188,6 @@
"xpack.triggersActionsUI.home.logsTabTitle": "日志",
"xpack.triggersActionsUI.home.rulesTabTitle": "规则",
"xpack.triggersActionsUI.home.sectionDescription": "使用规则来检测条件。",
"xpack.triggersActionsUI.home.TabTitle": "告警(仅限内部使用)",
"xpack.triggersActionsUI.inspect.modal.closeTitle": "关闭",
"xpack.triggersActionsUI.inspect.modal.indexPatternDescription": "连接到 Elasticsearch 索引的索引模式。可以在“Kibana”>“高级设置”中配置这些索引。",
"xpack.triggersActionsUI.inspect.modal.indexPatternLabel": "索引模式",
Expand Down Expand Up @@ -40326,9 +40325,7 @@
"xpack.triggersActionsUI.sections.alertsSummaryWidget.errorBody": "加载告警摘要时出现错误。请联系您的管理员寻求帮助。",
"xpack.triggersActionsUI.sections.alertsSummaryWidget.errorTitle": "无法加载告警摘要",
"xpack.triggersActionsUI.sections.alertsSummaryWidget.title": "告警活动",
"xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.name": "名称",
"xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.paginationLabel": "告警导航",
"xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.reason": "原因",
"xpack.triggersActionsUI.sections.alertsTable.column.actions": "操作",
"xpack.triggersActionsUI.sections.alertsTable.title": "告警表",
"xpack.triggersActionsUI.sections.confirmConnectorEditClose.cancelButtonLabel": "取消",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ export const StorybookContextDecorator: React.FC<StorybookContextDecoratorProps>
ExperimentalFeaturesService.init({
experimentalFeatures: {
rulesListDatagrid: true,
internalAlertsTable: true,
ruleTagFilter: true,
globalAlertsPage: false,
ruleStatusFilter: true,
rulesDetailLogs: true,
ruleUseExecutionStatus: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type ExperimentalFeatures = { [K in keyof typeof allowedExperimentalValue
*/
export const allowedExperimentalValues = Object.freeze({
rulesListDatagrid: true,
internalAlertsTable: false,
globalAlertsPage: false,
ruleTagFilter: true,
ruleStatusFilter: true,
rulesDetailLogs: true,
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/triggers_actions_ui/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ export const BASE_TRIGGERS_ACTIONS_UI_API_PATH = '/internal/triggers_actions_ui'
export * from './parse_interval';
export * from './experimental_features';
export * from './normalized_field_types';
export * from './alert_config';
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,4 @@
* 2.0.
*/

import { AlertConsumers } from '@kbn/rule-data-utils';

export const ALERT_TABLE_GENERIC_CONFIG_ID = `${AlertConsumers.STACK_ALERTS}-generic-alert-table`;
export const nonNullable = <T>(v: T): v is NonNullable<T> => v != null;
2 changes: 1 addition & 1 deletion x-pack/plugins/triggers_actions_ui/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"features",
"home",
"spaces",
"serverless",
"serverless"
],
"requiredBundles": [
"alerting",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,16 @@ describe('getAlertConfigIdPerRuleTypes()', () => {
'o11y-metric-threshold',
'ml-alerts-table',
]);
expect(configId).toEqual('stackAlerts-generic-alert-table');
expect(configId).toEqual('stackAlerts-generic-alerts-table');
});

test('should return the generic config ID if empty ruleTypeIds match with a configuration', () => {
const configId = alertTableConfigRegistry.getAlertConfigIdPerRuleTypes([]);
expect(configId).toEqual('stackAlerts-generic-alert-table');
expect(configId).toEqual('stackAlerts-generic-alerts-table');
});

test('should return the generic config ID if an unknown ruleTypeId match with NO configuration', () => {
const configId = alertTableConfigRegistry.getAlertConfigIdPerRuleTypes(['unknown-threshold']);
expect(configId).toEqual('stackAlerts-generic-alert-table');
expect(configId).toEqual('stackAlerts-generic-alerts-table');
});
});
Loading

0 comments on commit 963799f

Please sign in to comment.