mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Actionable Observability] Move the alerts search bar to triggers action UI plugin (#144541)
* Move alerts search bar to triggers action use package * Fix useAlertDataView test * Fix mocks for useAlertDataView test * Fix type issue and add getAlertsSearchBar mock
This commit is contained in:
parent
8ed9d8251e
commit
4d46bd79e0
18 changed files with 174 additions and 92 deletions
|
@ -122,9 +122,4 @@ export const translations = {
|
|||
}
|
||||
),
|
||||
},
|
||||
alertsSearchBar: {
|
||||
placeholder: i18n.translate('xpack.observability.alerts.searchBarPlaceholder', {
|
||||
defaultMessage: 'Search alerts (e.g. kibana.alert.evaluation.threshold > 75)',
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* 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, { useState } from 'react';
|
||||
import { DataView } from '@kbn/data-views-plugin/common';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import type { ValidFeatureId } from '@kbn/rule-data-utils';
|
||||
import { translations } from '../../../config';
|
||||
import { ObservabilityAppServices } from '../../../application/types';
|
||||
import { useAlertDataView } from '../../../hooks/use_alert_data_view';
|
||||
|
||||
type QueryLanguageType = 'lucene' | 'kuery';
|
||||
|
||||
const NO_INDEX_PATTERNS: DataView[] = [];
|
||||
|
||||
export function AlertsSearchBar({
|
||||
appName,
|
||||
featureIds,
|
||||
query,
|
||||
onQueryChange,
|
||||
rangeFrom,
|
||||
rangeTo,
|
||||
}: {
|
||||
appName: string;
|
||||
featureIds: ValidFeatureId[];
|
||||
rangeFrom?: string;
|
||||
rangeTo?: string;
|
||||
query?: string;
|
||||
onQueryChange: ({}: {
|
||||
dateRange: { from: string; to: string; mode?: 'absolute' | 'relative' };
|
||||
query?: string;
|
||||
}) => void;
|
||||
}) {
|
||||
const {
|
||||
unifiedSearch: {
|
||||
ui: { SearchBar },
|
||||
},
|
||||
} = useKibana<ObservabilityAppServices>().services;
|
||||
|
||||
const [queryLanguage, setQueryLanguage] = useState<QueryLanguageType>('kuery');
|
||||
const { value: dataView, loading, error } = useAlertDataView(featureIds);
|
||||
|
||||
return (
|
||||
<SearchBar
|
||||
appName={appName}
|
||||
indexPatterns={loading || error ? NO_INDEX_PATTERNS : [dataView!]}
|
||||
placeholder={translations.alertsSearchBar.placeholder}
|
||||
query={{ query: query ?? '', language: queryLanguage }}
|
||||
dateRangeFrom={rangeFrom}
|
||||
dateRangeTo={rangeTo}
|
||||
displayStyle="inPage"
|
||||
showFilterBar={false}
|
||||
onQuerySubmit={({ dateRange, query: nextQuery }) => {
|
||||
onQueryChange({
|
||||
dateRange,
|
||||
query: typeof nextQuery?.query === 'string' ? nextQuery.query : '',
|
||||
});
|
||||
setQueryLanguage((nextQuery?.language ?? 'kuery') as QueryLanguageType);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -9,7 +9,6 @@ export * from './alerts_flyout';
|
|||
export * from './render_cell_value';
|
||||
export * from './severity_badge';
|
||||
export * from './workflow_status_filter';
|
||||
export * from './alerts_search_bar';
|
||||
export * from './filter_for_value';
|
||||
export * from './parse_alert';
|
||||
export * from './alerts_status_filter';
|
||||
|
|
|
@ -29,7 +29,7 @@ import {
|
|||
useAlertsPageStateContainer,
|
||||
} from '../state_container';
|
||||
import './styles.scss';
|
||||
import { AlertsStatusFilter, AlertsSearchBar, ALERT_STATUS_QUERY } from '../../components';
|
||||
import { AlertsStatusFilter, ALERT_STATUS_QUERY } from '../../components';
|
||||
import { renderRuleStats } from '../../components/rule_stats';
|
||||
import { ObservabilityAppServices } from '../../../../application/types';
|
||||
import { ALERTS_PER_PAGE, ALERTS_TABLE_ID } from './constants';
|
||||
|
@ -46,7 +46,11 @@ function AlertsPage() {
|
|||
docLinks,
|
||||
http,
|
||||
notifications: { toasts },
|
||||
triggersActionsUi: { alertsTableConfigurationRegistry, getAlertsStateTable: AlertsStateTable },
|
||||
triggersActionsUi: {
|
||||
alertsTableConfigurationRegistry,
|
||||
getAlertsStateTable: AlertsStateTable,
|
||||
getAlertsSearchBar: AlertsSearchBar,
|
||||
},
|
||||
data: {
|
||||
query: {
|
||||
timefilter: { timefilter: timeFilterService },
|
||||
|
|
|
@ -38,7 +38,6 @@ import {
|
|||
} from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import { SecurityPluginStart } from '@kbn/security-plugin/public';
|
||||
import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public';
|
||||
import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
|
||||
import { observabilityAppId, observabilityFeatureId, casesPath } from '../common';
|
||||
import { createLazyObservabilityPageTemplate } from './components/shared';
|
||||
import { registerDataHandler } from './data_handler';
|
||||
|
@ -95,7 +94,6 @@ export interface ObservabilityPublicPluginsStart {
|
|||
actionTypeRegistry: ActionTypeRegistryContract;
|
||||
security: SecurityPluginStart;
|
||||
guidedOnboarding: GuidedOnboardingPluginStart;
|
||||
unifiedSearch: UnifiedSearchPublicPluginStart;
|
||||
}
|
||||
|
||||
export type ObservabilityPublicStart = ReturnType<Plugin['start']>;
|
||||
|
|
|
@ -23290,7 +23290,6 @@
|
|||
"xpack.observability.alerts.ruleStats.loadError": "Impossible de charger les statistiques de règles",
|
||||
"xpack.observability.alerts.ruleStats.muted": "Répété",
|
||||
"xpack.observability.alerts.ruleStats.ruleCount": "Nombre de règles",
|
||||
"xpack.observability.alerts.searchBarPlaceholder": "Alertes de recherche (par exemple, kibana.alert.evaluation.threshold > 75)",
|
||||
"xpack.observability.alerts.workflowStatusFilter.acknowledgedButtonLabel": "Reconnue(s)",
|
||||
"xpack.observability.alerts.workflowStatusFilter.closedButtonLabel": "Fermé",
|
||||
"xpack.observability.alerts.workflowStatusFilter.openButtonLabel": "Ouvrir",
|
||||
|
|
|
@ -23269,7 +23269,6 @@
|
|||
"xpack.observability.alerts.ruleStats.loadError": "ルール統計情報を読み込めません",
|
||||
"xpack.observability.alerts.ruleStats.muted": "スヌーズ済み",
|
||||
"xpack.observability.alerts.ruleStats.ruleCount": "ルール数",
|
||||
"xpack.observability.alerts.searchBarPlaceholder": "検索アラート(例:kibana.alert.evaluation.threshold > 75)",
|
||||
"xpack.observability.alerts.workflowStatusFilter.acknowledgedButtonLabel": "認識",
|
||||
"xpack.observability.alerts.workflowStatusFilter.closedButtonLabel": "終了",
|
||||
"xpack.observability.alerts.workflowStatusFilter.openButtonLabel": "開く",
|
||||
|
|
|
@ -23300,7 +23300,6 @@
|
|||
"xpack.observability.alerts.ruleStats.loadError": "无法加载规则统计信息",
|
||||
"xpack.observability.alerts.ruleStats.muted": "已暂停",
|
||||
"xpack.observability.alerts.ruleStats.ruleCount": "规则计数",
|
||||
"xpack.observability.alerts.searchBarPlaceholder": "搜索告警(例如 kibana.alert.evaluation.threshold > 75)",
|
||||
"xpack.observability.alerts.workflowStatusFilter.acknowledgedButtonLabel": "已确认",
|
||||
"xpack.observability.alerts.workflowStatusFilter.closedButtonLabel": "已关闭",
|
||||
"xpack.observability.alerts.workflowStatusFilter.openButtonLabel": "打开",
|
||||
|
|
|
@ -6,35 +6,41 @@
|
|||
*/
|
||||
|
||||
import { DataView } from '@kbn/data-views-plugin/common';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock';
|
||||
import type { ValidFeatureId } from '@kbn/rule-data-utils';
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { AsyncState } from 'react-use/lib/useAsync';
|
||||
import { kibanaStartMock } from '../utils/kibana_react.mock';
|
||||
import { observabilityAlertFeatureIds } from '../config';
|
||||
import { useAlertDataView } from './use_alert_data_view';
|
||||
|
||||
const mockUseKibanaReturnValue = kibanaStartMock.startContract();
|
||||
const mockUseKibanaReturnValue = createStartServicesMock();
|
||||
|
||||
jest.mock('@kbn/kibana-react-plugin/public', () => ({
|
||||
__esModule: true,
|
||||
useKibana: jest.fn(() => mockUseKibanaReturnValue),
|
||||
useKibana: jest.fn(() => ({
|
||||
services: mockUseKibanaReturnValue,
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('useAlertDataView', () => {
|
||||
const mockedDataView = 'dataView';
|
||||
const observabilityAlertFeatureIds: ValidFeatureId[] = [
|
||||
AlertConsumers.APM,
|
||||
AlertConsumers.INFRASTRUCTURE,
|
||||
AlertConsumers.LOGS,
|
||||
AlertConsumers.UPTIME,
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
mockUseKibanaReturnValue.services.http.get.mockImplementation(async () => ({
|
||||
mockUseKibanaReturnValue.http.get = jest.fn().mockReturnValue({
|
||||
index_name: [
|
||||
'.alerts-observability.uptime.alerts-*',
|
||||
'.alerts-observability.metrics.alerts-*',
|
||||
'.alerts-observability.logs.alerts-*',
|
||||
'.alerts-observability.apm.alerts-*',
|
||||
],
|
||||
}));
|
||||
mockUseKibanaReturnValue.services.data.dataViews.create.mockImplementation(
|
||||
async () => mockedDataView
|
||||
);
|
||||
});
|
||||
mockUseKibanaReturnValue.data.dataViews.create = jest.fn().mockReturnValue(mockedDataView);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -77,7 +83,7 @@ describe('useAlertDataView', () => {
|
|||
|
||||
it('returns error with no data when error happens', async () => {
|
||||
const error = new Error('http error');
|
||||
mockUseKibanaReturnValue.services.http.get.mockImplementation(async () => {
|
||||
mockUseKibanaReturnValue.http.get = jest.fn().mockImplementation(async () => {
|
||||
throw error;
|
||||
});
|
||||
|
|
@ -11,11 +11,10 @@ import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common';
|
|||
import type { ValidFeatureId } from '@kbn/rule-data-utils';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import type { AsyncState } from 'react-use/lib/useAsync';
|
||||
|
||||
import { ObservabilityAppServices } from '../application/types';
|
||||
import { TriggersAndActionsUiServices } from '../..';
|
||||
|
||||
export function useAlertDataView(featureIds: ValidFeatureId[]): AsyncState<DataView> {
|
||||
const { http, data: dataService } = useKibana<ObservabilityAppServices>().services;
|
||||
const { http, data: dataService } = useKibana<TriggersAndActionsUiServices>().services;
|
||||
const features = featureIds.sort().join(',');
|
||||
|
||||
const dataView = useAsync(async () => {
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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, { useCallback, useState } from 'react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { NO_INDEX_PATTERNS } from './constants';
|
||||
import { SEARCH_BAR_PLACEHOLDER } from './translations';
|
||||
import { AlertsSearchBarProps, QueryLanguageType } from './types';
|
||||
import { useAlertDataView } from '../../hooks/use_alert_data_view';
|
||||
import { TriggersAndActionsUiServices } from '../../..';
|
||||
|
||||
// TODO Share buildEsQuery to be used between AlertsSearchBar and AlertsStateTable component https://github.com/elastic/kibana/issues/144615
|
||||
export function AlertsSearchBar({
|
||||
appName,
|
||||
featureIds,
|
||||
query,
|
||||
onQueryChange,
|
||||
rangeFrom,
|
||||
rangeTo,
|
||||
}: AlertsSearchBarProps) {
|
||||
const {
|
||||
unifiedSearch: {
|
||||
ui: { SearchBar },
|
||||
},
|
||||
} = useKibana<TriggersAndActionsUiServices>().services;
|
||||
|
||||
const [queryLanguage, setQueryLanguage] = useState<QueryLanguageType>('kuery');
|
||||
const { value: dataView, loading, error } = useAlertDataView(featureIds);
|
||||
|
||||
const onQuerySubmit = useCallback(
|
||||
(payload) => {
|
||||
const { dateRange, query: nextQuery } = payload;
|
||||
onQueryChange({
|
||||
dateRange,
|
||||
query: typeof nextQuery?.query === 'string' ? nextQuery.query : '',
|
||||
});
|
||||
setQueryLanguage((nextQuery?.language ?? 'kuery') as QueryLanguageType);
|
||||
},
|
||||
[onQueryChange, setQueryLanguage]
|
||||
);
|
||||
|
||||
return (
|
||||
<SearchBar
|
||||
appName={appName}
|
||||
indexPatterns={loading || error ? NO_INDEX_PATTERNS : [dataView!]}
|
||||
placeholder={SEARCH_BAR_PLACEHOLDER}
|
||||
query={{ query: query ?? '', language: queryLanguage }}
|
||||
dateRangeFrom={rangeFrom}
|
||||
dateRangeTo={rangeTo}
|
||||
displayStyle="inPage"
|
||||
showFilterBar={false}
|
||||
onQuerySubmit={onQuerySubmit}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export { AlertsSearchBar as default };
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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 { DataView } from '@kbn/data-views-plugin/common';
|
||||
|
||||
export const NO_INDEX_PATTERNS: DataView[] = [];
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { AlertsSearchBar } from './alerts_search_bar';
|
||||
|
||||
export type { AlertsSearchBarProps } from './types';
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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';
|
||||
|
||||
export const SEARCH_BAR_PLACEHOLDER = i18n.translate(
|
||||
'xpack.triggersActionsUI.alertsSearchBar.placeholder',
|
||||
{
|
||||
defaultMessage: 'Search alerts (e.g. kibana.alert.evaluation.threshold > 75)',
|
||||
}
|
||||
);
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 { ValidFeatureId } from '@kbn/rule-data-utils';
|
||||
|
||||
export type QueryLanguageType = 'lucene' | 'kuery';
|
||||
|
||||
export interface AlertsSearchBarProps {
|
||||
appName: string;
|
||||
featureIds: ValidFeatureId[];
|
||||
rangeFrom?: string;
|
||||
rangeTo?: string;
|
||||
query?: string;
|
||||
onQueryChange: ({}: {
|
||||
dateRange: { from: string; to: string; mode?: 'absolute' | 'relative' };
|
||||
query?: string;
|
||||
}) => void;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 { EuiLoadingSpinner } from '@elastic/eui';
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import type { AlertsSearchBarProps } from '../application/sections/alerts_search_bar';
|
||||
|
||||
const AlertsSearchBarLazy: React.FC<AlertsSearchBarProps> = lazy(
|
||||
() => import('../application/sections/alerts_search_bar/alerts_search_bar')
|
||||
);
|
||||
|
||||
export const getAlertsSearchBarLazy = (props: AlertsSearchBarProps) => (
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<AlertsSearchBarLazy {...props} />
|
||||
</Suspense>
|
||||
);
|
|
@ -33,8 +33,10 @@ import { getRuleTagBadgeLazy } from './common/get_rule_tag_badge';
|
|||
import { getRuleEventLogListLazy } from './common/get_rule_event_log_list';
|
||||
import { getRulesListLazy } from './common/get_rules_list';
|
||||
import { getAlertsTableStateLazy } from './common/get_alerts_table_state';
|
||||
import { getAlertsSearchBarLazy } from './common/get_alerts_search_bar';
|
||||
import { getRulesListNotifyBadgeLazy } from './common/get_rules_list_notify_badge';
|
||||
import { AlertsTableStateProps } from './application/sections/alerts_table/alerts_table_state';
|
||||
import { AlertsSearchBarProps } from './application/sections/alerts_search_bar';
|
||||
import { CreateConnectorFlyoutProps } from './application/sections/action_connector_form/create_connector_flyout';
|
||||
import { EditConnectorFlyoutProps } from './application/sections/action_connector_form/edit_connector_flyout';
|
||||
import { getActionFormLazy } from './common/get_action_form';
|
||||
|
@ -86,6 +88,9 @@ function createStartMock(): TriggersAndActionsUIPublicPluginStart {
|
|||
getAlertsStateTable: (props: AlertsTableStateProps) => {
|
||||
return getAlertsTableStateLazy(props);
|
||||
},
|
||||
getAlertsSearchBar: (props: AlertsSearchBarProps) => {
|
||||
return getAlertsSearchBarLazy(props);
|
||||
},
|
||||
getAlertsTable: (props: AlertsTableProps) => {
|
||||
return getAlertsTableLazy(props);
|
||||
},
|
||||
|
|
|
@ -23,6 +23,7 @@ import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public';
|
|||
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
|
||||
import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
|
||||
import type { AlertsSearchBarProps } from './application/sections/alerts_search_bar';
|
||||
import { TypeRegistry } from './application/type_registry';
|
||||
|
||||
import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout';
|
||||
|
@ -72,6 +73,7 @@ import { registerAlertsTableConfiguration } from './application/sections/alerts_
|
|||
import { PLUGIN_ID, CONNECTORS_PLUGIN_ID } from './common/constants';
|
||||
import type { AlertsTableStateProps } from './application/sections/alerts_table/alerts_table_state';
|
||||
import { getAlertsTableStateLazy } from './common/get_alerts_table_state';
|
||||
import { getAlertsSearchBarLazy } from './common/get_alerts_search_bar';
|
||||
import { ActionAccordionFormProps } from './application/sections/action_connector_form/action_form';
|
||||
import type { FieldBrowserProps } from './application/sections/field_browser/types';
|
||||
import { getRuleDefinitionLazy } from './common/get_rule_definition';
|
||||
|
@ -108,6 +110,7 @@ export interface TriggersAndActionsUIPublicPluginStart {
|
|||
) => ReactElement<RuleEditProps>;
|
||||
getAlertsTable: (props: AlertsTableProps) => ReactElement<AlertsTableProps>;
|
||||
getAlertsStateTable: (props: AlertsTableStateProps) => ReactElement<AlertsTableStateProps>;
|
||||
getAlertsSearchBar: (props: AlertsSearchBarProps) => ReactElement<AlertsSearchBarProps>;
|
||||
getFieldBrowser: (props: FieldBrowserProps) => ReactElement<FieldBrowserProps>;
|
||||
getRuleStatusDropdown: (props: RuleStatusDropdownProps) => ReactElement<RuleStatusDropdownProps>;
|
||||
getRuleTagFilter: (props: RuleTagFilterProps) => ReactElement<RuleTagFilterProps>;
|
||||
|
@ -378,6 +381,9 @@ export class Plugin
|
|||
getAlertsStateTable: (props: AlertsTableStateProps) => {
|
||||
return getAlertsTableStateLazy(props);
|
||||
},
|
||||
getAlertsSearchBar: (props: AlertsSearchBarProps) => {
|
||||
return getAlertsSearchBarLazy(props);
|
||||
},
|
||||
getAlertsTable: (props: AlertsTableProps) => {
|
||||
return getAlertsTableLazy(props);
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue