mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Actionable Observability] Fix alerts' blank page in case of invalid query string (#145067)
Implements #143641 ## 📝 Summary Fixes the alerts page crash when a wrong query is entered in the search bar query string.  **Note** I am working on tests but I will create a separate PR for that. ## 🧪 How to test - Go to alerts / rule details page - Enter an invalid query such as `{`, page should not crash and you should see a toast with a related error message Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
6bba30f94c
commit
2d5709f030
10 changed files with 70 additions and 32 deletions
|
@ -8,12 +8,13 @@
|
|||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Query } from '@kbn/es-query';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { observabilityAlertFeatureIds } from '../../../config';
|
||||
import { ObservabilityAppServices } from '../../../application/types';
|
||||
import { AlertsStatusFilter } from './components';
|
||||
import { ALERT_STATUS_QUERY, DEFAULT_QUERIES } from './constants';
|
||||
import { ALERT_STATUS_QUERY, DEFAULT_QUERIES, DEFAULT_QUERY_STRING } from './constants';
|
||||
import { AlertSearchBarProps } from './types';
|
||||
import { buildEsQuery } from '../../../utils/build_es_query';
|
||||
import { AlertStatus } from '../../../../common/typings';
|
||||
|
@ -22,7 +23,7 @@ const getAlertStatusQuery = (status: string): Query[] => {
|
|||
return status ? [{ query: ALERT_STATUS_QUERY[status], language: 'kuery' }] : [];
|
||||
};
|
||||
|
||||
export function AlertSearchBar({
|
||||
export function ObservabilityAlertSearchBar({
|
||||
appName,
|
||||
rangeFrom,
|
||||
setRangeFrom,
|
||||
|
@ -41,6 +42,7 @@ export function AlertSearchBar({
|
|||
timefilter: { timefilter: timeFilterService },
|
||||
},
|
||||
},
|
||||
notifications: { toasts },
|
||||
triggersActionsUi: { getAlertsSearchBar: AlertsSearchBar },
|
||||
} = useKibana<ObservabilityAppServices>().services;
|
||||
|
||||
|
@ -66,20 +68,29 @@ export function AlertSearchBar({
|
|||
|
||||
const onSearchBarParamsChange = useCallback(
|
||||
({ dateRange, query }) => {
|
||||
timeFilterService.setTime(dateRange);
|
||||
setRangeFrom(dateRange.from);
|
||||
setRangeTo(dateRange.to);
|
||||
setKuery(query);
|
||||
setEsQuery(
|
||||
buildEsQuery(
|
||||
try {
|
||||
// First try to create es query to make sure query is valid, then save it in state
|
||||
const esQuery = buildEsQuery(
|
||||
{
|
||||
to: rangeTo,
|
||||
from: rangeFrom,
|
||||
},
|
||||
query,
|
||||
[...getAlertStatusQuery(status), ...queries]
|
||||
)
|
||||
);
|
||||
);
|
||||
setKuery(query);
|
||||
timeFilterService.setTime(dateRange);
|
||||
setRangeFrom(dateRange.from);
|
||||
setRangeTo(dateRange.to);
|
||||
setEsQuery(esQuery);
|
||||
} catch (error) {
|
||||
toasts.addError(error, {
|
||||
title: i18n.translate('xpack.observability.alerts.searchBar.invalidQueryTitle', {
|
||||
defaultMessage: 'Invalid query string',
|
||||
}),
|
||||
});
|
||||
setKuery(DEFAULT_QUERY_STRING);
|
||||
}
|
||||
},
|
||||
[
|
||||
timeFilterService,
|
||||
|
@ -91,6 +102,7 @@ export function AlertSearchBar({
|
|||
rangeFrom,
|
||||
status,
|
||||
queries,
|
||||
toasts,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -11,19 +11,20 @@ import {
|
|||
Provider,
|
||||
useAlertSearchBarStateContainer,
|
||||
} from './containers';
|
||||
import { AlertSearchBar } from './alert_search_bar';
|
||||
import { ObservabilityAlertSearchBar } from './alert_search_bar';
|
||||
import { AlertSearchBarWithUrlSyncProps } from './types';
|
||||
|
||||
function InternalAlertSearchbarWithUrlSync(props: AlertSearchBarWithUrlSyncProps) {
|
||||
const stateProps = useAlertSearchBarStateContainer();
|
||||
function AlertSearchbarWithUrlSync(props: AlertSearchBarWithUrlSyncProps) {
|
||||
const { urlStorageKey, ...searchBarProps } = props;
|
||||
const stateProps = useAlertSearchBarStateContainer(urlStorageKey);
|
||||
|
||||
return <AlertSearchBar {...props} {...stateProps} />;
|
||||
return <ObservabilityAlertSearchBar {...stateProps} {...searchBarProps} />;
|
||||
}
|
||||
|
||||
export function AlertSearchbarWithUrlSync(props: AlertSearchBarWithUrlSyncProps) {
|
||||
export function ObservabilityAlertSearchbarWithUrlSync(props: AlertSearchBarWithUrlSyncProps) {
|
||||
return (
|
||||
<Provider value={alertSearchBarStateContainer}>
|
||||
<InternalAlertSearchbarWithUrlSync {...props} />
|
||||
<AlertSearchbarWithUrlSync {...props} />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED, ALERT_STATUS } from '@kbn/
|
|||
import { AlertStatusFilter } from '../../../../common/typings';
|
||||
|
||||
export const DEFAULT_QUERIES: Query[] = [];
|
||||
export const DEFAULT_QUERY_STRING = '';
|
||||
|
||||
export const ALL_ALERTS: AlertStatusFilter = {
|
||||
status: '',
|
||||
|
|
|
@ -24,10 +24,10 @@ import {
|
|||
AlertSearchBarContainerState,
|
||||
} from './state_container';
|
||||
|
||||
export function useAlertSearchBarStateContainer() {
|
||||
export function useAlertSearchBarStateContainer(urlStorageKey: string) {
|
||||
const stateContainer = useContainer();
|
||||
|
||||
useUrlStateSyncEffect(stateContainer);
|
||||
useUrlStateSyncEffect(stateContainer, urlStorageKey);
|
||||
|
||||
const { setRangeFrom, setRangeTo, setKuery, setStatus } = stateContainer.transitions;
|
||||
const { rangeFrom, rangeTo, kuery, status } = useContainerSelector(
|
||||
|
@ -47,7 +47,10 @@ export function useAlertSearchBarStateContainer() {
|
|||
};
|
||||
}
|
||||
|
||||
function useUrlStateSyncEffect(stateContainer: AlertSearchBarStateContainer) {
|
||||
function useUrlStateSyncEffect(
|
||||
stateContainer: AlertSearchBarStateContainer,
|
||||
urlStorageKey: string
|
||||
) {
|
||||
const history = useHistory();
|
||||
const timefilterService = useTimefilterService();
|
||||
|
||||
|
@ -57,19 +60,25 @@ function useUrlStateSyncEffect(stateContainer: AlertSearchBarStateContainer) {
|
|||
useHash: false,
|
||||
useHashQuery: false,
|
||||
});
|
||||
const { start, stop } = setupUrlStateSync(stateContainer, urlStateStorage);
|
||||
const { start, stop } = setupUrlStateSync(stateContainer, urlStateStorage, urlStorageKey);
|
||||
|
||||
start();
|
||||
|
||||
syncUrlStateWithInitialContainerState(timefilterService, stateContainer, urlStateStorage);
|
||||
syncUrlStateWithInitialContainerState(
|
||||
timefilterService,
|
||||
stateContainer,
|
||||
urlStateStorage,
|
||||
urlStorageKey
|
||||
);
|
||||
|
||||
return stop;
|
||||
}, [stateContainer, history, timefilterService]);
|
||||
}, [stateContainer, history, timefilterService, urlStorageKey]);
|
||||
}
|
||||
|
||||
function setupUrlStateSync(
|
||||
stateContainer: AlertSearchBarStateContainer,
|
||||
stateStorage: IKbnUrlStateStorage
|
||||
stateStorage: IKbnUrlStateStorage,
|
||||
urlStorageKey: string
|
||||
) {
|
||||
// This handles filling the state when an incomplete URL set is provided
|
||||
const setWithDefaults = (changedState: Partial<AlertSearchBarContainerState> | null) => {
|
||||
|
@ -77,7 +86,7 @@ function setupUrlStateSync(
|
|||
};
|
||||
|
||||
return syncState({
|
||||
storageKey: '_a',
|
||||
storageKey: urlStorageKey,
|
||||
stateContainer: {
|
||||
...stateContainer,
|
||||
set: setWithDefaults,
|
||||
|
@ -89,9 +98,10 @@ function setupUrlStateSync(
|
|||
function syncUrlStateWithInitialContainerState(
|
||||
timefilterService: TimefilterContract,
|
||||
stateContainer: AlertSearchBarStateContainer,
|
||||
urlStateStorage: IKbnUrlStateStorage
|
||||
urlStateStorage: IKbnUrlStateStorage,
|
||||
urlStorageKey: string
|
||||
) {
|
||||
const urlState = urlStateStorage.get<Partial<AlertSearchBarContainerState>>('_a');
|
||||
const urlState = urlStateStorage.get<Partial<AlertSearchBarContainerState>>(urlStorageKey);
|
||||
|
||||
if (urlState) {
|
||||
const newState = {
|
||||
|
@ -115,5 +125,5 @@ function syncUrlStateWithInitialContainerState(
|
|||
stateContainer.set(defaultState);
|
||||
}
|
||||
|
||||
urlStateStorage.set('_a', stateContainer.get());
|
||||
urlStateStorage.set(urlStorageKey, stateContainer.get());
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { AlertSearchBar as ObservabilityAlertSearchBar } from './alert_search_bar';
|
||||
export { AlertSearchbarWithUrlSync as ObservabilityAlertSearchbarWithUrlSync } from './alert_search_bar_with_url_sync';
|
||||
export { ObservabilityAlertSearchBar } from './alert_search_bar';
|
||||
export { ObservabilityAlertSearchbarWithUrlSync } from './alert_search_bar_with_url_sync';
|
||||
|
|
|
@ -27,13 +27,17 @@ interface AlertSearchBarStateTransitions {
|
|||
setStatus: (status: AlertStatus) => AlertSearchBarContainerState;
|
||||
}
|
||||
|
||||
export interface AlertSearchBarWithUrlSyncProps {
|
||||
export interface CommonAlertSearchBarProps {
|
||||
appName: string;
|
||||
setEsQuery: (query: { bool: BoolQuery }) => void;
|
||||
queries?: Query[];
|
||||
}
|
||||
|
||||
export interface AlertSearchBarWithUrlSyncProps extends CommonAlertSearchBarProps {
|
||||
urlStorageKey: string;
|
||||
}
|
||||
|
||||
export interface AlertSearchBarProps
|
||||
extends AlertSearchBarContainerState,
|
||||
AlertSearchBarStateTransitions,
|
||||
AlertSearchBarWithUrlSyncProps {}
|
||||
CommonAlertSearchBarProps {}
|
||||
|
|
|
@ -25,7 +25,12 @@ import { LoadingObservability } from '../../../overview';
|
|||
import './styles.scss';
|
||||
import { renderRuleStats } from '../../components/rule_stats';
|
||||
import { ObservabilityAppServices } from '../../../../application/types';
|
||||
import { ALERTS_PER_PAGE, ALERTS_SEARCH_BAR_ID, ALERTS_TABLE_ID } from './constants';
|
||||
import {
|
||||
ALERTS_PER_PAGE,
|
||||
ALERTS_SEARCH_BAR_ID,
|
||||
ALERTS_TABLE_ID,
|
||||
URL_STORAGE_KEY,
|
||||
} from './constants';
|
||||
import { RuleStatsState } from './types';
|
||||
|
||||
export function AlertsPage() {
|
||||
|
@ -132,6 +137,7 @@ export function AlertsPage() {
|
|||
<ObservabilityAlertSearchbarWithUrlSync
|
||||
appName={ALERTS_SEARCH_BAR_ID}
|
||||
setEsQuery={setEsQuery}
|
||||
urlStorageKey={URL_STORAGE_KEY}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
|
|
|
@ -9,3 +9,4 @@ export const ALERTS_PAGE_ID = 'alerts-o11y';
|
|||
export const ALERTS_SEARCH_BAR_ID = 'alerts-search-bar-o11y';
|
||||
export const ALERTS_PER_PAGE = 50;
|
||||
export const ALERTS_TABLE_ID = 'xpack.observability.alerts.alert.table';
|
||||
export const URL_STORAGE_KEY = '_a';
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
export const EXECUTION_TAB = 'execution';
|
||||
export const ALERTS_TAB = 'alerts';
|
||||
export const URL_STORAGE_KEY = 'searchBarParams';
|
||||
export const EVENT_ERROR_LOG_TAB = 'rule_error_log_list';
|
||||
export const RULE_DETAILS_PAGE_ID = 'rule-details-alerts-o11y';
|
||||
export const RULE_DETAILS_ALERTS_SEARCH_BAR_ID = 'rule-details-alerts-search-bar-o11y';
|
||||
|
|
|
@ -46,6 +46,7 @@ import {
|
|||
ALERTS_TAB,
|
||||
RULE_DETAILS_PAGE_ID,
|
||||
RULE_DETAILS_ALERTS_SEARCH_BAR_ID,
|
||||
URL_STORAGE_KEY,
|
||||
} from './constants';
|
||||
import { RuleDetailsPathParams, TabId } from './types';
|
||||
import { useBreadcrumbs } from '../../hooks/use_breadcrumbs';
|
||||
|
@ -216,6 +217,7 @@ export function RuleDetailsPage() {
|
|||
<ObservabilityAlertSearchbarWithUrlSync
|
||||
appName={RULE_DETAILS_ALERTS_SEARCH_BAR_ID}
|
||||
setEsQuery={setEsQuery}
|
||||
urlStorageKey={URL_STORAGE_KEY}
|
||||
queries={ruleQuery.current}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue