diff --git a/packages/kbn-alerts-ui-shared/index.ts b/packages/kbn-alerts-ui-shared/index.ts index 5c7b985cb7087..3dd1174da4129 100644 --- a/packages/kbn-alerts-ui-shared/index.ts +++ b/packages/kbn-alerts-ui-shared/index.ts @@ -10,4 +10,8 @@ export { AlertLifecycleStatusBadge } from './src/alert_lifecycle_status_badge'; export type { AlertLifecycleStatusBadgeProps } from './src/alert_lifecycle_status_badge'; export { MaintenanceWindowCallout } from './src/maintenance_window_callout'; export { AddMessageVariables } from './src/add_message_variables'; + +export * from './src/alerts_search_bar/hooks'; +export * from './src/alerts_search_bar/apis'; export { AlertsSearchBar } from './src/alerts_search_bar'; +export type { AlertsSearchBarProps } from './src/alerts_search_bar/types'; diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_aad_fields.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_aad_fields.ts index 2419010e955ee..2bbfb698fb79d 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_aad_fields.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_aad_fields.ts @@ -1,8 +1,9 @@ /* * 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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import type { HttpStart } from '@kbn/core/public'; diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_alert_fields.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_alert_fields.ts index 1060cc8f66526..2c41e8adc7875 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_alert_fields.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_alert_fields.ts @@ -1,8 +1,9 @@ /* * 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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import { ValidFeatureId } from '@kbn/rule-data-utils'; diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_alert_index_names.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_alert_index_names.ts index 3e8e9d15e3e12..9cf7fe1d3ebc8 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_alert_index_names.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_alert_index_names.ts @@ -1,8 +1,9 @@ /* * 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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import { HttpSetup } from '@kbn/core/public'; diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_rule_types.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_rule_types.ts index a53246c2ecebb..276bb9f4513a9 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_rule_types.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/fetch_rule_types.ts @@ -1,9 +1,11 @@ /* * 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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ + import { HttpSetup } from '@kbn/core/public'; import type { AsApiContract, RewriteRequestCase } from '@kbn/actions-types'; import type { RuleType } from '@kbn/triggers-actions-ui-types'; diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/index.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/index.ts new file mode 100644 index 0000000000000..e3defec5c5003 --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/apis/index.ts @@ -0,0 +1,12 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './fetch_aad_fields'; +export * from './fetch_alert_fields'; +export * from './fetch_alert_index_names'; +export * from './fetch_rule_types'; diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/constants.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/constants.ts index c0febffc03aef..5fbdaf4fa7455 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/constants.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/constants.ts @@ -1,8 +1,9 @@ /* * 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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import type { DataView, DataViewField } from '@kbn/data-views-plugin/common'; diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/index.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/index.ts new file mode 100644 index 0000000000000..bee311f222fbc --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/index.ts @@ -0,0 +1,11 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './use_alert_data_view'; +export * from './use_load_rule_types_query'; +export * from './use_rule_aad_fields'; diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_alert_data_view.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_alert_data_view.ts index 10cb9c9d681cf..bae36dd041283 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_alert_data_view.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_alert_data_view.ts @@ -1,8 +1,9 @@ /* * 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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import { useEffect, useMemo, useState } from 'react'; @@ -21,19 +22,14 @@ export interface UseAlertDataViewResult { } export interface UseAlertDataViewProps { - featureIds: ValidFeatureId[], - http: HttpStart, - dataViewsService: DataViewsContract, - toasts: ToastsStart, + featureIds: ValidFeatureId[]; + http: HttpStart; + dataViewsService: DataViewsContract; + toasts: ToastsStart; } export function useAlertDataView(props: UseAlertDataViewProps): UseAlertDataViewResult { - const { - http, - dataViewsService, - toasts, - featureIds, - } = props; + const { http, dataViewsService, toasts, featureIds } = props; const [dataViews, setDataViews] = useState([]); const features = featureIds.sort().join(','); @@ -42,7 +38,8 @@ export function useAlertDataView(props: UseAlertDataViewProps): UseAlertDataView const hasSecurityAndO11yFeatureIds = featureIds.length > 1 && featureIds.includes(AlertConsumers.SIEM); - const hasNoSecuritySolution = !isOnlySecurity && !hasSecurityAndO11yFeatureIds; + const hasNoSecuritySolution = + featureIds.length > 0 && !isOnlySecurity && !hasSecurityAndO11yFeatureIds; const queryIndexNameFn = () => { return fetchAlertIndexNames({ http, features }); diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_load_rule_types_query.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_load_rule_types_query.ts index d33f0b2d31fa1..207a3a6d6e1a7 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_load_rule_types_query.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_load_rule_types_query.ts @@ -1,22 +1,23 @@ /* * 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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ + import { i18n } from '@kbn/i18n'; import { useQuery } from '@tanstack/react-query'; import type { RuleType, RuleTypeIndex } from '@kbn/triggers-actions-ui-types'; import type { ToastsStart, HttpStart } from '@kbn/core/public'; - import { ALERTS_FEATURE_ID } from '../constants'; import { fetchRuleTypes } from '../apis/fetch_rule_types'; -interface UseLoadRuleTypesQueryProps { +export interface UseLoadRuleTypesQueryProps { filteredRuleTypes: string[]; enabled?: boolean; - http: HttpStart, - toasts: ToastsStart, + http: HttpStart; + toasts: ToastsStart; } const getFilteredIndex = (data: Array>, filteredRuleTypes: string[]) => { @@ -36,12 +37,7 @@ const getFilteredIndex = (data: Array>, filteredRuleTyp }; export const useLoadRuleTypesQuery = (props: UseLoadRuleTypesQueryProps) => { - const { - filteredRuleTypes, - enabled = true, - http, - toasts, - } = props; + const { filteredRuleTypes, enabled = true, http, toasts } = props; const queryFn = () => { return fetchRuleTypes({ http }); diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_rule_aad_fields.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_rule_aad_fields.ts index 0faaef86a4ec0..68b0f477db591 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_rule_aad_fields.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/hooks/use_rule_aad_fields.ts @@ -1,8 +1,9 @@ /* * 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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import { useMemo } from 'react'; @@ -25,11 +26,7 @@ export interface UseRuleAADFieldsResult { } export function useRuleAADFields(props: UseRuleAADFieldsProps): UseRuleAADFieldsResult { - const { - ruleTypeId, - http, - toasts, - } = props; + const { ruleTypeId, http, toasts } = props; const queryAadFieldsFn = () => { return fetchAadFields({ http, ruleTypeId }); diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx index 6e6f7f216f329..4e42c33e7c153 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx @@ -1,18 +1,19 @@ /* * 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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import { useCallback, useState } from 'react'; -import { Query, TimeRange } from '@kbn/es-query'; -import { SuggestionsAbstraction } from '@kbn/unified-search-plugin/public/typeahead/suggestions_component'; +import type { Query, TimeRange } from '@kbn/es-query'; +import type { SuggestionsAbstraction } from '@kbn/unified-search-plugin/public/typeahead/suggestions_component'; import { AlertConsumers } from '@kbn/rule-data-utils'; 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 type { AlertsSearchBarProps, QueryLanguageType } from './types'; +import { useAlertDataView } from './hooks/use_alert_data_view'; import { useRuleAADFields } from './hooks/use_rule_aad_fields'; import { useLoadRuleTypesQuery } from './hooks/use_load_rule_types_query'; @@ -48,7 +49,7 @@ export const AlertsSearchBar = ({ dataViewsService, }); const { aadFields, loading: fieldsLoading } = useRuleAADFields({ - ruleTypeId: ruleTypeId, + ruleTypeId, http, toasts, }); @@ -97,29 +98,29 @@ export const AlertsSearchBar = ({ }; return unifiedSearchBar({ - appName: appName, - disableQueryLanguageSwitcher: disableQueryLanguageSwitcher, + appName, + disableQueryLanguageSwitcher, // @ts-expect-error - DataView fields prop and SearchBar indexPatterns props are overly broad indexPatterns: loading || fieldsLoading ? NO_INDEX_PATTERNS : indexPatterns, - placeholder: placeholder, + placeholder, query: { query: query ?? '', language: queryLanguage }, - filters: filters, + filters, dateRangeFrom: rangeFrom, dateRangeTo: rangeTo, - displayStyle: "inPage", - showFilterBar: showFilterBar, + displayStyle: 'inPage', + showFilterBar, onQuerySubmit: onSearchQuerySubmit, - onFiltersUpdated: onFiltersUpdated, - onRefresh: onRefresh, - showDatePicker: showDatePicker, + onFiltersUpdated, + onRefresh, + showDatePicker, showQueryInput: true, - saveQueryMenuVisibility: "allowed_by_app_privilege", - showSubmitButton: showSubmitButton, - submitOnBlur: submitOnBlur, + saveQueryMenuVisibility: 'allowed_by_app_privilege', + showSubmitButton, + submitOnBlur, onQueryChange: onSearchQueryChange, suggestionsAbstraction: isSecurity ? undefined : SA_ALERTS, }); -} +}; // eslint-disable-next-line import/no-default-export export { AlertsSearchBar as default }; diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/translations.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/translations.ts index a5f4fc4b5754e..5c5bc4c16213c 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/translations.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/translations.ts @@ -1,8 +1,9 @@ /* * 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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import { i18n } from '@kbn/i18n'; diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts index 4bbe52881a460..06ed60f1eab92 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts @@ -1,8 +1,9 @@ /* * 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. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import type { Filter } from '@kbn/es-query'; diff --git a/x-pack/plugins/alerting/common/builtin_action_groups.ts b/x-pack/plugins/alerting/common/builtin_action_groups.ts index 5120b388de3a5..65e01bae891a6 100644 --- a/x-pack/plugins/alerting/common/builtin_action_groups.ts +++ b/x-pack/plugins/alerting/common/builtin_action_groups.ts @@ -5,10 +5,7 @@ * 2.0. */ -import type { - RecoveredActionGroupId, - ActionGroup -} from '@kbn/alerting-types'; +import type { RecoveredActionGroupId, ActionGroup } from '@kbn/alerting-types'; import { RecoveredActionGroup } from '@kbn/alerting-types'; export type ReservedActionGroups = @@ -26,9 +23,6 @@ export function getBuiltinActionGroups( return [customRecoveryGroup ?? RecoveredActionGroup]; } -export type { - RecoveredActionGroupId, - DefaultActionGroupId, -} from '@kbn/alerting-types'; +export type { RecoveredActionGroupId, DefaultActionGroupId } from '@kbn/alerting-types'; export { RecoveredActionGroup } from '@kbn/alerting-types'; diff --git a/x-pack/plugins/alerting/common/index.ts b/x-pack/plugins/alerting/common/index.ts index d46bf35b1080b..c2cc65bf6fae2 100644 --- a/x-pack/plugins/alerting/common/index.ts +++ b/x-pack/plugins/alerting/common/index.ts @@ -38,6 +38,7 @@ export * from './rule_tags_aggregation'; export * from './iso_weekdays'; export * from './saved_objects/rules/mappings'; export * from './rule_circuit_breaker_error_message'; +export * from './maintenance_window_scoped_query_error_message'; export type { MaintenanceWindowModificationMetadata, diff --git a/x-pack/plugins/alerting/common/maintenance_window_scoped_query_error_message.ts b/x-pack/plugins/alerting/common/maintenance_window_scoped_query_error_message.ts new file mode 100644 index 0000000000000..6ccdf2ede6256 --- /dev/null +++ b/x-pack/plugins/alerting/common/maintenance_window_scoped_query_error_message.ts @@ -0,0 +1,16 @@ +/* + * 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 errorMessageIdentifier = 'invalid scoped query'; + +export const getScopedQueryErrorMessage = (errorMessage: string) => { + return `${errorMessageIdentifier} - ${errorMessage}`; +}; + +export const isScopedQueryError = (errorMessage: string) => { + return errorMessage.includes(errorMessageIdentifier); +}; diff --git a/x-pack/plugins/alerting/common/rule_type.ts b/x-pack/plugins/alerting/common/rule_type.ts index 7538851f23bc7..b683ed8e71938 100644 --- a/x-pack/plugins/alerting/common/rule_type.ts +++ b/x-pack/plugins/alerting/common/rule_type.ts @@ -5,8 +5,4 @@ * 2.0. */ -export type { - RuleType, - ActionGroup, - ActionGroupIdsOf, -} from '@kbn/alerting-types'; +export type { RuleType, ActionGroup, ActionGroupIdsOf } from '@kbn/alerting-types'; diff --git a/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts index e710595bc6180..ef76e8f0f5ee4 100644 --- a/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts +++ b/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts @@ -7,12 +7,20 @@ import { i18n } from '@kbn/i18n'; import { useMutation } from '@tanstack/react-query'; +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import type { KibanaServerError } from '@kbn/kibana-utils-plugin/public'; import { useKibana } from '../utils/kibana_react'; import { MaintenanceWindow } from '../pages/maintenance_windows/types'; import { createMaintenanceWindow } from '../services/maintenance_windows_api/create'; -export function useCreateMaintenanceWindow() { +interface UseCreateMaintenanceWindowProps { + onError?: (error: IHttpFetchError) => void; +} + +export function useCreateMaintenanceWindow(props?: UseCreateMaintenanceWindowProps) { + const { onError } = props || {}; + const { http, notifications: { toasts }, @@ -33,12 +41,13 @@ export function useCreateMaintenanceWindow() { }) ); }, - onError: () => { + onError: (error: IHttpFetchError) => { toasts.addDanger( i18n.translate('xpack.alerting.maintenanceWindowsCreateFailure', { defaultMessage: 'Failed to create maintenance window.', }) ); + onError?.(error); }, }); } diff --git a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts index c7dd73724b6df..14e67fa644385 100644 --- a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts +++ b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts @@ -7,12 +7,20 @@ import { i18n } from '@kbn/i18n'; import { useMutation } from '@tanstack/react-query'; +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import type { KibanaServerError } from '@kbn/kibana-utils-plugin/public'; import { useKibana } from '../utils/kibana_react'; import { MaintenanceWindow } from '../pages/maintenance_windows/types'; import { updateMaintenanceWindow } from '../services/maintenance_windows_api/update'; -export function useUpdateMaintenanceWindow() { +interface UseUpdateMaintenanceWindowProps { + onError?: (error: IHttpFetchError) => void; +} + +export function useUpdateMaintenanceWindow(props?: UseUpdateMaintenanceWindowProps) { + const { onError } = props || {}; + const { http, notifications: { toasts }, @@ -39,12 +47,13 @@ export function useUpdateMaintenanceWindow() { }) ); }, - onError: () => { + onError: (error: IHttpFetchError) => { toasts.addDanger( i18n.translate('xpack.alerting.maintenanceWindowsUpdateFailure', { defaultMessage: 'Failed to update maintenance window.', }) ); + onError?.(error); }, }); } diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx index c5fb3bdf8357a..3fea6d6611f08 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx @@ -45,6 +45,11 @@ describe('CreateMaintenanceWindowForm', () => { addDanger: jest.fn(), }, }, + unifiedSearch: { + ui: { + SearchBar:
, + }, + }, }, }); @@ -136,13 +141,13 @@ describe('CreateMaintenanceWindowForm', () => { const observabilityInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-observability'); + ).getByTestId('option-observability'); const securityInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-securitySolution'); + ).getByTestId('option-securitySolution'); const managementInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-management'); + ).getByTestId('option-management'); expect(observabilityInput).toBeChecked(); expect(securityInput).toBeChecked(); @@ -176,13 +181,13 @@ describe('CreateMaintenanceWindowForm', () => { const observabilityInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-observability'); + ).getByTestId('option-observability'); const securityInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-securitySolution'); + ).getByTestId('option-securitySolution'); const managementInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-management'); + ).getByTestId('option-management'); expect(observabilityInput).toBeChecked(); expect(securityInput).toBeChecked(); @@ -213,13 +218,13 @@ describe('CreateMaintenanceWindowForm', () => { const observabilityInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-observability'); + ).getByTestId('option-observability'); const securityInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-securitySolution'); + ).getByTestId('option-securitySolution'); const managementInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-management'); + ).getByTestId('option-management'); expect(observabilityInput).toBeChecked(); expect(managementInput).toBeChecked(); @@ -237,13 +242,13 @@ describe('CreateMaintenanceWindowForm', () => { const observabilityInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-observability'); + ).getByTestId('option-observability'); const securityInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-securitySolution'); + ).getByTestId('option-securitySolution'); const managementInput = within( result.getByTestId('maintenanceWindowCategorySelection') - ).getByTestId('checkbox-management'); + ).getByTestId('option-management'); expect(observabilityInput).toBeChecked(); expect(securityInput).toBeChecked(); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx index 8ae889fe110a5..0996182f74d25 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx @@ -30,13 +30,16 @@ import { } from '@elastic/eui'; import { TIMEZONE_OPTIONS as UI_TIMEZONE_OPTIONS } from '@kbn/core-ui-settings-common'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common'; -import { ValidFeatureId } from '@kbn/rule-data-utils'; -import { Filter } from '@kbn/es-query'; +import type { ValidFeatureId } from '@kbn/rule-data-utils'; +import type { Filter } from '@kbn/es-query'; +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import type { KibanaServerError } from '@kbn/kibana-utils-plugin/public'; import { FormProps, schema } from './schema'; import * as i18n from '../translations'; import { RecurringSchedule } from './recurring_schedule_form/recurring_schedule'; import { SubmitButton } from './submit_button'; import { convertToRRule } from '../helpers/convert_to_rrule'; +import { isScopedQueryError } from '../../../../common'; import { useCreateMaintenanceWindow } from '../../../hooks/use_create_maintenance_window'; import { useUpdateMaintenanceWindow } from '../../../hooks/use_update_maintenance_window'; import { useGetRuleTypes } from '../../../hooks/use_get_rule_types'; @@ -77,15 +80,33 @@ export const CreateMaintenanceWindowForm = React.memo( (initialValue?.scopedQuery?.filters as Filter[]) || [] ); + const [isQueryInvalid, setIsQueryInvalid] = useState(false); + const hasSetInitialCategories = useRef(false); const isEditMode = initialValue !== undefined && maintenanceWindowId !== undefined; - const hasSetInitialCategories = useRef(false); + const onCreateOrUpdateError = useCallback( + (error: IHttpFetchError) => { + if (!error.body?.message) { + return; + } + if (isScopedQueryError(error.body.message)) { + setIsQueryInvalid(true); + } + }, + [setIsQueryInvalid] + ); const { mutate: createMaintenanceWindow, isLoading: isCreateLoading } = - useCreateMaintenanceWindow(); + useCreateMaintenanceWindow({ + onError: onCreateOrUpdateError, + }); + const { mutate: updateMaintenanceWindow, isLoading: isUpdateLoading } = - useUpdateMaintenanceWindow(); + useUpdateMaintenanceWindow({ + onError: onCreateOrUpdateError, + }); + const { mutate: archiveMaintenanceWindow } = useArchiveMaintenanceWindow(); const { data: ruleTypes, isLoading: isLoadingRuleTypes } = useGetRuleTypes(); @@ -105,29 +126,33 @@ export const CreateMaintenanceWindowForm = React.memo { - if (isValid) { - const startDate = moment(formData.startDate); - const endDate = moment(formData.endDate); - const maintenanceWindow = { - title: formData.title, - duration: endDate.diff(startDate), - rRule: convertToRRule( - startDate, - formData.timezone ? formData.timezone[0] : defaultTimezone, - formData.recurringSchedule - ), - categoryIds: formData.categoryIds, - scopedQuery: scopedQueryPayload, - }; - if (isEditMode) { - updateMaintenanceWindow({ maintenanceWindowId, maintenanceWindow }, { onSuccess }); - } else { - createMaintenanceWindow(maintenanceWindow, { onSuccess }); - } + if (!isValid || isQueryInvalid) { + return; + } + + const startDate = moment(formData.startDate); + const endDate = moment(formData.endDate); + const maintenanceWindow = { + title: formData.title, + duration: endDate.diff(startDate), + rRule: convertToRRule( + startDate, + formData.timezone ? formData.timezone[0] : defaultTimezone, + formData.recurringSchedule + ), + categoryIds: formData.categoryIds, + scopedQuery: scopedQueryPayload, + }; + + if (isEditMode) { + updateMaintenanceWindow({ maintenanceWindowId, maintenanceWindow }, { onSuccess }); + } else { + createMaintenanceWindow(maintenanceWindow, { onSuccess }); } }, [ isEditMode, + isQueryInvalid, maintenanceWindowId, updateMaintenanceWindow, createMaintenanceWindow, @@ -179,30 +204,16 @@ export const CreateMaintenanceWindowForm = React.memo { + const onCategoryIdsChange = useCallback( + (ids: string[]) => { if (!categoryIds) { return; } - if (categoryIds.includes(id)) { - setFieldValue( - 'categoryIds', - categoryIds.filter((category) => category !== id) - ); - return; - } - setFieldValue('categoryIds', [...categoryIds, id]); + setFieldValue('categoryIds', ids); }, [categoryIds, setFieldValue] ); - const onCategoryIdsRadioClick = useCallback( - (id: string) => { - setFieldValue('categoryIds', [id]); - }, - [setFieldValue] - ); - const onScopeQueryToggle = useCallback( (isEnabled: boolean) => { if (isEnabled) { @@ -213,6 +224,16 @@ export const CreateMaintenanceWindowForm = React.memo { + if (isQueryInvalid) { + setIsQueryInvalid(false); + } + setQuery(newQuery); + }, + [isQueryInvalid] + ); + const modal = useMemo(() => { let m; if (isModalVisible) { @@ -390,7 +411,7 @@ export const CreateMaintenanceWindowForm = React.memo {() => ( )} @@ -406,8 +427,7 @@ export const CreateMaintenanceWindowForm = React.memo error.message)} - onCheckboxChange={onCategoryIdsCheckboxClick} - onRadioChange={onCategoryIdsRadioClick} + onChange={onCategoryIdsChange} /> )} @@ -420,7 +440,8 @@ export const CreateMaintenanceWindowForm = React.memo )} diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_category_selection.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_category_selection.test.tsx index b06eded5efa38..d99f75bd3ed64 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_category_selection.test.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_category_selection.test.tsx @@ -29,9 +29,9 @@ describe('maintenanceWindowCategorySelection', () => { /> ); - expect(screen.getByTestId('checkbox-observability')).not.toBeDisabled(); - expect(screen.getByTestId('checkbox-securitySolution')).not.toBeDisabled(); - expect(screen.getByTestId('checkbox-management')).not.toBeDisabled(); + expect(screen.getByTestId('option-observability')).not.toBeDisabled(); + expect(screen.getByTestId('option-securitySolution')).not.toBeDisabled(); + expect(screen.getByTestId('option-management')).not.toBeDisabled(); }); it('should disable options if option is not in the available categories array', () => { @@ -43,9 +43,9 @@ describe('maintenanceWindowCategorySelection', () => { /> ); - expect(screen.getByTestId('checkbox-observability')).toBeDisabled(); - expect(screen.getByTestId('checkbox-securitySolution')).toBeDisabled(); - expect(screen.getByTestId('checkbox-management')).toBeDisabled(); + expect(screen.getByTestId('option-observability')).toBeDisabled(); + expect(screen.getByTestId('option-securitySolution')).toBeDisabled(); + expect(screen.getByTestId('option-management')).toBeDisabled(); }); it('can initialize checkboxes with initial values from props', async () => { @@ -57,9 +57,9 @@ describe('maintenanceWindowCategorySelection', () => { /> ); - expect(screen.getByTestId('checkbox-observability')).not.toBeChecked(); - expect(screen.getByTestId('checkbox-securitySolution')).toBeChecked(); - expect(screen.getByTestId('checkbox-management')).toBeChecked(); + expect(screen.getByTestId('option-observability')).not.toBeChecked(); + expect(screen.getByTestId('option-securitySolution')).toBeChecked(); + expect(screen.getByTestId('option-management')).toBeChecked(); }); it('can check checkboxes', async () => { @@ -71,16 +71,16 @@ describe('maintenanceWindowCategorySelection', () => { /> ); - const managementCheckbox = screen.getByTestId('checkbox-management'); - const securityCheckbox = screen.getByTestId('checkbox-securitySolution'); + const managementCheckbox = screen.getByTestId('option-management'); + const securityCheckbox = screen.getByTestId('option-securitySolution'); fireEvent.click(managementCheckbox); - expect(mockOnChange).toHaveBeenLastCalledWith('management', expect.anything()); + expect(mockOnChange).toHaveBeenLastCalledWith(['observability', 'management']); fireEvent.click(securityCheckbox); - expect(mockOnChange).toHaveBeenLastCalledWith('securitySolution', expect.anything()); + expect(mockOnChange).toHaveBeenLastCalledWith(['observability', 'securitySolution']); }); it('should display loading spinner if isLoading is true', () => { @@ -106,4 +106,62 @@ describe('maintenanceWindowCategorySelection', () => { ); expect(screen.getByText('test error')).toBeInTheDocument(); }); + + it('should display radio group if scoped query is enabled', () => { + appMockRenderer.render( + + ); + + expect( + screen.getByTestId('maintenanceWindowCategorySelectionCheckboxGroup') + ).toBeInTheDocument(); + + appMockRenderer.render( + + ); + + expect(screen.getByTestId('maintenanceWindowCategorySelectionRadioGroup')).toBeInTheDocument(); + }); + + it('should set only 1 category at a time if scoped query is enabled', () => { + appMockRenderer.render( + + ); + + let managementCheckbox = screen.getByLabelText('Stack rules'); + + fireEvent.click(managementCheckbox); + + expect(mockOnChange).toHaveBeenLastCalledWith(['management']); + + appMockRenderer.render( + + ); + + managementCheckbox = screen.getByLabelText('Stack rules'); + + fireEvent.click(managementCheckbox); + + expect(mockOnChange).toHaveBeenLastCalledWith(['management']); + }); }); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_category_selection.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_category_selection.tsx index b9a3cd4268ef7..787941907c142 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_category_selection.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_category_selection.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import React, { useMemo, useCallback } from 'react'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common'; import { EuiFlexGroup, @@ -43,9 +43,8 @@ export interface MaintenanceWindowCategorySelectionProps { availableCategories: string[]; errors?: string[]; isLoading?: boolean; - isScopedQueryEnabled: boolean; - onCheckboxChange: (category: string) => void; - onRadioChange: (category: string) => void; + isScopedQueryEnabled?: boolean; + onChange: (categories: string[]) => void; } export const MaintenanceWindowCategorySelection = ( @@ -57,8 +56,7 @@ export const MaintenanceWindowCategorySelection = ( errors = [], isLoading = false, isScopedQueryEnabled = false, - onCheckboxChange, - onRadioChange, + onChange, } = props; const selectedMap = useMemo(() => { @@ -75,10 +73,29 @@ export const MaintenanceWindowCategorySelection = ( })); }, [availableCategories]); + const onCheckboxChange = useCallback( + (id: string) => { + if (selectedCategories.includes(id)) { + onChange(selectedCategories.filter((category) => category !== id)); + } else { + onChange([...selectedCategories, id]); + } + }, + [selectedCategories, onChange] + ); + + const onRadioChange = useCallback( + (id: string) => { + onChange([id]); + }, + [onChange] + ); + const categorySelection = useMemo(() => { if (isScopedQueryEnabled) { return ( ({ + AlertsSearchBar: () =>
, +})); + +const { useKibana } = jest.requireMock('../../../utils/kibana_react'); + +describe('MaintenanceWindowScopedQuery', () => { + let appMockRenderer: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + useKibana.mockReturnValue({ + services: { + notifications: { + toasts: { + addSuccess: jest.fn(), + addDanger: jest.fn(), + }, + }, + data: { + dataViews: {}, + }, + unifiedSearch: { + ui: { + SearchBar:
, + }, + }, + }, + }); + appMockRenderer = createAppMockRenderer(); + }); + + it('renders correctly', () => { + appMockRenderer.render( + + ); + expect(screen.getByTestId('maintenanceWindowScopeQuery')).toBeInTheDocument(); + }); + + it('should hide the search bar if isEnabled is false', () => { + appMockRenderer.render( + + ); + expect(screen.queryByTestId('maintenanceWindowScopeQuery')).not.toBeInTheDocument(); + }); + + it('should render loading if isLoading is true', () => { + appMockRenderer.render( + + ); + expect(screen.getByTestId('maintenanceWindowScopedQueryLoading')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query.tsx index 5cdc6675cc577..79f9d16a8f56f 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query.tsx @@ -6,10 +6,11 @@ */ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiLoadingSpinner } from '@elastic/eui'; import { AlertConsumers } from '@kbn/rule-data-utils'; import type { Filter } from '@kbn/es-query'; import { AlertsSearchBar } from '@kbn/alerts-ui-shared'; +import * as translations from '../translations'; import { PLUGIN } from '../../../../common/constants/plugin'; import { useKibana } from '../../../utils/kibana_react'; @@ -17,6 +18,7 @@ export interface MaintenanceWindowScopedQueryProps { featureIds: AlertConsumers[]; query: string; filters: Filter[]; + isInvalid?: boolean; isLoading?: boolean; isEnabled?: boolean; onQueryChange: (query: string) => void; @@ -25,8 +27,16 @@ export interface MaintenanceWindowScopedQueryProps { export const MaintenanceWindowScopedQuery = React.memo( (props: MaintenanceWindowScopedQueryProps) => { - const { featureIds, query, filters, isLoading, isEnabled, onQueryChange, onFiltersChange } = - props; + const { + featureIds, + query, + filters, + isInvalid = false, + isLoading, + isEnabled = true, + onQueryChange, + onFiltersChange, + } = props; const { http, @@ -64,24 +74,30 @@ export const MaintenanceWindowScopedQuery = React.memo( return ( - + + + ); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query_switch.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query_switch.test.tsx new file mode 100644 index 0000000000000..0c0c3b6259c7f --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query_switch.test.tsx @@ -0,0 +1,30 @@ +/* + * 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 { screen, fireEvent, render } from '@testing-library/react'; +import { MaintenanceWindowScopedQuerySwitch } from './maintenance_window_scoped_query_switch'; + +const mockOnEnabledChange = jest.fn(); + +describe('MaintenanceWindowScopedQuerySwitch', () => { + it('renders correctly', () => { + render( + + ); + expect(screen.getByTestId('maintenanceWindowScopedQuerySwitch')).toBeInTheDocument(); + }); + + it('should call onChange when switch is clicked', () => { + render( + + ); + + fireEvent.click(screen.getByRole('switch')); + expect(mockOnEnabledChange).toHaveBeenCalledWith(false); + }); +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query_switch.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query_switch.tsx index b9db359fd2239..db6ebf0282669 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query_switch.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query_switch.tsx @@ -18,14 +18,14 @@ import { import * as i18n from '../translations'; interface MaintenanceWindowScopedQuerySwitchProps { - isEnabled: boolean; - onEnabledChange: (isEnabled: boolean) => void; + checked: boolean; + onEnabledChange: (checked: boolean) => void; } export const MaintenanceWindowScopedQuerySwitch = ( props: MaintenanceWindowScopedQuerySwitchProps ) => { - const { isEnabled, onEnabledChange } = props; + const { checked, onEnabledChange } = props; const onEnabledChangeInternal = useCallback( (event: EuiSwitchEvent) => { @@ -35,7 +35,7 @@ export const MaintenanceWindowScopedQuerySwitch = ( ); return ( - +

{i18n.CREATE_FORM_SCOPED_QUERY_TITLE}

@@ -47,7 +47,7 @@ export const MaintenanceWindowScopedQuerySwitch = ( diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts index e85c2cae91b0d..edf21984883ba 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts @@ -230,6 +230,13 @@ export const CREATE_FORM_SCOPED_QUERY_TOGGLE_TITLE = i18n.translate( } ); +export const CREATE_FORM_SCOPED_QUERY_ERROR_MESSAGE = i18n.translate( + 'xpack.alerting.maintenanceWindows.createForm.scopedQuery.errorMessage', + { + defaultMessage: 'Invalid scoped query.', + } +); + export const CREATE_FORM_FREQUENCY_WEEKLY_ON = (dayOfWeek: string) => i18n.translate('xpack.alerting.maintenanceWindows.createForm.frequency.weeklyOnWeekday', { defaultMessage: 'Weekly on {dayOfWeek}', diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts index af04b62bfc7d9..ca98ba2ea72d1 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts @@ -151,7 +151,7 @@ describe('MaintenanceWindowClient - create', () => { title: mockMaintenanceWindow.title, duration: mockMaintenanceWindow.duration, rRule: mockMaintenanceWindow.rRule as CreateMaintenanceWindowParams['data']['rRule'], - categoryIds: ['observability', 'securitySolution'], + categoryIds: ['securitySolution'], scopedQuery: { kql: "_id: '1234'", filters: [ @@ -189,7 +189,7 @@ describe('MaintenanceWindowClient - create', () => { rRule: mockMaintenanceWindow.rRule, enabled: true, expirationDate: moment(new Date()).tz('UTC').add(1, 'year').toISOString(), - categoryIds: ['observability', 'securitySolution'], + categoryIds: ['securitySolution'], ...updatedMetadata, }), { @@ -245,12 +245,59 @@ describe('MaintenanceWindowClient - create', () => { }, }); }).rejects.toThrowErrorMatchingInlineSnapshot(` - "Error validating create maintenance scoped query - Expected \\"(\\", \\"{\\", value, whitespace but end of input found. + "Error validating create maintenance window data - invalid scoped query - Expected \\"(\\", \\"{\\", value, whitespace but end of input found. invalid: ---------^" `); }); + it('should throw if trying to create a MW with a scoped query with other than 1 category ID', async () => { + jest.useFakeTimers().setSystemTime(new Date('2023-02-26T00:00:00.000Z')); + + const mockMaintenanceWindow = getMockMaintenanceWindow({ + expirationDate: moment(new Date()).tz('UTC').add(1, 'year').toISOString(), + }); + + await expect(async () => { + await createMaintenanceWindow(mockContext, { + data: { + title: mockMaintenanceWindow.title, + duration: mockMaintenanceWindow.duration, + rRule: mockMaintenanceWindow.rRule as CreateMaintenanceWindowParams['data']['rRule'], + categoryIds: ['observability', 'securitySolution'], + scopedQuery: { + kql: "_id: '1234'", + filters: [ + { + meta: { + disabled: false, + negate: false, + alias: null, + key: 'kibana.alert.action_group', + field: 'kibana.alert.action_group', + params: { + query: 'test', + }, + type: 'phrase', + }, + $state: { + store: 'appState', + }, + query: { + match_phrase: { + 'kibana.alert.action_group': 'test', + }, + }, + }, + ], + }, + }, + }); + }).rejects.toThrowErrorMatchingInlineSnapshot( + `"Error validating create maintenance window data - scoped query must be accompanied by 1 category ID"` + ); + }); + it('should throw if trying to create a maintenance window with invalid category ids', async () => { jest.useFakeTimers().setSystemTime(new Date('2023-02-26T00:00:00.000Z')); diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.ts index 78f5e885874b0..e613a5dd5433a 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.ts @@ -11,6 +11,7 @@ import { SavedObjectsUtils } from '@kbn/core/server'; import { buildEsQuery, Filter } from '@kbn/es-query'; import { generateMaintenanceWindowEvents } from '../../lib/generate_maintenance_window_events'; import type { MaintenanceWindowClientContext } from '../../../../../common'; +import { getScopedQueryErrorMessage } from '../../../../../common'; import type { MaintenanceWindow } from '../../types'; import type { CreateMaintenanceWindowParams } from './types'; import { @@ -50,7 +51,19 @@ export async function createMaintenanceWindow( }; } } catch (error) { - throw Boom.badRequest(`Error validating create maintenance scoped query - ${error.message}`); + throw Boom.badRequest( + `Error validating create maintenance window data - ${getScopedQueryErrorMessage( + error.message + )}` + ); + } + + if (scopedQueryWithGeneratedValue) { + if (data.categoryIds?.length !== 1) { + throw Boom.badRequest( + `Error validating create maintenance window data - scoped query must be accompanied by 1 category ID` + ); + } } const id = SavedObjectsUtils.generateId(); diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts index 4caef4eca1ba1..08a01556bede8 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts @@ -223,6 +223,7 @@ describe('MaintenanceWindowClient - update', () => { } as MaintenanceWindow['rRule'], events: modifiedEvents, expirationDate: moment(new Date(firstTimestamp)).tz('UTC').add(2, 'week').toISOString(), + categoryIds: ['observability'], }); savedObjectsClient.get.mockResolvedValue({ @@ -367,12 +368,62 @@ describe('MaintenanceWindowClient - update', () => { }, }); }).rejects.toThrowErrorMatchingInlineSnapshot(` - "Error validating update maintenance scoped query - Expected \\"(\\", \\"{\\", value, whitespace but end of input found. + "Error validating update maintenance window data - invalid scoped query - Expected \\"(\\", \\"{\\", value, whitespace but end of input found. invalid: ---------^" `); }); + it('should throw if trying to update a MW with a scoped query with other than 1 category ID', async () => { + jest.useFakeTimers().setSystemTime(new Date(firstTimestamp)); + const mockMaintenanceWindow = getMockMaintenanceWindow({ + expirationDate: moment(new Date(firstTimestamp)).tz('UTC').subtract(1, 'year').toISOString(), + }); + + savedObjectsClient.get.mockResolvedValueOnce({ + attributes: mockMaintenanceWindow, + version: '123', + id: 'test-id', + categoryIds: ['observability', 'securitySolution'], + } as unknown as SavedObject); + + await expect(async () => { + await updateMaintenanceWindow(mockContext, { + id: 'test-id', + data: { + scopedQuery: { + kql: "_id: '1234'", + filters: [ + { + meta: { + disabled: false, + negate: false, + alias: null, + key: 'kibana.alert.action_group', + field: 'kibana.alert.action_group', + params: { + query: 'test', + }, + type: 'phrase', + }, + $state: { + store: 'appState', + }, + query: { + match_phrase: { + 'kibana.alert.action_group': 'test', + }, + }, + }, + ], + }, + }, + }); + }).rejects.toThrowErrorMatchingInlineSnapshot( + `"Failed to update maintenance window by id: test-id, Error: Error: Cannot edit archived maintenance windows: Cannot edit archived maintenance windows"` + ); + }); + it('should throw if updating a maintenance window that has expired', async () => { jest.useFakeTimers().setSystemTime(new Date(firstTimestamp)); const mockMaintenanceWindow = getMockMaintenanceWindow({ diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.ts index 6b7ab69cc8070..29531c69a0811 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.ts @@ -9,6 +9,7 @@ import moment from 'moment'; import Boom from '@hapi/boom'; import { buildEsQuery, Filter } from '@kbn/es-query'; import type { MaintenanceWindowClientContext } from '../../../../../common'; +import { getScopedQueryErrorMessage } from '../../../../../common'; import type { MaintenanceWindow } from '../../types'; import { generateMaintenanceWindowEvents, @@ -70,7 +71,11 @@ async function updateWithOCC( }; } } catch (error) { - throw Boom.badRequest(`Error validating update maintenance scoped query - ${error.message}`); + throw Boom.badRequest( + `Error validating update maintenance window data - ${getScopedQueryErrorMessage( + error.message + )}` + ); } try { @@ -119,6 +124,14 @@ async function updateWithOCC( updatedAt: modificationMetadata.updatedAt, }); + if (updateMaintenanceWindowAttributes.scopedQuery) { + if (updateMaintenanceWindowAttributes.categoryIds?.length !== 1) { + throw Boom.badRequest( + `Error validating update maintenance window data - scoped query must be accompanied by 1 category ID` + ); + } + } + // We are deleting and then creating rather than updating because SO.update // performs a partial update on the rRule, we would need to null out all of the fields // that are removed from a new rRule if that were the case. diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.ts index 7b72e5898d56d..0e423b5ade804 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.ts @@ -5,158 +5,22 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; -import { DataView } from '@kbn/data-views-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { AlertConsumers, ValidFeatureId } from '@kbn/rule-data-utils'; -import { useEffect, useMemo, useState } from 'react'; -import { useQuery } from '@tanstack/react-query'; +import { ValidFeatureId } from '@kbn/rule-data-utils'; +import { useAlertDataView as useAlertDataViewShared } from '@kbn/alerts-ui-shared'; import { TriggersAndActionsUiServices } from '../..'; -import { fetchAlertIndexNames } from '../lib/rule_api/alert_index'; -import { fetchAlertFields } from '../lib/rule_api/alert_fields'; -export interface UserAlertDataView { - dataviews?: DataView[]; - loading: boolean; -} - -export function useAlertDataView(featureIds: ValidFeatureId[]): UserAlertDataView { +export function useAlertDataView(featureIds: ValidFeatureId[]) { const { http, data: dataService, notifications: { toasts }, } = useKibana().services; - const [dataviews, setDataviews] = useState(undefined); - const features = featureIds.sort().join(','); - const isOnlySecurity = featureIds.length === 1 && featureIds.includes(AlertConsumers.SIEM); - - const hasSecurityAndO11yFeatureIds = - featureIds.length > 1 && featureIds.includes(AlertConsumers.SIEM); - - const hasNoSecuritySolution = - featureIds.length > 0 && !isOnlySecurity && !hasSecurityAndO11yFeatureIds; - - const queryIndexNameFn = () => { - return fetchAlertIndexNames({ http, features }); - }; - const queryAlertFieldsFn = () => { - return fetchAlertFields({ http, featureIds }); - }; - - const onErrorFn = () => { - toasts.addDanger( - i18n.translate('xpack.triggersActionsUI.useAlertDataView.useAlertDataMessage', { - defaultMessage: 'Unable to load alert data view', - }) - ); - }; - - const { - data: indexNames, - isSuccess: isIndexNameSuccess, - isInitialLoading: isIndexNameInitialLoading, - isLoading: isIndexNameLoading, - } = useQuery({ - queryKey: ['loadAlertIndexNames', features], - queryFn: queryIndexNameFn, - onError: onErrorFn, - refetchOnWindowFocus: false, - enabled: featureIds.length > 0 && !hasSecurityAndO11yFeatureIds, - }); - - const { - data: alertFields, - isSuccess: isAlertFieldsSuccess, - isInitialLoading: isAlertFieldsInitialLoading, - isLoading: isAlertFieldsLoading, - } = useQuery({ - queryKey: ['loadAlertFields', features], - queryFn: queryAlertFieldsFn, - onError: onErrorFn, - refetchOnWindowFocus: false, - enabled: hasNoSecuritySolution, + return useAlertDataViewShared({ + featureIds, + dataViewsService: dataService.dataViews, + http, + toasts, }); - - useEffect(() => { - return () => { - dataviews?.map((dv) => { - dataService.dataViews.clearInstanceCache(dv.id); - }); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [dataviews]); - - // FUTURE ENGINEER this useEffect is for security solution user since - // we are using the user privilege to access the security alert index - useEffect(() => { - async function createDataView() { - const localDataview = await dataService.dataViews.create({ - title: (indexNames ?? []).join(','), - allowNoIndex: true, - }); - setDataviews([localDataview]); - } - - if (isOnlySecurity && isIndexNameSuccess) { - createDataView(); - } - }, [dataService.dataViews, indexNames, isIndexNameSuccess, isOnlySecurity]); - - // FUTURE ENGINEER this useEffect is for o11y and stack solution user since - // we are using the kibana user privilege to access the alert index - useEffect(() => { - if ( - indexNames && - alertFields && - !isOnlySecurity && - isAlertFieldsSuccess && - isIndexNameSuccess - ) { - setDataviews([ - { - title: (indexNames ?? []).join(','), - fieldFormatMap: {}, - fields: (alertFields ?? [])?.map((field) => { - return { - ...field, - ...(field.esTypes && field.esTypes.includes('flattened') ? { type: 'string' } : {}), - }; - }), - }, - ] as unknown as DataView[]); - } - }, [ - alertFields, - dataService.dataViews, - indexNames, - isIndexNameSuccess, - isOnlySecurity, - isAlertFieldsSuccess, - ]); - - return useMemo( - () => ({ - dataviews, - loading: - featureIds.length === 0 || hasSecurityAndO11yFeatureIds - ? false - : isOnlySecurity - ? isIndexNameInitialLoading || isIndexNameLoading - : isIndexNameInitialLoading || - isIndexNameLoading || - isAlertFieldsInitialLoading || - isAlertFieldsLoading, - }), - [ - dataviews, - featureIds.length, - hasSecurityAndO11yFeatureIds, - isOnlySecurity, - isIndexNameInitialLoading, - isIndexNameLoading, - isAlertFieldsInitialLoading, - isAlertFieldsLoading, - ] - ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_types_query.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_types_query.ts index ab11bb4f18452..5bfe0eb23db95 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_types_query.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_types_query.ts @@ -4,34 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { i18n } from '@kbn/i18n'; -import { useQuery } from '@tanstack/react-query'; -import { ALERTS_FEATURE_ID } from '@kbn/alerting-plugin/common'; -import { loadRuleTypes } from '../lib/rule_api/rule_types'; +import { useLoadRuleTypesQuery as useLoadRuleTypesQueryShared } from '@kbn/alerts-ui-shared'; import { useKibana } from '../../common/lib/kibana'; -import { RuleType, RuleTypeIndex } from '../../types'; interface UseLoadRuleTypesQueryProps { filteredRuleTypes: string[]; enabled?: boolean; } -const getFilteredIndex = (data: Array>, filteredRuleTypes: string[]) => { - const index: RuleTypeIndex = new Map(); - for (const ruleType of data) { - index.set(ruleType.id, ruleType); - } - let filteredIndex = index; - if (filteredRuleTypes?.length) { - filteredIndex = new Map( - [...index].filter(([k, v]) => { - return filteredRuleTypes.includes(v.id); - }) - ); - } - return filteredIndex; -}; - export const useLoadRuleTypesQuery = (props: UseLoadRuleTypesQueryProps) => { const { filteredRuleTypes, enabled = true } = props; const { @@ -39,47 +19,10 @@ export const useLoadRuleTypesQuery = (props: UseLoadRuleTypesQueryProps) => { notifications: { toasts }, } = useKibana().services; - const queryFn = () => { - return loadRuleTypes({ http }); - }; - - const onErrorFn = () => { - toasts.addDanger( - i18n.translate('xpack.triggersActionsUI.sections.rulesList.unableToLoadRuleTypesMessage', { - defaultMessage: 'Unable to load rule types', - }) - ); - }; - - const { data, isSuccess, isFetching, isInitialLoading, isLoading } = useQuery({ - queryKey: ['loadRuleTypes'], - queryFn, - onError: onErrorFn, - refetchOnWindowFocus: false, + return useLoadRuleTypesQueryShared({ + filteredRuleTypes, enabled, + http, + toasts, }); - - const filteredIndex = data ? getFilteredIndex(data, filteredRuleTypes) : new Map(); - - const hasAnyAuthorizedRuleType = filteredIndex.size > 0; - const authorizedRuleTypes = [...filteredIndex.values()]; - const authorizedToCreateAnyRules = authorizedRuleTypes.some( - (ruleType) => ruleType.authorizedConsumers[ALERTS_FEATURE_ID]?.all - ); - const authorizedToReadAnyRules = - authorizedToCreateAnyRules || - authorizedRuleTypes.some((ruleType) => ruleType.authorizedConsumers[ALERTS_FEATURE_ID]?.read); - - return { - ruleTypesState: { - initialLoad: isLoading || isInitialLoading, - isLoading: isLoading || isFetching, - data: filteredIndex, - }, - hasAnyAuthorizedRuleType, - authorizedRuleTypes, - authorizedToReadAnyRules, - authorizedToCreateAnyRules, - isSuccess, - }; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_rule_aad_fields.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_rule_aad_fields.ts index 1ad7106910113..5658a53a476f6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_rule_aad_fields.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_rule_aad_fields.ts @@ -7,30 +7,9 @@ import { DataViewField } from '@kbn/data-views-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common'; -import { HttpSetup } from '@kbn/core/public'; -import { useQuery } from '@tanstack/react-query'; -import { i18n } from '@kbn/i18n'; -import { useMemo } from 'react'; +import { useRuleAADFields as useRuleAADFieldsShared } from '@kbn/alerts-ui-shared'; import { TriggersAndActionsUiServices } from '../..'; -const EMPTY_AAD_FIELDS: DataViewField[] = []; - -async function fetchAadFields({ - http, - ruleTypeId, -}: { - http: HttpSetup; - ruleTypeId?: string; -}): Promise { - if (!ruleTypeId) return EMPTY_AAD_FIELDS; - const fields = await http.get(`${BASE_RAC_ALERTS_API_PATH}/aad_fields`, { - query: { ruleTypeId }, - }); - - return fields; -} - export function useRuleAADFields(ruleTypeId?: string): { aadFields: DataViewField[]; loading: boolean; @@ -40,35 +19,9 @@ export function useRuleAADFields(ruleTypeId?: string): { notifications: { toasts }, } = useKibana().services; - const queryAadFieldsFn = () => { - return fetchAadFields({ http, ruleTypeId }); - }; - - const onErrorFn = () => { - toasts.addDanger( - i18n.translate('xpack.triggersActionsUI.useRuleAADFields.errorMessage', { - defaultMessage: 'Unable to load alert fields per rule type', - }) - ); - }; - - const { - data: aadFields = EMPTY_AAD_FIELDS, - isInitialLoading, - isLoading, - } = useQuery({ - queryKey: ['loadAlertAadFieldsPerRuleType', ruleTypeId], - queryFn: queryAadFieldsFn, - onError: onErrorFn, - refetchOnWindowFocus: false, - enabled: ruleTypeId !== undefined, + return useRuleAADFieldsShared({ + ruleTypeId, + http, + toasts, }); - - return useMemo( - () => ({ - aadFields, - loading: ruleTypeId === undefined ? false : isInitialLoading || isLoading, - }), - [aadFields, isInitialLoading, isLoading, ruleTypeId] - ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_fields.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_fields.ts index 7be5b3eec0e69..d659a9483be84 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_fields.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_fields.ts @@ -5,23 +5,4 @@ * 2.0. */ -import { ValidFeatureId } from '@kbn/rule-data-utils'; -import { HttpSetup } from '@kbn/core/public'; -import { FieldSpec } from '@kbn/data-views-plugin/common'; -import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common'; - -export async function fetchAlertFields({ - http, - featureIds, -}: { - http: HttpSetup; - featureIds: ValidFeatureId[]; -}): Promise { - const { fields: alertFields = [] } = await http.get<{ fields: FieldSpec[] }>( - `${BASE_RAC_ALERTS_API_PATH}/browser_fields`, - { - query: { featureIds }, - } - ); - return alertFields; -} +export { fetchAlertFields } from '@kbn/alerts-ui-shared'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_index.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_index.ts index 8ac678664168b..de647f389f0a2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_index.ts @@ -5,21 +5,4 @@ * 2.0. */ -import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common'; -import { HttpSetup } from '@kbn/core/public'; - -export async function fetchAlertIndexNames({ - http, - features, -}: { - http: HttpSetup; - features: string; -}): Promise { - const { index_name: indexNamesStr = [] } = await http.get<{ index_name: string[] }>( - `${BASE_RAC_ALERTS_API_PATH}/index`, - { - query: { features }, - } - ); - return indexNamesStr; -} +export { fetchAlertIndexNames } from '@kbn/alerts-ui-shared'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx index 10e2a8493d711..0c87ecbb9ff14 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx @@ -5,114 +5,29 @@ * 2.0. */ -import React, { useCallback, useState } from 'react'; +import React from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { Query, TimeRange } from '@kbn/es-query'; -import { SuggestionsAbstraction } from '@kbn/unified-search-plugin/public/typeahead/suggestions_component'; -import { AlertConsumers } from '@kbn/rule-data-utils'; -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 { AlertsSearchBar as AlertsSearchBarShared } from '@kbn/alerts-ui-shared'; +import { AlertsSearchBarProps } from './types'; import { TriggersAndActionsUiServices } from '../../..'; -import { useRuleAADFields } from '../../hooks/use_rule_aad_fields'; -import { useLoadRuleTypesQuery } from '../../hooks/use_load_rule_types_query'; -const SA_ALERTS = { type: 'alerts', fields: {} } as SuggestionsAbstraction; - -// TODO Share buildEsQuery to be used between AlertsSearchBar and AlertsStateTable component https://github.com/elastic/kibana/issues/144615 -export function AlertsSearchBar({ - appName, - disableQueryLanguageSwitcher = false, - featureIds, - ruleTypeId, - query, - filters, - onQueryChange, - onQuerySubmit, - onFiltersUpdated, - rangeFrom, - rangeTo, - showFilterBar = false, - showDatePicker = true, - showSubmitButton = true, - placeholder = SEARCH_BAR_PLACEHOLDER, - submitOnBlur = false, -}: AlertsSearchBarProps) { +export function AlertsSearchBar(props: AlertsSearchBarProps) { const { + http, + notifications: { toasts }, + data: dataService, unifiedSearch: { ui: { SearchBar }, }, } = useKibana().services; - const [queryLanguage, setQueryLanguage] = useState('kuery'); - const { dataviews, loading } = useAlertDataView(featureIds ?? []); - const { aadFields, loading: fieldsLoading } = useRuleAADFields(ruleTypeId); - - const indexPatterns = - ruleTypeId && aadFields?.length ? [{ title: ruleTypeId, fields: aadFields }] : dataviews; - - const ruleType = useLoadRuleTypesQuery({ - filteredRuleTypes: ruleTypeId !== undefined ? [ruleTypeId] : [], - enabled: ruleTypeId !== undefined, - }); - - const isSecurity = - (featureIds && featureIds.length === 1 && featureIds.includes(AlertConsumers.SIEM)) || - (ruleType && - ruleTypeId && - ruleType.ruleTypesState.data.get(ruleTypeId)?.producer === AlertConsumers.SIEM); - - const onSearchQuerySubmit = useCallback( - ({ dateRange, query: nextQuery }: { dateRange: TimeRange; query?: Query }) => { - onQuerySubmit({ - dateRange, - query: typeof nextQuery?.query === 'string' ? nextQuery.query : undefined, - }); - setQueryLanguage((nextQuery?.language ?? 'kuery') as QueryLanguageType); - }, - [onQuerySubmit, setQueryLanguage] - ); - - const onSearchQueryChange = useCallback( - ({ dateRange, query: nextQuery }: { dateRange: TimeRange; query?: Query }) => { - onQueryChange?.({ - dateRange, - query: typeof nextQuery?.query === 'string' ? nextQuery.query : undefined, - }); - setQueryLanguage((nextQuery?.language ?? 'kuery') as QueryLanguageType); - }, - [onQueryChange, setQueryLanguage] - ); - const onRefresh = ({ dateRange }: { dateRange: TimeRange }) => { - onQuerySubmit({ - dateRange, - }); - }; - return ( - ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index de73a690beef3..aa255b9783c34 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -64,6 +64,7 @@ import { } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import React from 'react'; import { ActionsPublicPluginSetup } from '@kbn/actions-plugin/public'; +import type { RuleType, RuleTypeIndex } from '@kbn/triggers-actions-ui-types'; import { TypeRegistry } from './application/type_registry'; import type { ComponentOpts as RuleStatusDropdownProps } from './application/sections/rules_list/components/rule_status_dropdown'; import type { RuleTagFilterProps } from './application/sections/rules_list/components/rule_tag_filter'; @@ -94,13 +95,7 @@ import type { RulesListNotifyBadgePropsWithApi } from './application/sections/ru import { Case } from './application/sections/alerts_table/hooks/apis/bulk_get_cases'; import { AlertTableConfigRegistry } from './application/alert_table_config_registry'; -import type { RuleType, RuleTypeIndex } from '@kbn/triggers-actions-ui-types'; - -export type { - ActionVariables, - RuleType, - RuleTypeIndex, -} from '@kbn/triggers-actions-ui-types'; +export type { ActionVariables, RuleType, RuleTypeIndex } from '@kbn/triggers-actions-ui-types'; export { REQUIRED_ACTION_VARIABLES,