From 6757b95b1e00827e587ff94dcbce9d178bdddcb4 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Mon, 7 Dec 2020 16:44:40 -0800 Subject: [PATCH] [Alerting UI] Replaced AlertsContextProvider with KibanaContextProvider and exposed components in API (#84604) * [Alerting UI] Replaced AlertsContextProvider with KibanaContextProvider and exposed components in API * removed AlertContextProvider * exposed AlertAdd and EditAlert flyouts with triggers_actions_ui plugin start * fixed type check * fixed tests * fixed typechecks * fixed wrong consumer * fixed monitoring flyout flickering * fixed due to comments * fixed typechecks * fixed typechecks * fixed typechecks * fixed typechecks * fixed due to comments --- .../public/alert_types/always_firing.tsx | 3 +- .../public/components/create_alert.tsx | 47 ++---- .../public/application/application.test.tsx | 20 ++- .../plugins/apm/public/application/index.tsx | 48 +++--- .../alerting/AlertingFlyout/index.tsx | 40 +++-- x-pack/plugins/apm/public/plugin.ts | 10 +- .../common/components/alert_preview.tsx | 10 +- .../inventory/components/alert_flyout.tsx | 62 +++----- .../inventory/components/expression.test.tsx | 43 ++---- .../inventory/components/expression.tsx | 36 ++--- .../log_threshold/components/alert_flyout.tsx | 56 +++---- .../components/expression_editor/criteria.tsx | 8 +- .../criterion_preview_chart.tsx | 11 +- .../components/expression_editor/editor.tsx | 17 +-- .../hooks/use_chart_preview_data.tsx | 14 +- .../components/alert_flyout.tsx | 59 +++----- .../components/expression.test.tsx | 45 ++---- .../components/expression.tsx | 45 +++--- .../components/expression_chart.test.tsx | 74 ++++------ .../components/expression_chart.tsx | 12 +- .../hooks/use_metrics_explorer_chart_data.ts | 8 +- .../hooks/use_metric_explorer_state.ts | 1 - .../hooks/use_metrics_explorer_data.ts | 4 +- x-pack/plugins/monitoring/kibana.json | 2 +- .../public/alerts/alert_form.test.tsx | 33 ++--- .../monitoring/public/alerts/panel.tsx | 41 ++---- .../plugins/monitoring/public/legacy_shims.ts | 4 +- x-pack/plugins/monitoring/public/plugin.ts | 2 +- x-pack/plugins/monitoring/public/types.ts | 4 +- .../alert_types/geo_containment/index.ts | 4 +- ...inment_alert_type_expression.test.tsx.snap | 42 +++++- .../expressions/boundary_index_expression.tsx | 19 ++- .../expressions/entity_index_expression.tsx | 18 ++- ...containment_alert_type_expression.test.tsx | 23 +-- .../geo_containment/query_builder/index.tsx | 19 +-- .../public/alert_types/geo_threshold/index.ts | 4 +- ...eshold_alert_type_expression.test.tsx.snap | 42 +++++- .../expressions/boundary_index_expression.tsx | 19 ++- .../expressions/entity_index_expression.tsx | 15 +- ...o_threshold_alert_type_expression.test.tsx | 25 +--- .../geo_threshold/query_builder/index.tsx | 15 +- .../alert_types/threshold/expression.tsx | 20 ++- .../public/alert_types/threshold/index.ts | 4 +- .../alert_types/threshold/visualization.tsx | 24 +-- x-pack/plugins/triggers_actions_ui/README.md | 125 +++------------- .../components/health_check.test.tsx | 47 +++--- .../application/components/health_check.tsx | 14 +- .../application/context/alerts_context.tsx | 59 -------- .../public/application/home.tsx | 5 +- .../components/alert_details.tsx | 39 ++--- .../sections/alert_form/alert_add.test.tsx | 75 ++++------ .../sections/alert_form/alert_add.tsx | 41 ++++-- .../sections/alert_form/alert_edit.test.tsx | 61 ++++---- .../sections/alert_form/alert_edit.tsx | 42 ++++-- .../sections/alert_form/alert_form.test.tsx | 137 ++++++------------ .../sections/alert_form/alert_form.tsx | 31 ++-- .../alerts_list/components/alerts_list.tsx | 38 ++--- .../with_actions_api_operations.tsx | 3 - .../with_bulk_alert_api_operations.tsx | 3 - .../public/common/get_add_alert_flyout.tsx | 16 ++ .../public/common/get_edit_alert_flyout.tsx | 16 ++ .../common/lib/kibana/kibana_react.mock.ts | 1 - .../triggers_actions_ui/public/index.ts | 1 - .../triggers_actions_ui/public/plugin.ts | 32 +++- .../triggers_actions_ui/public/types.ts | 14 +- .../plugins/uptime/public/apps/uptime_app.tsx | 23 ++- .../alerts/uptime_edit_alert_flyout.tsx | 25 +++- .../ml/__tests__/ml_integerations.test.tsx | 11 +- .../monitor/ml/__tests__/ml_job_link.test.tsx | 9 +- .../ml/__tests__/ml_manage_job.test.tsx | 9 +- .../components/overview/alerts/index.ts | 1 - .../alerts/uptime_alerts_context_provider.tsx | 66 --------- .../alerts/uptime_alerts_flyout_wrapper.tsx | 38 +++-- 73 files changed, 862 insertions(+), 1172 deletions(-) delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/context/alerts_context.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/common/get_add_alert_flyout.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/common/get_edit_alert_flyout.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx diff --git a/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx b/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx index 6e44479d058d..134cda6f5418 100644 --- a/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx +++ b/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx @@ -22,7 +22,6 @@ import { AlertConditionsGroup, AlertTypeModel, AlertTypeParamsExpressionProps, - AlertsContextValue, } from '../../../../plugins/triggers_actions_ui/public'; import { AlwaysFiringParams, @@ -65,7 +64,7 @@ const DEFAULT_THRESHOLDS: AlwaysFiringParams['thresholds'] = { }; export const AlwaysFiringExpression: React.FunctionComponent< - AlertTypeParamsExpressionProps + AlertTypeParamsExpressionProps > = ({ alertParams, setAlertParams, actionGroups, defaultActionGroupId }) => { const { instances = DEFAULT_INSTANCES_TO_GENERATE, diff --git a/x-pack/examples/alerting_example/public/components/create_alert.tsx b/x-pack/examples/alerting_example/public/components/create_alert.tsx index 6a85e21df450..8b62dfbb0997 100644 --- a/x-pack/examples/alerting_example/public/components/create_alert.tsx +++ b/x-pack/examples/alerting_example/public/components/create_alert.tsx @@ -4,26 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { EuiIcon, EuiFlexItem, EuiCard, EuiFlexGroup } from '@elastic/eui'; -import { AlertsContextProvider, AlertAdd } from '../../../../plugins/triggers_actions_ui/public'; import { AlertingExampleComponentParams } from '../application'; import { ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; -export const CreateAlert = ({ - http, - triggersActionsUi, - charts, - uiSettings, - docLinks, - data, - toastNotifications, - capabilities, -}: AlertingExampleComponentParams) => { +export const CreateAlert = ({ triggersActionsUi }: AlertingExampleComponentParams) => { const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState(false); + const AddAlertFlyout = useMemo( + () => + triggersActionsUi.getAddAlertFlyout({ + consumer: ALERTING_EXAMPLE_APP_ID, + addFlyoutVisible: alertFlyoutVisible, + setAddFlyoutVisibility: setAlertFlyoutVisibility, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [alertFlyoutVisible] + ); + return ( @@ -34,27 +35,7 @@ export const CreateAlert = ({ onClick={() => setAlertFlyoutVisibility(true)} /> - - - - - + {AddAlertFlyout} ); }; diff --git a/x-pack/plugins/apm/public/application/application.test.tsx b/x-pack/plugins/apm/public/application/application.test.tsx index 2ad5a85be7d7..57e5e4b49bd4 100644 --- a/x-pack/plugins/apm/public/application/application.test.tsx +++ b/x-pack/plugins/apm/public/application/application.test.tsx @@ -9,10 +9,12 @@ import { createMemoryHistory } from 'history'; import { Observable } from 'rxjs'; import { AppMountParameters, CoreStart, HttpSetup } from 'src/core/public'; import { mockApmPluginContextValue } from '../context/apm_plugin/mock_apm_plugin_context'; -import { ApmPluginSetupDeps } from '../plugin'; +import { ApmPluginSetupDeps, ApmPluginStartDeps } from '../plugin'; import { createCallApmApi } from '../services/rest/createCallApmApi'; import { renderApp } from './'; import { disableConsoleWarning } from '../utils/testHelpers'; +import { dataPluginMock } from 'src/plugins/data/public/mocks'; +import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks'; jest.mock('../services/rest/index_pattern', () => ({ createStaticIndexPattern: () => Promise.resolve(undefined), @@ -55,6 +57,19 @@ describe('renderApp', () => { history: createMemoryHistory(), setHeaderActionMenu: () => {}, }; + + const data = dataPluginMock.createStartContract(); + const embeddable = embeddablePluginMock.createStartContract(); + const startDeps = { + triggersActionsUi: { + actionTypeRegistry: {}, + alertTypeRegistry: {}, + getAddAlertFlyout: jest.fn(), + getEditAlertFlyout: jest.fn(), + }, + data, + embeddable, + }; jest.spyOn(window, 'scrollTo').mockReturnValueOnce(undefined); createCallApmApi((core.http as unknown) as HttpSetup); @@ -75,7 +90,8 @@ describe('renderApp', () => { (core as unknown) as CoreStart, (plugins as unknown) as ApmPluginSetupDeps, (params as unknown) as AppMountParameters, - config + config, + (startDeps as unknown) as ApmPluginStartDeps ); }); diff --git a/x-pack/plugins/apm/public/application/index.tsx b/x-pack/plugins/apm/public/application/index.tsx index 9c4413765a50..16b3aaf9a6cd 100644 --- a/x-pack/plugins/apm/public/application/index.tsx +++ b/x-pack/plugins/apm/public/application/index.tsx @@ -19,7 +19,6 @@ import { RedirectAppLinks, useUiSetting$, } from '../../../../../src/plugins/kibana_react/public'; -import { AlertsContextProvider } from '../../../triggers_actions_ui/public'; import { routes } from '../components/app/Main/route_config'; import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange'; import { @@ -29,7 +28,7 @@ import { import { LicenseProvider } from '../context/license/license_context'; import { UrlParamsProvider } from '../context/url_params_context/url_params_context'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; -import { ApmPluginSetupDeps } from '../plugin'; +import { ApmPluginSetupDeps, ApmPluginStartDeps } from '../plugin'; import { createCallApmApi } from '../services/rest/createCallApmApi'; import { createStaticIndexPattern } from '../services/rest/index_pattern'; import { setHelpExtension } from '../setHelpExtension'; @@ -66,38 +65,29 @@ function App() { export function ApmAppRoot({ apmPluginContextValue, + startDeps, }: { apmPluginContextValue: ApmPluginContextValue; + startDeps: ApmPluginStartDeps; }) { - const { appMountParameters, core, plugins } = apmPluginContextValue; + const { appMountParameters, core } = apmPluginContextValue; const { history } = appMountParameters; const i18nCore = core.i18n; return ( - - - - - - - - - - - - - + + + + + + + + + + + ); @@ -111,7 +101,8 @@ export const renderApp = ( core: CoreStart, setupDeps: ApmPluginSetupDeps, appMountParameters: AppMountParameters, - config: ConfigSchema + config: ConfigSchema, + startDeps: ApmPluginStartDeps ) => { const { element } = appMountParameters; const apmPluginContextValue = { @@ -133,7 +124,10 @@ export const renderApp = ( }); ReactDOM.render( - , + , element ); return () => { diff --git a/x-pack/plugins/apm/public/components/alerting/AlertingFlyout/index.tsx b/x-pack/plugins/apm/public/components/alerting/AlertingFlyout/index.tsx index 3bee6b238826..aa1d21dd1d58 100644 --- a/x-pack/plugins/apm/public/components/alerting/AlertingFlyout/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/AlertingFlyout/index.tsx @@ -3,29 +3,37 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useMemo } from 'react'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { AlertType } from '../../../../common/alert_types'; -import { AlertAdd } from '../../../../../triggers_actions_ui/public'; - -type AlertAddProps = React.ComponentProps; +import { TriggersAndActionsUIPublicPluginStart } from '../../../../../triggers_actions_ui/public'; interface Props { - addFlyoutVisible: AlertAddProps['addFlyoutVisible']; - setAddFlyoutVisibility: AlertAddProps['setAddFlyoutVisibility']; + addFlyoutVisible: boolean; + setAddFlyoutVisibility: React.Dispatch>; alertType: AlertType | null; } +interface KibanaDeps { + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; +} + export function AlertingFlyout(props: Props) { const { addFlyoutVisible, setAddFlyoutVisibility, alertType } = props; - return ( - alertType && ( - - ) + const { + services: { triggersActionsUi }, + } = useKibana(); + const addAlertFlyout = useMemo( + () => + alertType && + triggersActionsUi.getAddAlertFlyout({ + consumer: 'apm', + addFlyoutVisible, + setAddFlyoutVisibility, + alertTypeId: alertType, + canChangeTrigger: false, + }), + [addFlyoutVisible, alertType, setAddFlyoutVisibility, triggersActionsUi] ); + return <>{addAlertFlyout}; } diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index cc0151afba63..89401d9192b0 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -138,12 +138,18 @@ export class ApmPlugin implements Plugin { async mount(params: AppMountParameters) { // Load application bundle and Get start services - const [{ renderApp }, [coreStart]] = await Promise.all([ + const [{ renderApp }, [coreStart, corePlugins]] = await Promise.all([ import('./application'), core.getStartServices(), ]); - return renderApp(coreStart, pluginSetupDeps, params, config); + return renderApp( + coreStart, + pluginSetupDeps, + params, + config, + corePlugins as ApmPluginStartDeps + ); }, }); diff --git a/x-pack/plugins/infra/public/alerting/common/components/alert_preview.tsx b/x-pack/plugins/infra/public/alerting/common/components/alert_preview.tsx index 7b15acfb6c4e..4bf61a5a269f 100644 --- a/x-pack/plugins/infra/public/alerting/common/components/alert_preview.tsx +++ b/x-pack/plugins/infra/public/alerting/common/components/alert_preview.tsx @@ -3,7 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { HttpSetup } from 'kibana/public'; import { omit } from 'lodash'; import React, { useCallback, useMemo, useState } from 'react'; import { @@ -20,6 +19,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { FORMATTERS } from '../../../../common/formatters'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ValidationResult } from '../../../../../triggers_actions_ui/public/types'; @@ -35,7 +35,6 @@ interface Props { alertInterval: string; alertThrottle: string; alertType: PreviewableAlertTypes; - fetch: HttpSetup['fetch']; alertParams: { criteria: any[]; sourceId: string } & Record; validate: (params: any) => ValidationResult; showNoDataResults?: boolean; @@ -47,12 +46,13 @@ export const AlertPreview: React.FC = (props) => { alertParams, alertInterval, alertThrottle, - fetch, alertType, validate, showNoDataResults, groupByDisplayName, } = props; + const { http } = useKibana().services; + const [previewLookbackInterval, setPreviewLookbackInterval] = useState('h'); const [isPreviewLoading, setIsPreviewLoading] = useState(false); const [previewError, setPreviewError] = useState(false); @@ -70,7 +70,7 @@ export const AlertPreview: React.FC = (props) => { setPreviewError(false); try { const result = await getAlertPreview({ - fetch, + fetch: http!.fetch, params: { ...alertParams, lookback: previewLookbackInterval as 'h' | 'd' | 'w' | 'M', @@ -89,12 +89,12 @@ export const AlertPreview: React.FC = (props) => { }, [ alertParams, alertInterval, - fetch, alertType, groupByDisplayName, previewLookbackInterval, alertThrottle, showNoDataResults, + http, ]); const previewIntervalError = useMemo(() => { diff --git a/x-pack/plugins/infra/public/alerting/inventory/components/alert_flyout.tsx b/x-pack/plugins/infra/public/alerting/inventory/components/alert_flyout.tsx index f7f7048aedf4..432d2073d93b 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/components/alert_flyout.tsx +++ b/x-pack/plugins/infra/public/alerting/inventory/components/alert_flyout.tsx @@ -4,12 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; -import { ApplicationStart, DocLinksStart, HttpStart, NotificationsStart } from 'src/core/public'; +import React, { useContext, useMemo } from 'react'; -import { AlertsContextProvider, AlertAdd } from '../../../../../triggers_actions_ui/public'; import { TriggerActionsContext } from '../../../utils/triggers_actions_context'; -import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from '../../../../server/lib/alerting/inventory_metric_threshold/types'; import { InfraWaffleMapOptions } from '../../../lib/lib'; @@ -24,48 +21,31 @@ interface Props { setVisible: React.Dispatch>; } -interface KibanaDeps { - notifications: NotificationsStart; - http: HttpStart; - docLinks: DocLinksStart; - application: ApplicationStart; -} - export const AlertFlyout = ({ options, nodeType, filter, visible, setVisible }: Props) => { const { triggersActionsUI } = useContext(TriggerActionsContext); - const { services } = useKibana(); const { inventoryPrefill } = useAlertPrefillContext(); const { customMetrics } = inventoryPrefill; - return ( - <> - {triggersActionsUI && ( - - - - )} - + const AddAlertFlyout = useMemo( + () => + triggersActionsUI && + triggersActionsUI.getAddAlertFlyout({ + consumer: 'infrastructure', + addFlyoutVisible: visible!, + setAddFlyoutVisibility: setVisible, + canChangeTrigger: false, + alertTypeId: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, + metadata: { + options, + nodeType, + filter, + customMetrics, + }, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [triggersActionsUI, visible] ); + + return <>{AddAlertFlyout}; }; diff --git a/x-pack/plugins/infra/public/alerting/inventory/components/expression.test.tsx b/x-pack/plugins/infra/public/alerting/inventory/components/expression.test.tsx index 6f830598ac46..43764c518ef9 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/components/expression.test.tsx +++ b/x-pack/plugins/infra/public/alerting/inventory/components/expression.test.tsx @@ -5,10 +5,8 @@ */ import { mountWithIntl, nextTick } from '@kbn/test/jest'; -import { actionTypeRegistryMock } from '../../../../../triggers_actions_ui/public/application/action_type_registry.mock'; -import { alertTypeRegistryMock } from '../../../../../triggers_actions_ui/public/application/alert_type_registry.mock'; -import { coreMock } from '../../../../../../../src/core/public/mocks'; -import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; +// We are using this inside a `jest.mock` call. Jest requires dynamic dependencies to be prefixed with `mock` +import { coreMock as mockCoreMock } from 'src/core/public/mocks'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { InventoryMetricConditions } from '../../../../server/lib/alerting/inventory_metric_threshold/types'; import React from 'react'; @@ -25,6 +23,12 @@ jest.mock('../../../containers/source/use_source_via_http', () => ({ }), })); +jest.mock('../../../hooks/use_kibana', () => ({ + useKibanaContextForPlugin: () => ({ + services: mockCoreMock.createStart(), + }), +})); + const exampleCustomMetric = { id: 'this-is-an-id', field: 'some.system.field', @@ -39,41 +43,15 @@ describe('Expression', () => { nodeType: undefined, filterQueryText: '', }; - - const mocks = coreMock.createSetup(); - const startMocks = coreMock.createStart(); - const [ - { - application: { capabilities }, - }, - ] = await mocks.getStartServices(); - - const context: AlertsContextValue = { - http: mocks.http, - toastNotifications: mocks.notifications.toasts, - actionTypeRegistry: actionTypeRegistryMock.create() as any, - alertTypeRegistry: alertTypeRegistryMock.create() as any, - docLinks: startMocks.docLinks, - capabilities: { - ...capabilities, - actions: { - delete: true, - save: true, - show: true, - }, - }, - metadata: currentOptions, - }; - const wrapper = mountWithIntl( Reflect.set(alertParams, key, value)} setAlertProperty={() => {}} + metadata={currentOptions} /> ); @@ -153,9 +131,6 @@ describe('ExpressionRow', () => { metric: [], }} expression={expression} - alertsContextMetadata={{ - customMetrics: [], - }} fields={[{ name: 'some.system.field', type: 'bzzz' }]} /> ); diff --git a/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx b/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx index e16b2aeaacac..3dc754822879 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx @@ -38,8 +38,6 @@ import { } from '../../../../../triggers_actions_ui/public/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { IErrorObject } from '../../../../../triggers_actions_ui/public/types'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar'; import { useSourceViaHttp } from '../../../containers/source/use_source_via_http'; import { sqsMetricTypes } from '../../../../common/inventory_models/aws_sqs/toolbar_items'; @@ -67,6 +65,7 @@ import { } from '../../../../common/http_api/snapshot_api'; import { validateMetricThreshold } from './validation'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; const FILTER_TYPING_DEBOUNCE_MS = 500; @@ -89,9 +88,9 @@ interface Props { }; alertInterval: string; alertThrottle: string; - alertsContext: AlertsContextValue; setAlertParams(key: string, value: any): void; setAlertProperty(key: string, value: any): void; + metadata: AlertContextMeta; } export const defaultExpression = { @@ -109,19 +108,13 @@ export const defaultExpression = { } as InventoryMetricConditions; export const Expressions: React.FC = (props) => { - const { - setAlertParams, - alertParams, - errors, - alertsContext, - alertInterval, - alertThrottle, - } = props; + const { http, notifications } = useKibanaContextForPlugin().services; + const { setAlertParams, alertParams, errors, alertInterval, alertThrottle, metadata } = props; const { source, createDerivedIndexPattern } = useSourceViaHttp({ sourceId: 'default', type: 'metrics', - fetch: alertsContext.http.fetch, - toastWarning: alertsContext.toastNotifications.addWarning, + fetch: http.fetch, + toastWarning: notifications.toasts.addWarning, }); const [timeSize, setTimeSize] = useState(1); const [timeUnit, setTimeUnit] = useState('m'); @@ -221,7 +214,7 @@ export const Expressions: React.FC = (props) => { ); const preFillAlertCriteria = useCallback(() => { - const md = alertsContext.metadata; + const md = metadata; if (md && md.options) { setAlertParams('criteria', [ { @@ -235,10 +228,10 @@ export const Expressions: React.FC = (props) => { } else { setAlertParams('criteria', [defaultExpression]); } - }, [alertsContext.metadata, setAlertParams]); + }, [metadata, setAlertParams]); const preFillAlertFilter = useCallback(() => { - const md = alertsContext.metadata; + const md = metadata; if (md && md.filter) { setAlertParams('filterQueryText', md.filter); setAlertParams( @@ -246,10 +239,10 @@ export const Expressions: React.FC = (props) => { convertKueryToElasticSearchQuery(md.filter, derivedIndexPattern) || '' ); } - }, [alertsContext.metadata, derivedIndexPattern, setAlertParams]); + }, [metadata, derivedIndexPattern, setAlertParams]); useEffect(() => { - const md = alertsContext.metadata; + const md = metadata; if (!alertParams.nodeType) { if (md && md.nodeType) { setAlertParams('nodeType', md.nodeType); @@ -272,7 +265,7 @@ export const Expressions: React.FC = (props) => { if (!alertParams.sourceId) { setAlertParams('sourceId', source?.id || 'default'); } - }, [alertsContext.metadata, derivedIndexPattern, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps + }, [metadata, derivedIndexPattern, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps return ( <> @@ -308,7 +301,6 @@ export const Expressions: React.FC = (props) => { setAlertParams={updateParams} errors={errors[idx] || emptyError} expression={e || {}} - alertsContextMetadata={alertsContext.metadata} fields={derivedIndexPattern.fields} /> ); @@ -371,7 +363,7 @@ export const Expressions: React.FC = (props) => { fullWidth display="rowCompressed" > - {(alertsContext.metadata && ( + {(metadata && ( = (props) => { alertType={METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID} alertParams={pick(alertParams, 'criteria', 'nodeType', 'sourceId', 'filterQuery')} validate={validateMetricThreshold} - fetch={alertsContext.http.fetch} groupByDisplayName={alertParams.nodeType} showNoDataResults={alertParams.alertOnNoData} /> @@ -418,7 +409,6 @@ interface ExpressionRowProps { addExpression(): void; remove(id: number): void; setAlertParams(id: number, params: Partial): void; - alertsContextMetadata: AlertsContextValue['metadata']; fields: IFieldType[]; } diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_flyout.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_flyout.tsx index cd69fe02c584..206621c4d4dc 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_flyout.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_flyout.tsx @@ -4,11 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; -import { ApplicationStart, DocLinksStart, HttpStart, NotificationsStart } from 'src/core/public'; -import { AlertsContextProvider, AlertAdd } from '../../../../../triggers_actions_ui/public'; +import React, { useContext, useMemo } from 'react'; import { TriggerActionsContext } from '../../../utils/triggers_actions_context'; -import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../../common/alerting/logs/log_threshold/types'; interface Props { @@ -16,42 +13,25 @@ interface Props { setVisible: React.Dispatch>; } -interface KibanaDeps { - notifications: NotificationsStart; - http: HttpStart; - docLinks: DocLinksStart; - application: ApplicationStart; -} - export const AlertFlyout = (props: Props) => { const { triggersActionsUI } = useContext(TriggerActionsContext); - const { services } = useKibana(); - return ( - <> - {triggersActionsUI && ( - - - - )} - + const AddAlertFlyout = useMemo( + () => + triggersActionsUI && + triggersActionsUI.getAddAlertFlyout({ + consumer: 'logs', + addFlyoutVisible: props.visible!, + setAddFlyoutVisibility: props.setVisible, + canChangeTrigger: false, + alertTypeId: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, + metadata: { + isInternal: true, + }, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [triggersActionsUI, props.visible] ); + + return <>{AddAlertFlyout}; }; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx index c35e7141efc9..3c474ee1d0ec 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx @@ -22,7 +22,7 @@ import { getDenominator, } from '../../../../../common/alerting/logs/log_threshold/types'; import { Errors, CriterionErrors } from '../../validation'; -import { AlertsContext, ExpressionLike } from './editor'; +import { ExpressionLike } from './editor'; import { CriterionPreview } from './criterion_preview_chart'; const DEFAULT_CRITERIA = { field: 'log.level', comparator: Comparator.EQ, value: 'error' }; @@ -40,7 +40,6 @@ interface SharedProps { criteria?: AlertParams['criteria']; errors: Errors['criteria']; alertParams: Partial; - context: AlertsContext; sourceId: string; updateCriteria: (criteria: AlertParams['criteria']) => void; } @@ -66,7 +65,6 @@ interface CriteriaWrapperProps { addCriterion: () => void; criteria: CriteriaType; errors: CriterionErrors; - context: SharedProps['context']; sourceId: SharedProps['sourceId']; isRatio?: boolean; } @@ -80,7 +78,6 @@ const CriteriaWrapper: React.FC = (props) => { fields, errors, alertParams, - context, sourceId, isRatio = false, } = props; @@ -108,7 +105,6 @@ const CriteriaWrapper: React.FC = (props) => { > void; } @@ -201,7 +196,6 @@ interface CountCriteriaProps { fields: SharedProps['fields']; criteria: CountCriteriaType; errors: Errors['criteria']; - context: SharedProps['context']; sourceId: SharedProps['sourceId']; updateCriteria: (criteria: AlertParams['criteria']) => void; } diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx index 1d23b4d4778c..47dc41902288 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx @@ -19,6 +19,7 @@ import { } from '@elastic/charts'; import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { ChartContainer, LoadingState, @@ -43,7 +44,6 @@ import { GetLogAlertsChartPreviewDataAlertParamsSubset, getLogAlertsChartPreviewDataAlertParamsSubsetRT, } from '../../../../../common/http_api/log_alerts/'; -import { AlertsContext } from './editor'; import { useChartPreviewData } from './hooks/use_chart_preview_data'; import { decodeOrThrow } from '../../../../../common/runtime_types'; @@ -51,7 +51,6 @@ const GROUP_LIMIT = 5; interface Props { alertParams: Partial; - context: AlertsContext; chartCriterion: Partial; sourceId: string; showThreshold: boolean; @@ -59,7 +58,6 @@ interface Props { export const CriterionPreview: React.FC = ({ alertParams, - context, chartCriterion, sourceId, showThreshold, @@ -91,7 +89,6 @@ export const CriterionPreview: React.FC = ({ ? NUM_BUCKETS : NUM_BUCKETS / 4 } // Display less data for groups due to space limitations - context={context} sourceId={sourceId} threshold={alertParams.count} chartAlertParams={chartAlertParams} @@ -102,7 +99,6 @@ export const CriterionPreview: React.FC = ({ interface ChartProps { buckets: number; - context: AlertsContext; sourceId: string; threshold?: Threshold; chartAlertParams: GetLogAlertsChartPreviewDataAlertParamsSubset; @@ -111,13 +107,13 @@ interface ChartProps { const CriterionPreviewChart: React.FC = ({ buckets, - context, sourceId, threshold, chartAlertParams, showThreshold, }) => { - const isDarkMode = context.uiSettings?.get('theme:darkMode') || false; + const { uiSettings } = useKibana().services; + const isDarkMode = uiSettings?.get('theme:darkMode') || false; const { getChartPreviewData, @@ -125,7 +121,6 @@ const CriterionPreviewChart: React.FC = ({ hasError, chartPreviewData: series, } = useChartPreviewData({ - context, sourceId, alertParams: chartAlertParams, buckets, diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx index 662b7f68f8fe..854363aacca5 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx @@ -8,11 +8,9 @@ import React, { useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiLoadingSpinner, EuiSpacer, EuiButton, EuiCallOut } from '@elastic/eui'; import useMount from 'react-use/lib/useMount'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { GroupByExpression } from '../../../common/group_by_expression/group_by_expression'; -import { - ForLastExpression, - AlertsContextValue, -} from '../../../../../../triggers_actions_ui/public'; +import { ForLastExpression } from '../../../../../../triggers_actions_ui/public'; import { AlertParams, Comparator, @@ -36,14 +34,13 @@ interface LogsContextMeta { isInternal?: boolean; } -export type AlertsContext = AlertsContextValue; interface Props { errors: Errors; alertParams: Partial; setAlertParams(key: string, value: any): void; setAlertProperty(key: string, value: any): void; - alertsContext: AlertsContext; sourceId: string; + metadata: LogsContextMeta; } const DEFAULT_CRITERIA = { field: 'log.level', comparator: Comparator.EQ, value: 'error' }; @@ -75,8 +72,9 @@ const DEFAULT_RATIO_EXPRESSION = { }; export const ExpressionEditor: React.FC = (props) => { - const isInternal = props.alertsContext.metadata?.isInternal; + const isInternal = props.metadata?.isInternal; const [sourceId] = useSourceId(); + const { http } = useKibana().services; return ( <> @@ -85,7 +83,7 @@ export const ExpressionEditor: React.FC = (props) => { ) : ( - + @@ -139,7 +137,7 @@ export const SourceStatusWrapper: React.FC = (props) => { }; export const Editor: React.FC = (props) => { - const { setAlertParams, alertParams, errors, alertsContext, sourceId } = props; + const { setAlertParams, alertParams, errors, sourceId } = props; const [hasSetDefaults, setHasSetDefaults] = useState(false); const { sourceStatus } = useLogSourceContext(); useMount(() => { @@ -228,7 +226,6 @@ export const Editor: React.FC = (props) => { criteria={alertParams.criteria} errors={errors.criteria} alertParams={alertParams} - context={alertsContext} sourceId={sourceId} updateCriteria={updateCriteria} /> diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx index d5ba730026b1..5ca8927dcd4a 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { useState, useMemo } from 'react'; -import { AlertsContext } from '../editor'; +import { HttpHandler } from 'kibana/public'; +import { useKibana } from '../../../../../../../../../src/plugins/kibana_react/public'; import { useTrackedPromise } from '../../../../../utils/use_tracked_promise'; import { GetLogAlertsChartPreviewDataSuccessResponsePayload, @@ -17,12 +18,13 @@ import { GetLogAlertsChartPreviewDataAlertParamsSubset } from '../../../../../.. interface Options { sourceId: string; - context: AlertsContext; alertParams: GetLogAlertsChartPreviewDataAlertParamsSubset; buckets: number; } -export const useChartPreviewData = ({ context, sourceId, alertParams, buckets }: Options) => { +export const useChartPreviewData = ({ sourceId, alertParams, buckets }: Options) => { + const { http } = useKibana().services; + const [chartPreviewData, setChartPreviewData] = useState< GetLogAlertsChartPreviewDataSuccessResponsePayload['data']['series'] >([]); @@ -32,7 +34,7 @@ export const useChartPreviewData = ({ context, sourceId, alertParams, buckets }: cancelPreviousOn: 'creation', createPromise: async () => { setHasError(false); - return await callGetChartPreviewDataAPI(sourceId, context.http.fetch, alertParams, buckets); + return await callGetChartPreviewDataAPI(sourceId, http!.fetch, alertParams, buckets); }, onResolve: ({ data: { series } }) => { setHasError(false); @@ -42,7 +44,7 @@ export const useChartPreviewData = ({ context, sourceId, alertParams, buckets }: setHasError(true); }, }, - [sourceId, context.http.fetch, alertParams, buckets] + [sourceId, http, alertParams, buckets] ); const isLoading = useMemo(() => getChartPreviewDataRequest.state === 'pending', [ @@ -59,7 +61,7 @@ export const useChartPreviewData = ({ context, sourceId, alertParams, buckets }: export const callGetChartPreviewDataAPI = async ( sourceId: string, - fetch: AlertsContext['http']['fetch'], + fetch: HttpHandler, alertParams: GetLogAlertsChartPreviewDataAlertParamsSubset, buckets: number ) => { diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_flyout.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_flyout.tsx index 1f1af38809a3..779478a313b7 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_flyout.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_flyout.tsx @@ -4,12 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; -import { ApplicationStart, DocLinksStart, HttpStart, NotificationsStart } from 'src/core/public'; - -import { AlertsContextProvider, AlertAdd } from '../../../../../triggers_actions_ui/public'; +import React, { useContext, useMemo } from 'react'; import { TriggerActionsContext } from '../../../utils/triggers_actions_context'; -import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../../server/lib/alerting/metric_threshold/types'; import { MetricsExplorerSeries } from '../../../../common/http_api/metrics_explorer'; @@ -22,43 +18,26 @@ interface Props { setVisible: React.Dispatch>; } -interface KibanaDeps { - notifications: NotificationsStart; - http: HttpStart; - docLinks: DocLinksStart; - application: ApplicationStart; -} - export const AlertFlyout = (props: Props) => { const { triggersActionsUI } = useContext(TriggerActionsContext); - const { services } = useKibana(); - return ( - <> - {triggersActionsUI && ( - - - - )} - + const AddAlertFlyout = useMemo( + () => + triggersActionsUI && + triggersActionsUI.getAddAlertFlyout({ + consumer: 'infrastructure', + addFlyoutVisible: props.visible!, + setAddFlyoutVisibility: props.setVisible, + canChangeTrigger: false, + alertTypeId: METRIC_THRESHOLD_ALERT_TYPE_ID, + metadata: { + currentOptions: props.options, + series: props.series, + }, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [triggersActionsUI, props.visible] ); + + return <>{AddAlertFlyout}; }; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.test.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.test.tsx index c83f9ff33bac..9358204aba28 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.test.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.test.tsx @@ -5,11 +5,8 @@ */ import { mountWithIntl, nextTick } from '@kbn/test/jest'; -import { actionTypeRegistryMock } from '../../../../../triggers_actions_ui/public/application/action_type_registry.mock'; -import { alertTypeRegistryMock } from '../../../../../triggers_actions_ui/public/application/alert_type_registry.mock'; -import { coreMock } from '../../../../../../../src/core/public/mocks'; -import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; -import { AlertContextMeta } from '../types'; +// We are using this inside a `jest.mock` call. Jest requires dynamic dependencies to be prefixed with `mock` +import { coreMock as mockCoreMock } from 'src/core/public/mocks'; import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer'; import React from 'react'; import { Expressions } from './expression'; @@ -24,6 +21,12 @@ jest.mock('../../../containers/source/use_source_via_http', () => ({ }), })); +jest.mock('../../../hooks/use_kibana', () => ({ + useKibanaContextForPlugin: () => ({ + services: mockCoreMock.createStart(), + }), +})); + describe('Expression', () => { async function setup(currentOptions: { metrics?: MetricsExplorerMetric[]; @@ -36,43 +39,17 @@ describe('Expression', () => { filterQueryText: '', sourceId: 'default', }; - - const mocks = coreMock.createSetup(); - const startMocks = coreMock.createStart(); - const [ - { - application: { capabilities }, - }, - ] = await mocks.getStartServices(); - - const context: AlertsContextValue = { - http: mocks.http, - toastNotifications: mocks.notifications.toasts, - actionTypeRegistry: actionTypeRegistryMock.create() as any, - alertTypeRegistry: alertTypeRegistryMock.create() as any, - docLinks: startMocks.docLinks, - capabilities: { - ...capabilities, - actions: { - delete: true, - save: true, - show: true, - }, - }, - metadata: { - currentOptions, - }, - }; - const wrapper = mountWithIntl( Reflect.set(alertParams, key, value)} setAlertProperty={() => {}} + metadata={{ + currentOptions, + }} /> ); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx index 48e15e0026ff..a24a4601ba68 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx @@ -31,8 +31,6 @@ import { } from '../../../../../triggers_actions_ui/public/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { IErrorObject } from '../../../../../triggers_actions_ui/public/types'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar'; import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; import { MetricsExplorerGroupBy } from '../../../pages/metrics/metrics_explorer/components/group_by'; @@ -40,20 +38,21 @@ import { useSourceViaHttp } from '../../../containers/source/use_source_via_http import { convertKueryToElasticSearchQuery } from '../../../utils/kuery'; import { ExpressionRow } from './expression_row'; -import { AlertContextMeta, MetricExpression, AlertParams } from '../types'; +import { MetricExpression, AlertParams, AlertContextMeta } from '../types'; import { ExpressionChart } from './expression_chart'; import { validateMetricThreshold } from './validation'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; const FILTER_TYPING_DEBOUNCE_MS = 500; interface Props { errors: IErrorObject[]; alertParams: AlertParams; - alertsContext: AlertsContextValue; alertInterval: string; alertThrottle: string; setAlertParams(key: string, value: any): void; setAlertProperty(key: string, value: any): void; + metadata: AlertContextMeta; } const defaultExpression = { @@ -66,19 +65,13 @@ const defaultExpression = { export { defaultExpression }; export const Expressions: React.FC = (props) => { - const { - setAlertParams, - alertParams, - errors, - alertsContext, - alertInterval, - alertThrottle, - } = props; + const { setAlertParams, alertParams, errors, alertInterval, alertThrottle, metadata } = props; + const { http, notifications } = useKibanaContextForPlugin().services; const { source, createDerivedIndexPattern } = useSourceViaHttp({ sourceId: 'default', type: 'metrics', - fetch: alertsContext.http.fetch, - toastWarning: alertsContext.toastNotifications.addWarning, + fetch: http.fetch, + toastWarning: notifications.toasts.addWarning, }); const [timeSize, setTimeSize] = useState(1); @@ -88,15 +81,15 @@ export const Expressions: React.FC = (props) => { ]); const options = useMemo(() => { - if (alertsContext.metadata?.currentOptions?.metrics) { - return alertsContext.metadata.currentOptions as MetricsExplorerOptions; + if (metadata?.currentOptions?.metrics) { + return metadata.currentOptions as MetricsExplorerOptions; } else { return { metrics: [], aggregation: 'avg', }; } - }, [alertsContext.metadata]); + }, [metadata]); const updateParams = useCallback( (id, e: MetricExpression) => { @@ -186,7 +179,7 @@ export const Expressions: React.FC = (props) => { ); const preFillAlertCriteria = useCallback(() => { - const md = alertsContext.metadata; + const md = metadata; if (md?.currentOptions?.metrics?.length) { setAlertParams( 'criteria', @@ -202,10 +195,10 @@ export const Expressions: React.FC = (props) => { } else { setAlertParams('criteria', [defaultExpression]); } - }, [alertsContext.metadata, setAlertParams, timeSize, timeUnit]); + }, [metadata, setAlertParams, timeSize, timeUnit]); const preFillAlertFilter = useCallback(() => { - const md = alertsContext.metadata; + const md = metadata; if (md && md.currentOptions?.filterQuery) { setAlertParams('filterQueryText', md.currentOptions.filterQuery); setAlertParams( @@ -223,14 +216,14 @@ export const Expressions: React.FC = (props) => { convertKueryToElasticSearchQuery(filter, derivedIndexPattern) || '' ); } - }, [alertsContext.metadata, derivedIndexPattern, setAlertParams]); + }, [metadata, derivedIndexPattern, setAlertParams]); const preFillAlertGroupBy = useCallback(() => { - const md = alertsContext.metadata; + const md = metadata; if (md && md.currentOptions?.groupBy && !md.series) { setAlertParams('groupBy', md.currentOptions.groupBy); } - }, [alertsContext.metadata, setAlertParams]); + }, [metadata, setAlertParams]); useEffect(() => { if (alertParams.criteria && alertParams.criteria.length) { @@ -251,7 +244,7 @@ export const Expressions: React.FC = (props) => { if (!alertParams.sourceId) { setAlertParams('sourceId', source?.id || 'default'); } - }, [alertsContext.metadata, source]); // eslint-disable-line react-hooks/exhaustive-deps + }, [metadata, source]); // eslint-disable-line react-hooks/exhaustive-deps const handleFieldSearchChange = useCallback( (e: ChangeEvent) => onFilterChange(e.target.value), @@ -291,7 +284,6 @@ export const Expressions: React.FC = (props) => { > = (props) => { fullWidth display="rowCompressed" > - {(alertsContext.metadata && ( + {(metadata && ( = (props) => { alertParams={pick(alertParams, 'criteria', 'groupBy', 'filterQuery', 'sourceId')} showNoDataResults={alertParams.alertOnNoData} validate={validateMetricThreshold} - fetch={alertsContext.http.fetch} groupByDisplayName={groupByPreviewDisplayName} /> diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx index 5f3f0ea7f349..a75a692e6e57 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx @@ -5,11 +5,9 @@ */ import { mountWithIntl, nextTick } from '@kbn/test/jest'; -import { actionTypeRegistryMock } from '../../../../../triggers_actions_ui/public/application/action_type_registry.mock'; -import { alertTypeRegistryMock } from '../../../../../triggers_actions_ui/public/application/alert_type_registry.mock'; -import { coreMock } from '../../../../../../../src/core/public/mocks'; -import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; -import { AlertContextMeta, MetricExpression } from '../types'; +// We are using this inside a `jest.mock` call. Jest requires dynamic dependencies to be prefixed with `mock` +import { coreMock as mockCoreMock } from 'src/core/public/mocks'; +import { MetricExpression } from '../types'; import { IIndexPattern } from 'src/plugins/data/public'; import { InfraSource } from '../../../../common/http_api/source_api'; import React from 'react'; @@ -17,38 +15,30 @@ import { ExpressionChart } from './expression_chart'; import { act } from 'react-dom/test-utils'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { Aggregators, Comparator } from '../../../../server/lib/alerting/metric_threshold/types'; -import { MetricsExplorerResponse } from '../../../../common/http_api'; -describe('ExpressionChart', () => { - async function setup( - expression: MetricExpression, - response: MetricsExplorerResponse | null, - filterQuery?: string, - groupBy?: string - ) { - const mocks = coreMock.createSetup(); - const startMocks = coreMock.createStart(); - const [ - { - application: { capabilities }, - }, - ] = await mocks.getStartServices(); +const mockStartServices = mockCoreMock.createStart(); +jest.mock('../../../hooks/use_kibana', () => ({ + useKibanaContextForPlugin: () => ({ + services: { + ...mockStartServices, + }, + }), +})); - const context: AlertsContextValue = { - http: mocks.http, - toastNotifications: mocks.notifications.toasts, - actionTypeRegistry: actionTypeRegistryMock.create() as any, - alertTypeRegistry: alertTypeRegistryMock.create() as any, - docLinks: startMocks.docLinks, - capabilities: { - ...capabilities, - actions: { - delete: true, - save: true, - show: true, - }, - }, - }; +const mockResponse = { + pageInfo: { + afterKey: null, + total: 0, + }, + series: [{ id: 'Everything', rows: [], columns: [] }], +}; + +jest.mock('../hooks/use_metrics_explorer_chart_data', () => ({ + useMetricsExplorerChartData: () => ({ loading: false, data: mockResponse }), +})); + +describe('ExpressionChart', () => { + async function setup(expression: MetricExpression, filterQuery?: string, groupBy?: string) { const derivedIndexPattern: IIndexPattern = { title: 'metricbeat-*', fields: [], @@ -76,11 +66,8 @@ describe('ExpressionChart', () => { }, }; - mocks.http.fetch.mockImplementation(() => Promise.resolve(response)); - const wrapper = mountWithIntl( { await update(); - return { wrapper, update, fetchMock: mocks.http.fetch }; + return { wrapper, update }; } it('should display no data message', async () => { @@ -109,14 +96,7 @@ describe('ExpressionChart', () => { threshold: [1], comparator: Comparator.GT_OR_EQ, }; - const response = { - pageInfo: { - afterKey: null, - total: 0, - }, - series: [{ id: 'Everything', rows: [], columns: [] }], - }; - const { wrapper } = await setup(expression, response); + const { wrapper } = await setup(expression); expect(wrapper.find('[data-test-subj~="noChartData"]').exists()).toBeTruthy(); }); }); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx index 065314d31b00..ae53cd8c2081 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx @@ -21,8 +21,6 @@ import { i18n } from '@kbn/i18n'; import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { IIndexPattern } from 'src/plugins/data/public'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; import { InfraSource } from '../../../../common/http_api/source_api'; import { Comparator, @@ -31,16 +29,16 @@ import { import { Color, colorTransformer } from '../../../../common/color_palette'; import { MetricsExplorerRow, MetricsExplorerAggregation } from '../../../../common/http_api'; import { MetricExplorerSeriesChart } from '../../../pages/metrics/metrics_explorer/components/series_chart'; -import { MetricExpression, AlertContextMeta } from '../types'; +import { MetricExpression } from '../types'; import { MetricsExplorerChartType } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; import { getChartTheme } from '../../../pages/metrics/metrics_explorer/components/helpers/get_chart_theme'; import { createFormatterForMetric } from '../../../pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric'; import { calculateDomain } from '../../../pages/metrics/metrics_explorer/components/helpers/calculate_domain'; import { useMetricsExplorerChartData } from '../hooks/use_metrics_explorer_chart_data'; import { getMetricId } from '../../../pages/metrics/metrics_explorer/components/helpers/get_metric_id'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; interface Props { - context: AlertsContextValue; expression: MetricExpression; derivedIndexPattern: IIndexPattern; source: InfraSource | null; @@ -62,7 +60,6 @@ const TIME_LABELS = { export const ExpressionChart: React.FC = ({ expression, - context, derivedIndexPattern, source, filterQuery, @@ -70,19 +67,20 @@ export const ExpressionChart: React.FC = ({ }) => { const { loading, data } = useMetricsExplorerChartData( expression, - context, derivedIndexPattern, source, filterQuery, groupBy ); + const { uiSettings } = useKibanaContextForPlugin().services; + const metric = { field: expression.metric, aggregation: expression.aggType as MetricsExplorerAggregation, color: Color.color0, }; - const isDarkMode = context.uiSettings?.get('theme:darkMode') || false; + const isDarkMode = uiSettings?.get('theme:darkMode') || false; const dateFormatter = useMemo(() => { const firstSeries = first(data?.series); const firstTimestamp = first(firstSeries?.rows)?.timestamp; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts index a3d09742e9a5..a1ebc37b8e97 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts @@ -7,15 +7,12 @@ import { IIndexPattern } from 'src/plugins/data/public'; import { useMemo } from 'react'; import { InfraSource } from '../../../../common/http_api/source_api'; -import { AlertContextMeta, MetricExpression } from '../types'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; +import { MetricExpression } from '../types'; import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; import { useMetricsExplorerData } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data'; export const useMetricsExplorerChartData = ( expression: MetricExpression, - context: AlertsContextValue, derivedIndexPattern: IIndexPattern, source: InfraSource | null, filterQuery?: string, @@ -54,7 +51,6 @@ export const useMetricsExplorerChartData = ( derivedIndexPattern, timerange, null, - null, - context.http.fetch + null ); }; diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts index 4b46ed2efafc..169bc9bcbcdb 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts @@ -48,7 +48,6 @@ export const useMetricsExplorerState = ( currentTimerange, afterKey, refreshSignal, - undefined, shouldLoadImmediately ); diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts index db1e4ec8e4db..924f59eb0d1d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts @@ -7,7 +7,6 @@ import DateMath from '@elastic/datemath'; import { isEqual } from 'lodash'; import { useEffect, useState, useCallback } from 'react'; -import { HttpHandler } from 'src/core/public'; import { IIndexPattern } from 'src/plugins/data/public'; import { SourceQuery } from '../../../../../common/graphql/types'; import { @@ -30,11 +29,10 @@ export function useMetricsExplorerData( timerange: MetricsExplorerTimeOptions, afterKey: string | null | Record, signal: any, - fetch?: HttpHandler, shouldLoadImmediately = true ) { const kibana = useKibana(); - const fetchFn = fetch ? fetch : kibana.services.http?.fetch; + const fetchFn = kibana.services.http?.fetch; const [error, setError] = useState(null); const [loading, setLoading] = useState(true); const [data, setData] = useState(null); diff --git a/x-pack/plugins/monitoring/kibana.json b/x-pack/plugins/monitoring/kibana.json index a3d886b14cdf..501b84dd8825 100644 --- a/x-pack/plugins/monitoring/kibana.json +++ b/x-pack/plugins/monitoring/kibana.json @@ -24,5 +24,5 @@ ], "server": true, "ui": true, - "requiredBundles": ["kibanaUtils", "home", "alerts", "kibanaReact", "licenseManagement", "triggersActionsUi"] + "requiredBundles": ["kibanaUtils", "home", "alerts", "kibanaReact", "licenseManagement"] } diff --git a/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx b/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx index c62f548dd678..e4ee805c4b48 100644 --- a/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx +++ b/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx @@ -18,7 +18,6 @@ import { alertTypeRegistryMock } from '../../../triggers_actions_ui/public/appli import { ValidationResult, Alert } from '../../../triggers_actions_ui/public/types'; import { AlertForm } from '../../../triggers_actions_ui/public/application/sections/alert_form/alert_form'; import ActionForm from '../../../triggers_actions_ui/public/application/sections/action_connector_form/action_form'; -import { AlertsContextProvider } from '../../../triggers_actions_ui/public/application/context/alerts_context'; import { Legacy } from '../legacy_shims'; import { I18nProvider } from '@kbn/i18n/react'; import { createKibanaReactContext } from '../../../../../src/plugins/kibana_react/public'; @@ -100,7 +99,6 @@ describe('alert_form', () => { let wrapper: ReactWrapper; beforeEach(async () => { - const coreStart = coreMock.createStart(); alertTypeRegistry.list.mockReturnValue([alertType]); alertTypeRegistry.get.mockReturnValue(alertType); alertTypeRegistry.has.mockReturnValue(true); @@ -108,12 +106,7 @@ describe('alert_form', () => { actionTypeRegistry.has.mockReturnValue(true); actionTypeRegistry.get.mockReturnValue(actionType); - const monitoringDependencies = { - toastNotifications: coreStart.notifications.toasts, - ...Legacy.shims.kibanaServices, - actionTypeRegistry, - alertTypeRegistry, - } as any; + const KibanaReactContext = createKibanaReactContext(Legacy.shims.kibanaServices); const initialAlert = ({ name: 'test', @@ -131,18 +124,18 @@ describe('alert_form', () => { } as unknown) as Alert; wrapper = mountWithIntl( - - {}} - errors={{ name: [], interval: [] }} - operation="create" - /> - + + + {}} + errors={{ name: [], interval: [] }} + operation="create" + actionTypeRegistry={actionTypeRegistry} + alertTypeRegistry={alertTypeRegistry} + /> + + ); await act(async () => { diff --git a/x-pack/plugins/monitoring/public/alerts/panel.tsx b/x-pack/plugins/monitoring/public/alerts/panel.tsx index 99db6c8b3c94..fd09a3f9a627 100644 --- a/x-pack/plugins/monitoring/public/alerts/panel.tsx +++ b/x-pack/plugins/monitoring/public/alerts/panel.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment } from 'react'; +import React, { Fragment, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { @@ -21,8 +21,6 @@ import { import { CommonAlertStatus, CommonAlertState, AlertMessage } from '../../common/types/alerts'; import { Legacy } from '../legacy_shims'; import { replaceTokens } from './lib/replace_tokens'; -import { AlertsContextProvider } from '../../../triggers_actions_ui/public'; -import { AlertEdit } from '../../../triggers_actions_ui/public'; import { isInSetupMode, hideBottomBar, showBottomBar } from '../lib/setup_mode'; import { BASE_ALERT_API_PATH } from '../../../alerts/common'; import { SetupModeContext } from '../components/setup_mode/setup_mode_context'; @@ -44,6 +42,20 @@ export const AlertPanel: React.FC = (props: Props) => { const [isSaving, setIsSaving] = React.useState(false); const inSetupMode = isInSetupMode(React.useContext(SetupModeContext)); + const flyoutUi = useMemo( + () => + showFlyout && + Legacy.shims.triggersActionsUi.getEditAlertFlyout({ + initialAlert: alert.rawAlert, + onClose: () => { + setShowFlyout(false); + showBottomBar(); + }, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [showFlyout] + ); + if (!alert.rawAlert) { return null; } @@ -105,29 +117,6 @@ export const AlertPanel: React.FC = (props: Props) => { setIsSaving(false); } - const flyoutUi = showFlyout ? ( - {}, - capabilities: Legacy.shims.capabilities, - }} - > - { - setShowFlyout(false); - showBottomBar(); - }} - /> - - ) : null; - const configurationUi = ( diff --git a/x-pack/plugins/monitoring/public/legacy_shims.ts b/x-pack/plugins/monitoring/public/legacy_shims.ts index f2af4bd0b19a..12a971391f62 100644 --- a/x-pack/plugins/monitoring/public/legacy_shims.ts +++ b/x-pack/plugins/monitoring/public/legacy_shims.ts @@ -8,7 +8,7 @@ import { CoreStart, HttpSetup, IUiSettingsClient } from 'kibana/public'; import { Observable } from 'rxjs'; import { HttpRequestInit } from '../../../../src/core/public'; import { MonitoringStartPluginDependencies } from './types'; -import { TriggersAndActionsUIPublicPluginSetup } from '../../triggers_actions_ui/public'; +import { TriggersAndActionsUIPublicPluginStart } from '../../triggers_actions_ui/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { TypeRegistry } from '../../triggers_actions_ui/public/application/type_registry'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths @@ -59,7 +59,7 @@ export interface IShims { kfetchOptions?: KFetchKibanaOptions | undefined ) => Promise; isCloud: boolean; - triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; usageCollection: UsageCollectionSetup; kibanaServices: CoreStart & { usageCollection: UsageCollectionSetup }; } diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts index a228c540761b..0439b47569e7 100644 --- a/x-pack/plugins/monitoring/public/plugin.ts +++ b/x-pack/plugins/monitoring/public/plugin.ts @@ -93,7 +93,7 @@ export class MonitoringPlugin isCloud: Boolean(plugins.cloud?.isCloudEnabled), pluginInitializerContext: this.initializerContext, externalConfig: this.getExternalConfig(), - triggersActionsUi: plugins.triggersActionsUi, + triggersActionsUi: pluginsStart.triggersActionsUi, usageCollection: plugins.usageCollection, }; diff --git a/x-pack/plugins/monitoring/public/types.ts b/x-pack/plugins/monitoring/public/types.ts index 238af7276d58..db872691a5e7 100644 --- a/x-pack/plugins/monitoring/public/types.ts +++ b/x-pack/plugins/monitoring/public/types.ts @@ -7,7 +7,7 @@ import { PluginInitializerContext, CoreStart } from 'kibana/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; -import { TriggersAndActionsUIPublicPluginSetup } from '../../triggers_actions_ui/public'; +import { TriggersAndActionsUIPublicPluginStart } from '../../triggers_actions_ui/public'; import { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; @@ -23,6 +23,6 @@ export interface MonitoringStartPluginDependencies { isCloud: boolean; pluginInitializerContext: PluginInitializerContext; externalConfig: Array | Array>; - triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; usageCollection: UsageCollectionSetup; } diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/index.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/index.ts index d3b5f14dcc9e..59effdbf8f51 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/index.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/index.ts @@ -7,9 +7,9 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { validateExpression } from './validation'; import { GeoContainmentAlertParams } from './types'; -import { AlertTypeModel, AlertsContextValue } from '../../../../triggers_actions_ui/public'; +import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; -export function getAlertType(): AlertTypeModel { +export function getAlertType(): AlertTypeModel { return { id: '.geo-containment', name: i18n.translate('xpack.stackAlerts.geoContainment.name.trackingContainment', { diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/__snapshots__/geo_containment_alert_type_expression.test.tsx.snap b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/__snapshots__/geo_containment_alert_type_expression.test.tsx.snap index cc8395455d89..535c883aed53 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/__snapshots__/geo_containment_alert_type_expression.test.tsx.snap +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/__snapshots__/geo_containment_alert_type_expression.test.tsx.snap @@ -16,13 +16,23 @@ exports[`should render BoundaryIndexExpression 1`] = ` labelType="label" > @@ -82,13 +92,23 @@ exports[`should render EntityIndexExpression 1`] = ` labelType="label" > @@ -154,13 +174,23 @@ exports[`should render EntityIndexExpression w/ invalid flag if invalid 1`] = ` labelType="label" > diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/boundary_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/boundary_index_expression.tsx index a6a5aeb366cc..f875e6179d82 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/boundary_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/boundary_index_expression.tsx @@ -7,7 +7,10 @@ import React, { Fragment, FunctionComponent, useEffect, useRef } from 'react'; import { EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { IErrorObject, AlertsContextValue } from '../../../../../../triggers_actions_ui/public'; +import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { HttpSetup } from 'kibana/public'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { IErrorObject } from '../../../../../../triggers_actions_ui/public'; import { ES_GEO_SHAPE_TYPES, GeoContainmentAlertParams } from '../../types'; import { GeoIndexPatternSelect } from '../util_components/geo_index_pattern_select'; import { SingleFieldSelect } from '../util_components/single_field_select'; @@ -17,29 +20,33 @@ import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/i interface Props { alertParams: GeoContainmentAlertParams; - alertsContext: AlertsContextValue; errors: IErrorObject; boundaryIndexPattern: IIndexPattern; boundaryNameField?: string; setBoundaryIndexPattern: (boundaryIndexPattern?: IIndexPattern) => void; setBoundaryGeoField: (boundaryGeoField?: string) => void; setBoundaryNameField: (boundaryNameField?: string) => void; + data: DataPublicPluginStart; +} + +interface KibanaDeps { + http: HttpSetup; } export const BoundaryIndexExpression: FunctionComponent = ({ alertParams, - alertsContext, errors, boundaryIndexPattern, boundaryNameField, setBoundaryIndexPattern, setBoundaryGeoField, setBoundaryNameField, + data, }) => { // eslint-disable-next-line react-hooks/exhaustive-deps const BOUNDARY_NAME_ENTITY_TYPES = ['string', 'number', 'ip']; - const { dataUi, dataIndexPatterns, http } = alertsContext; - const IndexPatternSelect = (dataUi && dataUi.IndexPatternSelect) || null; + const { http } = useKibana().services; + const IndexPatternSelect = (data.ui && data.ui.IndexPatternSelect) || null; const { boundaryGeoField } = alertParams; // eslint-disable-next-line react-hooks/exhaustive-deps const nothingSelected: IFieldType = { @@ -110,7 +117,7 @@ export const BoundaryIndexExpression: FunctionComponent = ({ }} value={boundaryIndexPattern.id} IndexPatternSelectComponent={IndexPatternSelect} - indexPatternService={dataIndexPatterns} + indexPatternService={data.indexPatterns} http={http} includedGeoTypes={ES_GEO_SHAPE_TYPES} /> diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_index_expression.tsx index 76edeac06ac9..26b8a1a5165f 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_index_expression.tsx @@ -8,9 +8,11 @@ import React, { Fragment, FunctionComponent, useEffect, useRef } from 'react'; import { EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { HttpSetup } from 'kibana/public'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { IErrorObject, - AlertsContextValue, AlertTypeParamsExpressionProps, } from '../../../../../../triggers_actions_ui/public'; import { ES_GEO_FIELD_TYPES } from '../../types'; @@ -23,7 +25,6 @@ import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/i interface Props { dateField: string; geoField: string; - alertsContext: AlertsContextValue; errors: IErrorObject; setAlertParamsDate: (date: string) => void; setAlertParamsGeoField: (geoField: string) => void; @@ -31,21 +32,26 @@ interface Props { setIndexPattern: (indexPattern: IIndexPattern) => void; indexPattern: IIndexPattern; isInvalid: boolean; + data: DataPublicPluginStart; +} + +interface KibanaDeps { + http: HttpSetup; } export const EntityIndexExpression: FunctionComponent = ({ setAlertParamsDate, setAlertParamsGeoField, errors, - alertsContext, setIndexPattern, indexPattern, isInvalid, dateField: timeField, geoField, + data, }) => { - const { dataUi, dataIndexPatterns, http } = alertsContext; - const IndexPatternSelect = (dataUi && dataUi.IndexPatternSelect) || null; + const { http } = useKibana().services; + const IndexPatternSelect = (data.ui && data.ui.IndexPatternSelect) || null; const usePrevious = (value: T): T | undefined => { const ref = useRef(); @@ -98,7 +104,7 @@ export const EntityIndexExpression: FunctionComponent = ({ }} value={indexPattern.id} IndexPatternSelectComponent={IndexPatternSelect} - indexPatternService={dataIndexPatterns} + indexPatternService={data.indexPatterns} http={http} includedGeoTypes={ES_GEO_FIELD_TYPES} /> diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/geo_containment_alert_type_expression.test.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/geo_containment_alert_type_expression.test.tsx index c35427bc6bc0..0c5f121943e0 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/geo_containment_alert_type_expression.test.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/geo_containment_alert_type_expression.test.tsx @@ -8,22 +8,11 @@ import React from 'react'; import { shallow } from 'enzyme'; import { EntityIndexExpression } from './expressions/entity_index_expression'; import { BoundaryIndexExpression } from './expressions/boundary_index_expression'; -import { ApplicationStart, DocLinksStart, HttpSetup, ToastsStart } from 'kibana/public'; -import { - ActionTypeRegistryContract, - AlertTypeRegistryContract, - IErrorObject, -} from '../../../../../triggers_actions_ui/public'; +import { IErrorObject } from '../../../../../triggers_actions_ui/public'; import { IIndexPattern } from '../../../../../../../src/plugins/data/common'; +import { dataPluginMock } from 'src/plugins/data/public/mocks'; -const alertsContext = { - http: (null as unknown) as HttpSetup, - alertTypeRegistry: (null as unknown) as AlertTypeRegistryContract, - actionTypeRegistry: (null as unknown) as ActionTypeRegistryContract, - toastNotifications: (null as unknown) as ToastsStart, - docLinks: (null as unknown) as DocLinksStart, - capabilities: (null as unknown) as ApplicationStart['capabilities'], -}; +const dataStartMock = dataPluginMock.createStartContract(); const alertParams = { index: '', @@ -42,7 +31,6 @@ test('should render EntityIndexExpression', async () => { {}} setAlertParamsGeoField={() => {}} @@ -50,6 +38,7 @@ test('should render EntityIndexExpression', async () => { setIndexPattern={() => {}} indexPattern={('' as unknown) as IIndexPattern} isInvalid={false} + data={dataStartMock} /> ); @@ -61,7 +50,6 @@ test('should render EntityIndexExpression w/ invalid flag if invalid', async () {}} setAlertParamsGeoField={() => {}} @@ -69,6 +57,7 @@ test('should render EntityIndexExpression w/ invalid flag if invalid', async () setIndexPattern={() => {}} indexPattern={('' as unknown) as IIndexPattern} isInvalid={true} + data={dataStartMock} /> ); @@ -79,13 +68,13 @@ test('should render BoundaryIndexExpression', async () => { const component = shallow( {}} setBoundaryGeoField={() => {}} setBoundaryNameField={() => {}} boundaryNameField={'testNameField'} + data={dataStartMock} /> ); diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx index 1c0b712566d5..cb8fd9a95e7c 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx @@ -8,10 +8,7 @@ import React, { Fragment, useEffect, useState } from 'react'; import { EuiCallOut, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { - AlertTypeParamsExpressionProps, - AlertsContextValue, -} from '../../../../../triggers_actions_ui/public'; +import { AlertTypeParamsExpressionProps } from '../../../../../triggers_actions_ui/public'; import { GeoContainmentAlertParams } from '../types'; import { EntityIndexExpression } from './expressions/entity_index_expression'; import { EntityByExpression } from './expressions/entity_by_expression'; @@ -52,8 +49,8 @@ function validateQuery(query: Query) { } export const GeoContainmentAlertTypeExpression: React.FunctionComponent< - AlertTypeParamsExpressionProps -> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, alertsContext }) => { + AlertTypeParamsExpressionProps +> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, data }) => { const { index, indexId, @@ -137,15 +134,15 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< boundaryGeoField: boundaryGeoField ?? DEFAULT_VALUES.BOUNDARY_GEO_FIELD, boundaryNameField: boundaryNameField ?? DEFAULT_VALUES.BOUNDARY_NAME_FIELD, }); - if (!alertsContext.dataIndexPatterns) { + if (!data.indexPatterns) { return; } if (indexId) { - const _indexPattern = await alertsContext.dataIndexPatterns.get(indexId); + const _indexPattern = await data.indexPatterns.get(indexId); setIndexPattern(_indexPattern); } if (boundaryIndexId) { - const _boundaryIndexPattern = await alertsContext.dataIndexPatterns.get(boundaryIndexId); + const _boundaryIndexPattern = await data.indexPatterns.get(boundaryIndexId); setBoundaryIndexPattern(_boundaryIndexPattern); } }; @@ -175,7 +172,6 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< setAlertParams('dateField', _date)} setAlertParamsGeoField={(_geoField) => setAlertParams('geoField', _geoField)} @@ -183,6 +179,7 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< setIndexPattern={setIndexPattern} indexPattern={indexPattern} isInvalid={!indexId || !dateField || !geoField} + data={data} /> diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/index.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/index.ts index 35f5648de40f..cc8d78b53137 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/index.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/index.ts @@ -7,9 +7,9 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { validateExpression } from './validation'; import { GeoThresholdAlertParams } from './types'; -import { AlertTypeModel, AlertsContextValue } from '../../../../triggers_actions_ui/public'; +import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; -export function getAlertType(): AlertTypeModel { +export function getAlertType(): AlertTypeModel { return { id: '.geo-threshold', name: i18n.translate('xpack.stackAlerts.geoThreshold.name.trackingThreshold', { diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/__snapshots__/geo_threshold_alert_type_expression.test.tsx.snap b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/__snapshots__/geo_threshold_alert_type_expression.test.tsx.snap index dae168417b0b..127dd2f3b847 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/__snapshots__/geo_threshold_alert_type_expression.test.tsx.snap +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/__snapshots__/geo_threshold_alert_type_expression.test.tsx.snap @@ -16,13 +16,23 @@ exports[`should render BoundaryIndexExpression 1`] = ` labelType="label" > @@ -82,13 +92,23 @@ exports[`should render EntityIndexExpression 1`] = ` labelType="label" > @@ -154,13 +174,23 @@ exports[`should render EntityIndexExpression w/ invalid flag if invalid 1`] = ` labelType="label" > diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx index 6433845370ff..93918c82d664 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx @@ -7,7 +7,10 @@ import React, { Fragment, FunctionComponent, useEffect, useRef } from 'react'; import { EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { IErrorObject, AlertsContextValue } from '../../../../../../triggers_actions_ui/public'; +import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { HttpSetup } from 'kibana/public'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { IErrorObject } from '../../../../../../triggers_actions_ui/public'; import { ES_GEO_SHAPE_TYPES, GeoThresholdAlertParams } from '../../types'; import { GeoIndexPatternSelect } from '../util_components/geo_index_pattern_select'; import { SingleFieldSelect } from '../util_components/single_field_select'; @@ -17,29 +20,33 @@ import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/i interface Props { alertParams: GeoThresholdAlertParams; - alertsContext: AlertsContextValue; errors: IErrorObject; boundaryIndexPattern: IIndexPattern; boundaryNameField?: string; setBoundaryIndexPattern: (boundaryIndexPattern?: IIndexPattern) => void; setBoundaryGeoField: (boundaryGeoField?: string) => void; setBoundaryNameField: (boundaryNameField?: string) => void; + data: DataPublicPluginStart; +} + +interface KibanaDeps { + http: HttpSetup; } export const BoundaryIndexExpression: FunctionComponent = ({ alertParams, - alertsContext, errors, boundaryIndexPattern, boundaryNameField, setBoundaryIndexPattern, setBoundaryGeoField, setBoundaryNameField, + data, }) => { // eslint-disable-next-line react-hooks/exhaustive-deps const BOUNDARY_NAME_ENTITY_TYPES = ['string', 'number', 'ip']; - const { dataUi, dataIndexPatterns, http } = alertsContext; - const IndexPatternSelect = (dataUi && dataUi.IndexPatternSelect) || null; + const { http } = useKibana().services; + const IndexPatternSelect = (data.ui && data.ui.IndexPatternSelect) || null; const { boundaryGeoField } = alertParams; // eslint-disable-next-line react-hooks/exhaustive-deps const nothingSelected: IFieldType = { @@ -110,7 +117,7 @@ export const BoundaryIndexExpression: FunctionComponent = ({ }} value={boundaryIndexPattern.id} IndexPatternSelectComponent={IndexPatternSelect} - indexPatternService={dataIndexPatterns} + indexPatternService={data.indexPatterns} http={http} includedGeoTypes={ES_GEO_SHAPE_TYPES} /> diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx index 0a722734ffc5..aea1f2bbf56a 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx @@ -8,9 +8,10 @@ import React, { Fragment, FunctionComponent, useEffect, useRef } from 'react'; import { EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { IErrorObject, - AlertsContextValue, AlertTypeParamsExpressionProps, } from '../../../../../../triggers_actions_ui/public'; import { ES_GEO_FIELD_TYPES } from '../../types'; @@ -23,7 +24,6 @@ import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/i interface Props { dateField: string; geoField: string; - alertsContext: AlertsContextValue; errors: IErrorObject; setAlertParamsDate: (date: string) => void; setAlertParamsGeoField: (geoField: string) => void; @@ -31,21 +31,22 @@ interface Props { setIndexPattern: (indexPattern: IIndexPattern) => void; indexPattern: IIndexPattern; isInvalid: boolean; + data: DataPublicPluginStart; } export const EntityIndexExpression: FunctionComponent = ({ setAlertParamsDate, setAlertParamsGeoField, errors, - alertsContext, setIndexPattern, indexPattern, isInvalid, dateField: timeField, geoField, + data, }) => { - const { dataUi, dataIndexPatterns, http } = alertsContext; - const IndexPatternSelect = (dataUi && dataUi.IndexPatternSelect) || null; + const { http } = useKibana().services; + const IndexPatternSelect = (data.ui && data.ui.IndexPatternSelect) || null; const usePrevious = (value: T): T | undefined => { const ref = useRef(); @@ -98,8 +99,8 @@ export const EntityIndexExpression: FunctionComponent = ({ }} value={indexPattern.id} IndexPatternSelectComponent={IndexPatternSelect} - indexPatternService={dataIndexPatterns} - http={http} + indexPatternService={data.indexPatterns} + http={http!} includedGeoTypes={ES_GEO_FIELD_TYPES} /> diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/geo_threshold_alert_type_expression.test.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/geo_threshold_alert_type_expression.test.tsx index d115dbeb76e3..c8158b0a6fea 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/geo_threshold_alert_type_expression.test.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/geo_threshold_alert_type_expression.test.tsx @@ -8,22 +8,9 @@ import React from 'react'; import { shallow } from 'enzyme'; import { EntityIndexExpression } from './expressions/entity_index_expression'; import { BoundaryIndexExpression } from './expressions/boundary_index_expression'; -import { ApplicationStart, DocLinksStart, HttpSetup, ToastsStart } from 'kibana/public'; -import { - ActionTypeRegistryContract, - AlertTypeRegistryContract, - IErrorObject, -} from '../../../../../triggers_actions_ui/public'; +import { IErrorObject } from '../../../../../triggers_actions_ui/public'; import { IIndexPattern } from '../../../../../../../src/plugins/data/common'; - -const alertsContext = { - http: (null as unknown) as HttpSetup, - alertTypeRegistry: (null as unknown) as AlertTypeRegistryContract, - actionTypeRegistry: (null as unknown) as ActionTypeRegistryContract, - toastNotifications: (null as unknown) as ToastsStart, - docLinks: (null as unknown) as DocLinksStart, - capabilities: (null as unknown) as ApplicationStart['capabilities'], -}; +import { dataPluginMock } from 'src/plugins/data/public/mocks'; const alertParams = { index: '', @@ -38,12 +25,13 @@ const alertParams = { boundaryGeoField: '', }; +const dataStartMock = dataPluginMock.createStartContract(); + test('should render EntityIndexExpression', async () => { const component = shallow( {}} setAlertParamsGeoField={() => {}} @@ -51,6 +39,7 @@ test('should render EntityIndexExpression', async () => { setIndexPattern={() => {}} indexPattern={('' as unknown) as IIndexPattern} isInvalid={false} + data={dataStartMock} /> ); @@ -62,7 +51,6 @@ test('should render EntityIndexExpression w/ invalid flag if invalid', async () {}} setAlertParamsGeoField={() => {}} @@ -70,6 +58,7 @@ test('should render EntityIndexExpression w/ invalid flag if invalid', async () setIndexPattern={() => {}} indexPattern={('' as unknown) as IIndexPattern} isInvalid={true} + data={dataStartMock} /> ); @@ -80,13 +69,13 @@ test('should render BoundaryIndexExpression', async () => { const component = shallow( {}} setBoundaryGeoField={() => {}} setBoundaryNameField={() => {}} boundaryNameField={'testNameField'} + data={dataStartMock} /> ); diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx index a34b367668ec..2a08a4b32f07 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx @@ -22,7 +22,6 @@ import { i18n } from '@kbn/i18n'; import { AlertTypeParamsExpressionProps, getTimeOptions, - AlertsContextValue, } from '../../../../../triggers_actions_ui/public'; import { GeoThresholdAlertParams, TrackingEvent } from '../types'; import { ExpressionWithPopover } from './util_components/expression_with_popover'; @@ -86,8 +85,8 @@ function validateQuery(query: Query) { } export const GeoThresholdAlertTypeExpression: React.FunctionComponent< - AlertTypeParamsExpressionProps -> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, alertsContext }) => { + AlertTypeParamsExpressionProps +> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, data }) => { const { index, indexId, @@ -181,15 +180,15 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent< boundaryNameField: boundaryNameField ?? DEFAULT_VALUES.BOUNDARY_NAME_FIELD, delayOffsetWithUnits: delayOffsetWithUnits ?? DEFAULT_VALUES.DELAY_OFFSET_WITH_UNITS, }); - if (!alertsContext.dataIndexPatterns) { + if (!data?.indexPatterns) { return; } if (indexId) { - const _indexPattern = await alertsContext.dataIndexPatterns.get(indexId); + const _indexPattern = await data?.indexPatterns.get(indexId); setIndexPattern(_indexPattern); } if (boundaryIndexId) { - const _boundaryIndexPattern = await alertsContext.dataIndexPatterns.get(boundaryIndexId); + const _boundaryIndexPattern = await data?.indexPatterns.get(boundaryIndexId); setBoundaryIndexPattern(_boundaryIndexPattern); } if (delayOffsetWithUnits) { @@ -263,7 +262,6 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent< setAlertParams('dateField', _date)} setAlertParamsGeoField={(_geoField) => setAlertParams('geoField', _geoField)} @@ -271,6 +269,7 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent< setIndexPattern={setIndexPattern} indexPattern={indexPattern} isInvalid={!indexId || !dateField || !geoField} + data={data} /> diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx index 274cd5a9d20d..3c84f2a5d4f9 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx @@ -24,6 +24,8 @@ import { EuiTitle, } from '@elastic/eui'; import { EuiButtonIcon } from '@elastic/eui'; +import { HttpSetup } from 'kibana/public'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { firstFieldOption, getIndexPatterns, @@ -39,7 +41,6 @@ import { WhenExpression, builtInAggregationTypes, AlertTypeParamsExpressionProps, - AlertsContextValue, } from '../../../../triggers_actions_ui/public'; import { ThresholdVisualization } from './visualization'; import { IndexThresholdAlertParams } from './types'; @@ -66,9 +67,13 @@ const expressionFieldsWithValidation = [ 'timeWindowSize', ]; +interface KibanaDeps { + http: HttpSetup; +} + export const IndexThresholdAlertTypeExpression: React.FunctionComponent< - AlertTypeParamsExpressionProps -> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, alertsContext }) => { + AlertTypeParamsExpressionProps +> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, charts, data }) => { const { index, timeField, @@ -83,7 +88,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< timeWindowUnit, } = alertParams; - const { http } = alertsContext; + const { http } = useKibana().services; const [indexPopoverOpen, setIndexPopoverOpen] = useState(false); const [indexPatterns, setIndexPatterns] = useState([]); @@ -208,7 +213,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< }); return; } - const currentEsFields = await getFields(http, indices); + const currentEsFields = await getFields(http!, indices); const timeFields = getTimeFieldOptions(currentEsFields); setEsFields(currentEsFields); @@ -216,7 +221,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< }} onSearchChange={async (search) => { setIsIndiciesLoading(true); - setIndexOptions(await getIndexOptions(http, search, indexPatterns)); + setIndexOptions(await getIndexOptions(http!, search, indexPatterns)); setIsIndiciesLoading(false); }} onBlur={() => { @@ -433,7 +438,8 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< alertInterval={alertInterval} aggregationTypes={builtInAggregationTypes} comparators={builtInComparators} - alertsContext={alertsContext} + charts={charts} + dataFieldsFormats={data!.fieldFormats} /> )} diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/index.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/index.ts index 93a8011de7e0..f09d1630cd67 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/index.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/index.ts @@ -7,9 +7,9 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { validateExpression } from './validation'; import { IndexThresholdAlertParams } from './types'; -import { AlertTypeModel, AlertsContextValue } from '../../../../triggers_actions_ui/public'; +import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; -export function getAlertType(): AlertTypeModel { +export function getAlertType(): AlertTypeModel { return { id: '.index-threshold', name: i18n.translate('xpack.stackAlerts.threshold.ui.alertType.nameText', { diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.tsx index 6145aa3671a7..a3f27d7efb71 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.tsx @@ -29,15 +29,14 @@ import { EuiLoadingSpinner, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { ChartsPluginSetup } from 'src/plugins/charts/public'; +import { FieldFormatsStart } from 'src/plugins/data/public'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { getThresholdAlertVisualizationData, GetThresholdAlertVisualizationDataParams, } from './index_threshold_api'; -import { - AlertsContextValue, - AggregationType, - Comparator, -} from '../../../../triggers_actions_ui/public'; +import { AggregationType, Comparator } from '../../../../triggers_actions_ui/public'; import { IndexThresholdAlertParams } from './types'; import { parseDuration } from '../../../../alerts/common/parse_duration'; @@ -94,8 +93,9 @@ interface Props { comparators: { [key: string]: Comparator; }; - alertsContext: AlertsContextValue; refreshRateInMilliseconds?: number; + charts: ChartsPluginSetup; + dataFieldsFormats: FieldFormatsStart; } const DEFAULT_REFRESH_RATE = 5000; @@ -112,8 +112,9 @@ export const ThresholdVisualization: React.FunctionComponent = ({ alertInterval, aggregationTypes, comparators, - alertsContext, refreshRateInMilliseconds = DEFAULT_REFRESH_RATE, + charts, + dataFieldsFormats, }) => { const { index, @@ -128,8 +129,7 @@ export const ThresholdVisualization: React.FunctionComponent = ({ groupBy, threshold, } = alertParams; - const { http, toastNotifications, charts, uiSettings, dataFieldsFormats } = alertsContext; - + const { http, notifications, uiSettings } = useKibana().services; const [loadingState, setLoadingState] = useState(null); const [error, setError] = useState(undefined); const [visualizationData, setVisualizationData] = useState>(); @@ -150,11 +150,11 @@ export const ThresholdVisualization: React.FunctionComponent = ({ try { setLoadingState(loadingState ? LoadingStateType.Refresh : LoadingStateType.FirstLoad); setVisualizationData( - await getVisualizationData(alertWithoutActions, visualizeOptions, http) + await getVisualizationData(alertWithoutActions, visualizeOptions, http!) ); } catch (e) { - if (toastNotifications) { - toastNotifications.addDanger({ + if (notifications) { + notifications.toasts.addDanger({ title: i18n.translate( 'xpack.stackAlerts.threshold.ui.visualization.unableToLoadVisualizationMessage', { defaultMessage: 'Unable to load visualization' } diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index 28667741801f..2d80dda964a0 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -86,7 +86,6 @@ interface IndexThresholdProps { setAlertParams: (property: string, value: any) => void; setAlertProperty: (key: string, value: any) => void; errors: { [key: string]: string[] }; - alertsContext: AlertsContextValue; } ``` @@ -96,7 +95,6 @@ interface IndexThresholdProps { |setAlertParams|Alert reducer method, which is used to create a new copy of alert object with the changed params property any subproperty value.| |setAlertProperty|Alert reducer method, which is used to create a new copy of alert object with the changed any direct alert property value.| |errors|Alert level errors tracking object.| -|alertsContext|Alert context, which is used to pass down common objects like http client.| Alert reducer is defined on the AlertAdd functional component level and passed down to the subcomponents to provide a new state of Alert object: @@ -181,7 +179,6 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent { .... @@ -244,7 +241,7 @@ Each alert type should be defined as `AlertTypeModel` object with the these prop iconClass: string; validate: (alertParams: any) => ValidationResult; alertParamsExpression: React.LazyExoticComponent< - ComponentType> + ComponentType> >; defaultActionMessage?: string; ``` @@ -787,18 +784,15 @@ This component renders a standard EuiTitle foe each action group, wrapping the A ## Embed the Create Alert flyout within any Kibana plugin Follow the instructions bellow to embed the Create Alert flyout within any Kibana plugin: -1. Add TriggersAndActionsUIPublicPluginSetup to Kibana plugin setup dependencies: +1. Add TriggersAndActionsUIPublicPluginStart to Kibana plugin setup dependencies: ``` -triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; +triggersActionsUi: TriggersAndActionsUIPublicPluginStart; ``` -Then this dependency will be used to embed Create Alert flyout or register new alert/action type. +Then this dependency will be used to embed Create Alert flyout. -2. Add Create Alert flyout to React component: +2. Add Create Alert flyout to React component using triggersActionsUi start contract: ``` -// import section -import { AlertsContextProvider, AlertAdd } from '../../../../../../../triggers_actions_ui/public'; - // in the component state definition section const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState(false); @@ -815,26 +809,22 @@ const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState(false); /> +const AddAlertFlyout = useMemo( + () => + triggersActionsUi.getAddAlertFlyout({ + consumer: ALERTING_EXAMPLE_APP_ID, + addFlyoutVisible: alertFlyoutVisible, + setAddFlyoutVisibility: setAlertFlyoutVisibility, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [alertFlyoutVisible] +); + // in render section of component - - - + return <>{AddAlertFlyout}; ``` -AlertAdd Props definition: +getAddAlertFlyout variables definition: ``` interface AlertAddProps { consumer: string; @@ -842,6 +832,9 @@ interface AlertAddProps { setAddFlyoutVisibility: React.Dispatch>; alertTypeId?: string; canChangeTrigger?: boolean; + initialValues?: Partial; + reloadAlerts?: () => Promise; + metadata?: MetaData; } ``` @@ -852,37 +845,8 @@ interface AlertAddProps { |setAddFlyoutVisibility|Function for changing visibility state of the Create Alert flyout.| |alertTypeId|Optional property to preselect alert type.| |canChangeTrigger|Optional property, that hides change alert type possibility.| - -AlertsContextProvider value options: -``` -export interface AlertsContextValue> { - reloadAlerts?: () => Promise; - http: HttpSetup; - alertTypeRegistry: TypeRegistry; - actionTypeRegistry: TypeRegistry; - uiSettings?: IUiSettingsClient; - docLinks: DocLinksStart; - toastNotifications: Pick< - ToastsApi, - 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' - >; - charts?: ChartsPluginSetup; - dataFieldsFormats?: Pick; - metadata?: MetaData; -} -``` - -|Property|Description| -|---|---| |reloadAlerts|Optional function, which will be executed if alert was saved sucsessfuly.| -|http|HttpSetup needed for executing API calls.| -|alertTypeRegistry|Registry for alert types.| -|actionTypeRegistry|Registry for action types.| -|uiSettings|Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring.| -|docLinks|Documentation Links, needed to link to the documentation from informational callouts.| -|toastNotifications|Toast messages.| -|charts|Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring.| -|dataFieldsFormats|Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring.| +|initialValues|Default values for Alert properties.| |metadata|Optional generic property, which allows to define component specific metadata. This metadata can be used for passing down preloaded data for Alert type expression component.| ## Build and register Action Types @@ -1504,25 +1468,6 @@ interface ActionAccordionFormProps { |defaultActionMessage|Optional property, which allowes to define a message value for action with 'message' property.| |capabilities|Kibana core's Capabilities ApplicationStart['capabilities'].| - -AlertsContextProvider value options: -``` -export interface AlertsContextValue { - reloadAlerts?: () => Promise; - http: HttpSetup; - alertTypeRegistry: TypeRegistry; - actionTypeRegistry: TypeRegistry; - uiSettings?: IUiSettingsClient; - docLinks: DocLinksStart; - toastNotifications: Pick< - ToastsApi, - 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' - >; - charts?: ChartsPluginSetup; - dataFieldsFormats?: Pick; -} -``` - |Property|Description| |---|---| |reloadAlerts|Optional function, which will be executed if alert was saved sucsessfuly.| @@ -1618,32 +1563,6 @@ export interface ConnectorAddFlyoutProps { |setAddFlyoutVisibility|Function for changing visibility state of the Create Connector flyout.| |actionTypes|Optional property, that allows to define only specific action types list which is available for a current plugin.| -ActionsConnectorsContextValue options: -``` -export interface ActionsConnectorsContextValue { - http: HttpSetup; - actionTypeRegistry: TypeRegistry; - toastNotifications: Pick< - ToastsApi, - 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' - >; - capabilities: ApplicationStart['capabilities']; - docLinks: DocLinksStart; - reloadConnectors?: () => Promise; - consumer: string; -} -``` - -|Property|Description| -|---|---| -|http|HttpSetup needed for executing API calls.| -|actionTypeRegistry|Registry for action types.| -|capabilities|Property, which is defining action current user usage capabilities like canSave or canDelete.| -|toastNotifications|Toast messages.| -|reloadConnectors|Optional function, which will be executed if connector was saved sucsessfuly, like reload list of connecotrs.| -|consumer|Optional name of the plugin that creates an action.| - - ## Embed the Edit Connector flyout within any Kibana plugin Follow the instructions bellow to embed the Edit Connector flyout within any Kibana plugin: diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx index a51765fc3a72..be6c72eef6f9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx @@ -9,20 +9,20 @@ import { render } from '@testing-library/react'; import { HealthCheck } from './health_check'; import { act } from 'react-dom/test-utils'; -import { httpServiceMock } from '../../../../../../src/core/public/mocks'; import { HealthContextProvider } from '../context/health_context'; +import { useKibana } from '../../common/lib/kibana'; +jest.mock('../../common/lib/kibana'); -const docLinks = { ELASTIC_WEBSITE_URL: 'elastic.co/', DOC_LINK_VERSION: 'current' }; - -const http = httpServiceMock.createStartContract(); +const useKibanaMock = useKibana as jest.Mocked; describe('health check', () => { test('renders spinner while health is loading', async () => { - http.get.mockImplementationOnce(() => new Promise(() => {})); - + useKibanaMock().services.http.get = jest + .fn() + .mockImplementationOnce(() => new Promise(() => {})); const { queryByText, container } = render( - +

{'shouldnt render'}

@@ -36,11 +36,13 @@ describe('health check', () => { }); it('renders children immediately if waitForCheck is false', async () => { - http.get.mockImplementationOnce(() => new Promise(() => {})); + useKibanaMock().services.http.get = jest + .fn() + .mockImplementationOnce(() => new Promise(() => {})); const { queryByText, container } = render( - +

{'should render'}

@@ -54,11 +56,12 @@ describe('health check', () => { }); it('renders children if keys are enabled', async () => { - http.get.mockResolvedValue({ isSufficientlySecure: true, hasPermanentEncryptionKey: true }); - + useKibanaMock().services.http.get = jest + .fn() + .mockResolvedValue({ isSufficientlySecure: true, hasPermanentEncryptionKey: true }); const { queryByText } = render( - +

{'should render'}

@@ -70,14 +73,13 @@ describe('health check', () => { }); test('renders warning if keys are disabled', async () => { - http.get.mockImplementationOnce(async () => ({ + useKibanaMock().services.http.get = jest.fn().mockImplementationOnce(async () => ({ isSufficientlySecure: false, hasPermanentEncryptionKey: true, })); - const { queryAllByText } = render( - +

{'should render'}

@@ -97,19 +99,18 @@ describe('health check', () => { ); expect(action.getAttribute('href')).toMatchInlineSnapshot( - `"elastic.co/guide/en/kibana/current/configuring-tls.html"` + `"https://www.elastic.co/guide/en/kibana/mocked-test-branch/configuring-tls.html"` ); }); test('renders warning if encryption key is ephemeral', async () => { - http.get.mockImplementationOnce(async () => ({ + useKibanaMock().services.http.get = jest.fn().mockImplementationOnce(async () => ({ isSufficientlySecure: true, hasPermanentEncryptionKey: false, })); - const { queryByText, queryByRole } = render( - +

{'should render'}

@@ -126,19 +127,19 @@ describe('health check', () => { const action = queryByText(/Learn/i); expect(action!.textContent).toMatchInlineSnapshot(`"Learn how.(opens in a new tab or window)"`); expect(action!.getAttribute('href')).toMatchInlineSnapshot( - `"elastic.co/guide/en/kibana/current/alert-action-settings-kb.html#general-alert-action-settings"` + `"https://www.elastic.co/guide/en/kibana/mocked-test-branch/alert-action-settings-kb.html#general-alert-action-settings"` ); }); test('renders warning if encryption key is ephemeral and keys are disabled', async () => { - http.get.mockImplementationOnce(async () => ({ + useKibanaMock().services.http.get = jest.fn().mockImplementationOnce(async () => ({ isSufficientlySecure: false, hasPermanentEncryptionKey: false, })); const { queryByText } = render( - +

{'should render'}

@@ -156,7 +157,7 @@ describe('health check', () => { const action = queryByText(/Learn/i); expect(action!.textContent).toMatchInlineSnapshot(`"Learn how(opens in a new tab or window)"`); expect(action!.getAttribute('href')).toMatchInlineSnapshot( - `"elastic.co/guide/en/kibana/current/alerting-getting-started.html#alerting-setup-prerequisites"` + `"https://www.elastic.co/guide/en/kibana/mocked-test-branch/alerting-getting-started.html#alerting-setup-prerequisites"` ); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx index c4d0b4976266..0f20ade8187f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx @@ -12,28 +12,25 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiLink, EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { DocLinksStart, HttpSetup } from 'kibana/public'; - import { EuiEmptyPrompt, EuiCode } from '@elastic/eui'; +import { DocLinksStart } from 'kibana/public'; import { AlertingFrameworkHealth } from '../../types'; import { health } from '../lib/alert_api'; import './health_check.scss'; import { useHealthContext } from '../context/health_context'; +import { useKibana } from '../../common/lib/kibana'; interface Props { - docLinks: Pick; - http: HttpSetup; inFlyout?: boolean; waitForCheck: boolean; } export const HealthCheck: React.FunctionComponent = ({ - docLinks, - http, children, waitForCheck, inFlyout = false, }) => { + const { http, docLinks } = useKibana().services; const { setLoadingHealthCheck } = useHealthContext(); const [alertingHealth, setAlertingHealth] = React.useState>(none); @@ -66,9 +63,10 @@ export const HealthCheck: React.FunctionComponent = ({ ); }; -type PromptErrorProps = Pick & { +interface PromptErrorProps { + docLinks: Pick; className?: string; -}; +} const TlsAndEncryptionError = ({ // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/x-pack/plugins/triggers_actions_ui/public/application/context/alerts_context.tsx b/x-pack/plugins/triggers_actions_ui/public/application/context/alerts_context.tsx deleted file mode 100644 index 0b2f777d13f2..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/context/alerts_context.tsx +++ /dev/null @@ -1,59 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useContext, createContext } from 'react'; -import { - HttpSetup, - IUiSettingsClient, - ToastsStart, - DocLinksStart, - ApplicationStart, -} from 'kibana/public'; -import { ChartsPluginSetup } from 'src/plugins/charts/public'; -import { - DataPublicPluginSetup, - DataPublicPluginStartUi, - IndexPatternsContract, -} from 'src/plugins/data/public'; -import { KibanaFeature } from '../../../../features/common'; -import { AlertTypeRegistryContract, ActionTypeRegistryContract } from '../../types'; - -export interface AlertsContextValue> { - reloadAlerts?: () => Promise; - http: HttpSetup; - alertTypeRegistry: AlertTypeRegistryContract; - actionTypeRegistry: ActionTypeRegistryContract; - toastNotifications: ToastsStart; - uiSettings?: IUiSettingsClient; - charts?: ChartsPluginSetup; - docLinks: DocLinksStart; - capabilities: ApplicationStart['capabilities']; - dataFieldsFormats?: DataPublicPluginSetup['fieldFormats']; - metadata?: MetaData; - dataUi?: DataPublicPluginStartUi; - dataIndexPatterns?: IndexPatternsContract; - kibanaFeatures?: KibanaFeature[]; -} - -const AlertsContext = createContext(null as any); - -export const AlertsContextProvider = ({ - children, - value, -}: { - value: AlertsContextValue; - children: React.ReactNode; -}) => { - return {children}; -}; - -export const useAlertsContext = () => { - const ctx = useContext(AlertsContext); - if (!ctx) { - throw new Error('AlertsContext has not been set.'); - } - return ctx; -}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx index 97faef6d4996..8f9e9c0a080c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx @@ -46,7 +46,6 @@ export const TriggersActionsUIHome: React.FunctionComponent ( - + @@ -156,7 +155,7 @@ export const TriggersActionsUIHome: React.FunctionComponent ( - + diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 03734779886b..0c1b00d78d19 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -37,7 +37,6 @@ import { import { AlertInstancesRouteWithApi } from './alert_instances_route'; import { ViewInApp } from './view_in_app'; import { AlertEdit } from '../../alert_form'; -import { AlertsContextProvider } from '../../../context/alerts_context'; import { routeToAlertDetails } from '../../../constants'; import { alertsErrorReasonTranslationsMapping } from '../../alerts_list/translations'; import { useKibana } from '../../../../common/lib/kibana'; @@ -62,15 +61,9 @@ export const AlertDetails: React.FunctionComponent = ({ }) => { const history = useHistory(); const { - http, - notifications: { toasts }, application: { capabilities }, alertTypeRegistry, actionTypeRegistry, - uiSettings, - docLinks, - charts, - data, setBreadcrumbs, chrome, } = useKibana().services; @@ -153,30 +146,16 @@ export const AlertDetails: React.FunctionComponent = ({ /> {editFlyoutVisible && ( - { + setInitialAlert(alert); + setEditFlyoutVisibility(false); }} - > - { - setInitialAlert(alert); - setEditFlyoutVisibility(false); - }} - /> - + actionTypeRegistry={actionTypeRegistry} + alertTypeRegistry={alertTypeRegistry} + reloadAlerts={setAlert} + /> )} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx index 608a4482543e..117abddb4d9d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx @@ -12,39 +12,34 @@ import { coreMock } from '../../../../../../../src/core/public/mocks'; import AlertAdd from './alert_add'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { Alert, ValidationResult } from '../../../types'; -import { AlertsContextProvider, useAlertsContext } from '../../context/alerts_context'; import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; -import { chartPluginMock } from '../../../../../../../src/plugins/charts/public/mocks'; -import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; import { ReactWrapper } from 'enzyme'; import { ALERTS_FEATURE_ID } from '../../../../../alerts/common'; -import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; +import { useKibana } from '../../../common/lib/kibana'; +jest.mock('../../../common/lib/kibana'); jest.mock('../../lib/alert_api', () => ({ loadAlertTypes: jest.fn(), - health: jest.fn((async) => ({ isSufficientlySecure: true, hasPermanentEncryptionKey: true })), + health: jest.fn(() => ({ isSufficientlySecure: true, hasPermanentEncryptionKey: true })), })); const actionTypeRegistry = actionTypeRegistryMock.create(); const alertTypeRegistry = alertTypeRegistryMock.create(); +const useKibanaMock = useKibana as jest.Mocked; export const TestExpression: React.FunctionComponent = () => { - const alertsContext = useAlertsContext(); - const { metadata } = alertsContext; - return ( ); }; describe('alert_add', () => { - let deps: any; let wrapper: ReactWrapper; async function setup(initialValues?: Partial) { @@ -80,15 +75,14 @@ describe('alert_add', () => { application: { capabilities }, }, ] = await mocks.getStartServices(); - deps = { - toastNotifications: mocks.notifications.toasts, - http: mocks.http, - uiSettings: mocks.uiSettings, - data: dataPluginMock.createStartContract(), - charts: chartPluginMock.createStartContract(), - actionTypeRegistry, - alertTypeRegistry, - docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' }, + // eslint-disable-next-line react-hooks/rules-of-hooks + useKibanaMock().services.application.capabilities = { + ...capabilities, + alerts: { + show: true, + save: true, + delete: true, + }, }; mocks.http.get.mockResolvedValue({ @@ -132,37 +126,18 @@ describe('alert_add', () => { actionTypeRegistry.has.mockReturnValue(true); wrapper = mountWithIntl( - - { - return new Promise(() => {}); - }, - http: deps.http, - actionTypeRegistry: deps.actionTypeRegistry, - alertTypeRegistry: deps.alertTypeRegistry, - toastNotifications: deps.toastNotifications, - uiSettings: deps.uiSettings, - docLinks: deps.docLinks, - metadata: { test: 'some value', fields: ['test'] }, - capabilities: { - ...capabilities, - actions: { - delete: true, - save: true, - show: true, - }, - }, - }} - > - {}} - initialValues={initialValues} - /> - - + {}} + initialValues={initialValues} + reloadAlerts={() => { + return new Promise(() => {}); + }} + actionTypeRegistry={actionTypeRegistry} + alertTypeRegistry={alertTypeRegistry} + metadata={{ test: 'some value', fields: ['test'] }} + /> ); // Wait for active space to resolve before requesting the component to update diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index 34a4c909c65a..03c8b539227a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -7,8 +7,13 @@ import React, { useCallback, useReducer, useMemo, useState, useEffect } from 're import { FormattedMessage } from '@kbn/i18n/react'; import { EuiTitle, EuiFlyoutHeader, EuiFlyout, EuiFlyoutBody, EuiPortal } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useAlertsContext } from '../../context/alerts_context'; -import { Alert, AlertAction, IErrorObject } from '../../../types'; +import { + ActionTypeRegistryContract, + Alert, + AlertAction, + AlertTypeRegistryContract, + IErrorObject, +} from '../../../types'; import { AlertForm, isValidAlert, validateBaseProperties } from './alert_form'; import { alertReducer, InitialAlert, InitialAlertReducer } from './alert_reducer'; import { createAlert } from '../../lib/alert_api'; @@ -17,23 +22,32 @@ import { ConfirmAlertSave } from './confirm_alert_save'; import { hasShowActionsCapability } from '../../lib/capabilities'; import AlertAddFooter from './alert_add_footer'; import { HealthContextProvider } from '../../context/health_context'; +import { useKibana } from '../../../common/lib/kibana'; -interface AlertAddProps { +export interface AlertAddProps> { consumer: string; addFlyoutVisible: boolean; + alertTypeRegistry: AlertTypeRegistryContract; + actionTypeRegistry: ActionTypeRegistryContract; setAddFlyoutVisibility: React.Dispatch>; alertTypeId?: string; canChangeTrigger?: boolean; initialValues?: Partial; + reloadAlerts?: () => Promise; + metadata?: MetaData; } -export const AlertAdd = ({ +const AlertAdd = ({ consumer, addFlyoutVisible, + alertTypeRegistry, + actionTypeRegistry, setAddFlyoutVisibility, canChangeTrigger, alertTypeId, initialValues, + reloadAlerts, + metadata, }: AlertAddProps) => { const initialAlert: InitialAlert = useMemo( () => ({ @@ -65,14 +79,10 @@ export const AlertAdd = ({ }; const { - reloadAlerts, http, - toastNotifications, - alertTypeRegistry, - actionTypeRegistry, - docLinks, - capabilities, - } = useAlertsContext(); + notifications: { toasts }, + application: { capabilities }, + } = useKibana().services; const canShowActions = hasShowActionsCapability(capabilities); @@ -127,7 +137,7 @@ export const AlertAdd = ({ try { if (isValidAlert(alert, errors)) { const newAlert = await createAlert({ http, alert }); - toastNotifications.addSuccess( + toasts.addSuccess( i18n.translate('xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText', { defaultMessage: 'Created alert "{alertName}"', values: { @@ -138,7 +148,7 @@ export const AlertAdd = ({ return newAlert; } } catch (errorRes) { - toastNotifications.addDanger( + toasts.addDanger( errorRes.body?.message ?? i18n.translate('xpack.triggersActionsUI.sections.alertAdd.saveErrorNotificationText', { defaultMessage: 'Cannot create alert.', @@ -166,7 +176,7 @@ export const AlertAdd = ({ - + ; describe('alert_edit', () => { - let deps: any; let wrapper: ReactWrapper; let mockedCoreSetup: ReturnType; @@ -32,17 +32,19 @@ describe('alert_edit', () => { application: { capabilities }, }, ] = await mockedCoreSetup.getStartServices(); - deps = { - toastNotifications: mockedCoreSetup.notifications.toasts, - http: mockedCoreSetup.http, - uiSettings: mockedCoreSetup.uiSettings, - actionTypeRegistry, - alertTypeRegistry, - docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' }, - capabilities, + // eslint-disable-next-line react-hooks/rules-of-hooks + useKibanaMock().services.application.capabilities = { + ...capabilities, + alerts: { + show: true, + save: true, + delete: true, + execute: true, + }, }; - mockedCoreSetup.http.get.mockResolvedValue({ + // eslint-disable-next-line react-hooks/rules-of-hooks + useKibanaMock().services.http.get = jest.fn().mockResolvedValue({ isSufficientlySecure: true, hasPermanentEncryptionKey: true, }); @@ -122,24 +124,15 @@ describe('alert_edit', () => { actionTypeRegistry.has.mockReturnValue(true); wrapper = mountWithIntl( - - { - return new Promise(() => {}); - }, - http: deps!.http, - actionTypeRegistry: deps!.actionTypeRegistry, - alertTypeRegistry: deps!.alertTypeRegistry, - toastNotifications: deps!.toastNotifications, - uiSettings: deps!.uiSettings, - docLinks: deps.docLinks, - capabilities: deps!.capabilities, - }} - > - {}} initialAlert={alert} /> - - + {}} + initialAlert={alert} + reloadAlerts={() => { + return new Promise(() => {}); + }} + actionTypeRegistry={actionTypeRegistry} + alertTypeRegistry={alertTypeRegistry} + /> ); // Wait for active space to resolve before requesting the component to update await act(async () => { @@ -155,15 +148,17 @@ describe('alert_edit', () => { }); it('displays a toast message on save for server errors', async () => { - mockedCoreSetup.http.get.mockResolvedValue([]); + useKibanaMock().services.http.get = jest.fn().mockResolvedValue([]); await setup(); const err = new Error() as any; err.body = {}; err.body.message = 'Fail message'; - mockedCoreSetup.http.put.mockRejectedValue(err); + useKibanaMock().services.http.put = jest.fn().mockRejectedValue(err); await act(async () => { wrapper.find('[data-test-subj="saveEditedAlertButton"]').first().simulate('click'); }); - expect(mockedCoreSetup.notifications.toasts.addDanger).toHaveBeenCalledWith('Fail message'); + expect(useKibanaMock().services.notifications.toasts.addDanger).toHaveBeenCalledWith( + 'Fail message' + ); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index 2e2a77fa6afc..dbf460d1778c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -20,20 +20,37 @@ import { EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useAlertsContext } from '../../context/alerts_context'; -import { Alert, AlertAction, IErrorObject } from '../../../types'; +import { + ActionTypeRegistryContract, + Alert, + AlertAction, + AlertTypeRegistryContract, + IErrorObject, +} from '../../../types'; import { AlertForm, validateBaseProperties } from './alert_form'; import { alertReducer, ConcreteAlertReducer } from './alert_reducer'; import { updateAlert } from '../../lib/alert_api'; import { HealthCheck } from '../../components/health_check'; import { HealthContextProvider } from '../../context/health_context'; +import { useKibana } from '../../../common/lib/kibana'; -interface AlertEditProps { +export interface AlertEditProps> { initialAlert: Alert; + alertTypeRegistry: AlertTypeRegistryContract; + actionTypeRegistry: ActionTypeRegistryContract; onClose(): void; + reloadAlerts?: () => Promise; + metadata?: MetaData; } -export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { +export const AlertEdit = ({ + initialAlert, + onClose, + reloadAlerts, + alertTypeRegistry, + actionTypeRegistry, + metadata, +}: AlertEditProps) => { const [{ alert }, dispatch] = useReducer(alertReducer as ConcreteAlertReducer, { alert: initialAlert, }); @@ -44,13 +61,9 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { ); const { - reloadAlerts, http, - toastNotifications, - alertTypeRegistry, - actionTypeRegistry, - docLinks, - } = useAlertsContext(); + notifications: { toasts }, + } = useKibana().services; const alertType = alertTypeRegistry.get(alert.alertTypeId); @@ -76,7 +89,7 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { async function onSaveAlert(): Promise { try { const newAlert = await updateAlert({ http, alert, id: alert.id }); - toastNotifications.addSuccess( + toasts.addSuccess( i18n.translate('xpack.triggersActionsUI.sections.alertEdit.saveSuccessNotificationText', { defaultMessage: "Updated '{alertName}'", values: { @@ -86,7 +99,7 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { ); return newAlert; } catch (errorRes) { - toastNotifications.addDanger( + toasts.addDanger( errorRes.body?.message ?? i18n.translate('xpack.triggersActionsUI.sections.alertEdit.saveErrorNotificationText', { defaultMessage: 'Cannot update alert.', @@ -114,7 +127,7 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { - + {hasActionsDisabled && ( @@ -135,12 +148,15 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { alert={alert} dispatch={dispatch} errors={errors} + actionTypeRegistry={actionTypeRegistry} + alertTypeRegistry={alertTypeRegistry} canChangeTrigger={false} setHasActionsDisabled={setHasActionsDisabled} setHasActionsWithBrokenConnector={setHasActionsWithBrokenConnector} operation="i18n.translate('xpack.triggersActionsUI.sections.alertEdit.operationName', { defaultMessage: 'edit', })" + metadata={metadata} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx index 0d5972d075f4..785eaeb9059d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx @@ -11,22 +11,18 @@ import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; import { ValidationResult, Alert } from '../../../types'; import { AlertForm } from './alert_form'; -import { AlertsContextProvider } from '../../context/alerts_context'; import { coreMock } from 'src/core/public/mocks'; import { ALERTS_FEATURE_ID, RecoveredActionGroup } from '../../../../../alerts/common'; +import { useKibana } from '../../../common/lib/kibana'; const actionTypeRegistry = actionTypeRegistryMock.create(); const alertTypeRegistry = alertTypeRegistryMock.create(); jest.mock('../../lib/alert_api', () => ({ loadAlertTypes: jest.fn(), })); +jest.mock('../../../common/lib/kibana'); describe('alert_form', () => { - beforeEach(() => { - jest.resetAllMocks(); - }); - - let deps: any; const alertType = { id: 'my-alert-type', iconClass: 'test', @@ -67,6 +63,7 @@ describe('alert_form', () => { alertParamsExpression: () => , requiresAppContext: true, }; + const useKibanaMock = useKibana as jest.Mocked; describe('alert_form create alert', () => { let wrapper: ReactWrapper; @@ -99,14 +96,14 @@ describe('alert_form', () => { application: { capabilities }, }, ] = await mocks.getStartServices(); - deps = { - toastNotifications: mocks.notifications.toasts, - http: mocks.http, - uiSettings: mocks.uiSettings, - actionTypeRegistry, - alertTypeRegistry, - docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' }, - capabilities, + // eslint-disable-next-line react-hooks/rules-of-hooks + useKibanaMock().services.application.capabilities = { + ...capabilities, + alerts: { + show: true, + save: true, + delete: true, + }, }; alertTypeRegistry.list.mockReturnValue([alertType, alertTypeNonEditable]); alertTypeRegistry.has.mockReturnValue(true); @@ -128,27 +125,14 @@ describe('alert_form', () => { } as unknown) as Alert; wrapper = mountWithIntl( - { - return new Promise(() => {}); - }, - http: deps!.http, - docLinks: deps.docLinks, - actionTypeRegistry: deps!.actionTypeRegistry, - alertTypeRegistry: deps!.alertTypeRegistry, - toastNotifications: deps!.toastNotifications, - uiSettings: deps!.uiSettings, - capabilities: deps!.capabilities, - }} - > - {}} - errors={{ name: [], interval: [] }} - operation="create" - /> - + {}} + errors={{ name: [], interval: [] }} + operation="create" + actionTypeRegistry={actionTypeRegistry} + alertTypeRegistry={alertTypeRegistry} + /> ); await act(async () => { @@ -208,6 +192,7 @@ describe('alert_form', () => { async function setup() { const { loadAlertTypes } = jest.requireMock('../../lib/alert_api'); + loadAlertTypes.mockResolvedValue([ { id: 'other-consumer-producer-alert-type', @@ -250,14 +235,14 @@ describe('alert_form', () => { application: { capabilities }, }, ] = await mocks.getStartServices(); - deps = { - toastNotifications: mocks.notifications.toasts, - http: mocks.http, - uiSettings: mocks.uiSettings, - actionTypeRegistry, - alertTypeRegistry, - docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' }, - capabilities, + // eslint-disable-next-line react-hooks/rules-of-hooks + useKibanaMock().services.application.capabilities = { + ...capabilities, + alerts: { + show: true, + save: true, + delete: true, + }, }; alertTypeRegistry.list.mockReturnValue([ { @@ -302,27 +287,14 @@ describe('alert_form', () => { } as unknown) as Alert; wrapper = mountWithIntl( - { - return new Promise(() => {}); - }, - http: deps!.http, - docLinks: deps.docLinks, - actionTypeRegistry: deps!.actionTypeRegistry, - alertTypeRegistry: deps!.alertTypeRegistry, - toastNotifications: deps!.toastNotifications, - uiSettings: deps!.uiSettings, - capabilities: deps!.capabilities, - }} - > - {}} - errors={{ name: [], interval: [] }} - operation="create" - /> - + {}} + errors={{ name: [], interval: [] }} + operation="create" + actionTypeRegistry={actionTypeRegistry} + alertTypeRegistry={alertTypeRegistry} + /> ); await act(async () => { @@ -354,14 +326,6 @@ describe('alert_form', () => { let wrapper: ReactWrapper; async function setup() { - const mockes = coreMock.createSetup(); - deps = { - toastNotifications: mockes.notifications.toasts, - http: mockes.http, - uiSettings: mockes.uiSettings, - actionTypeRegistry, - alertTypeRegistry, - }; alertTypeRegistry.list.mockReturnValue([alertType]); alertTypeRegistry.get.mockReturnValue(alertType); alertTypeRegistry.has.mockReturnValue(true); @@ -385,27 +349,14 @@ describe('alert_form', () => { } as unknown) as Alert; wrapper = mountWithIntl( - { - return new Promise(() => {}); - }, - http: deps!.http, - docLinks: deps.docLinks, - actionTypeRegistry: deps!.actionTypeRegistry, - alertTypeRegistry: deps!.alertTypeRegistry, - toastNotifications: deps!.toastNotifications, - uiSettings: deps!.uiSettings, - capabilities: deps!.capabilities, - }} - > - {}} - errors={{ name: [], interval: [] }} - operation="create" - /> - + {}} + errors={{ name: [], interval: [] }} + operation="create" + actionTypeRegistry={actionTypeRegistry} + alertTypeRegistry={alertTypeRegistry} + /> ); await act(async () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx index 5b0585e2cc79..c94086a6adab 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx @@ -50,14 +50,16 @@ import { AlertTypeIndex, AlertType, ValidationResult, + AlertTypeRegistryContract, + ActionTypeRegistryContract, } from '../../../types'; import { getTimeOptions } from '../../../common/lib/get_time_options'; -import { useAlertsContext } from '../../context/alerts_context'; import { ActionForm } from '../action_connector_form'; import { AlertActionParam, ALERTS_FEATURE_ID } from '../../../../../alerts/common'; import { hasAllPrivilege, hasShowActionsCapability } from '../../lib/capabilities'; import { SolutionFilter } from './solution_filter'; import './alert_form.scss'; +import { useKibana } from '../../../common/lib/kibana'; import { recoveredActionGroupMessage } from '../../constants'; import { getDefaultsForActionParams } from '../../lib/get_defaults_for_action_params'; @@ -113,14 +115,17 @@ function getProducerFeatureName(producer: string, kibanaFeatures: KibanaFeature[ return kibanaFeatures.find((featureItem) => featureItem.id === producer)?.name; } -interface AlertFormProps { +interface AlertFormProps> { alert: InitialAlert; dispatch: React.Dispatch; errors: IErrorObject; + alertTypeRegistry: AlertTypeRegistryContract; + actionTypeRegistry: ActionTypeRegistryContract; + operation: string; canChangeTrigger?: boolean; // to hide Change trigger button setHasActionsDisabled?: (value: boolean) => void; setHasActionsWithBrokenConnector?: (value: boolean) => void; - operation: string; + metadata?: MetaData; } export const AlertForm = ({ @@ -131,17 +136,19 @@ export const AlertForm = ({ setHasActionsDisabled, setHasActionsWithBrokenConnector, operation, + alertTypeRegistry, + actionTypeRegistry, + metadata, }: AlertFormProps) => { - const alertsContext = useAlertsContext(); const { http, - toastNotifications, - alertTypeRegistry, - actionTypeRegistry, + notifications: { toasts }, docLinks, - capabilities, + application: { capabilities }, kibanaFeatures, - } = alertsContext; + charts, + data, + } = useKibana().services; const canShowActions = hasShowActionsCapability(capabilities); const [alertTypeModel, setAlertTypeModel] = useState(null); @@ -207,7 +214,7 @@ export const AlertForm = ({ new Map([...solutionsResult.entries()].sort(([, a], [, b]) => a.localeCompare(b))) ); } catch (e) { - toastNotifications.addDanger({ + toasts.addDanger({ title: i18n.translate( 'xpack.triggersActionsUI.sections.alertForm.unableToLoadAlertTypesMessage', { defaultMessage: 'Unable to load alert types' } @@ -486,9 +493,11 @@ export const AlertForm = ({ errors={errors} setAlertParams={setAlertParams} setAlertProperty={setAlertProperty} - alertsContext={alertsContext} defaultActionGroupId={defaultActionGroupId} actionGroups={selectedAlertType.actionGroups} + metadata={metadata} + charts={charts} + data={data} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index 0a674b4d5486..aaaf843d43ea 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -30,7 +30,6 @@ import { import { useHistory } from 'react-router-dom'; import { isEmpty } from 'lodash'; -import { AlertsContextProvider } from '../../../context/alerts_context'; import { ActionType, Alert, AlertTableItem, AlertTypeIndex, Pagination } from '../../../../types'; import { AlertAdd } from '../../alert_form'; import { BulkOperationPopover } from '../../common/components/bulk_operation_popover'; @@ -80,10 +79,6 @@ export const AlertsList: React.FunctionComponent = () => { application: { capabilities }, alertTypeRegistry, actionTypeRegistry, - uiSettings, - docLinks, - charts, - data, kibanaFeatures, } = useKibana().services; const canExecuteActions = hasExecuteActionsCapability(capabilities); @@ -619,7 +614,7 @@ export const AlertsList: React.FunctionComponent = () => { return (
{ + onDeleted={async () => { setAlertsToDelete([]); setSelectedIds([]); await loadAlertsData(); @@ -658,29 +653,14 @@ export const AlertsList: React.FunctionComponent = () => { ) : ( noPermissionPrompt )} - - - +
); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.tsx index ff66d31044b0..79636056267f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.tsx @@ -21,9 +21,6 @@ export function withActionOperations( ): React.FunctionComponent> { return (props: PropsWithOptionalApiHandlers) => { const { http } = useKibana().services; - if (!http) { - throw new Error('KibanaContext has not been initalized correctly.'); - } return ( loadActionTypes({ http })} /> ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.tsx index 77f7631b6d63..5656aa9de779 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.tsx @@ -70,9 +70,6 @@ export function withBulkAlertOperations( ): React.FunctionComponent> { return (props: PropsWithOptionalApiHandlers) => { const { http } = useKibana().services; - if (!http) { - throw new Error('KibanaContext has not been initalized correctly.'); - } return ( { + const AlertAddFlyoutLazy = lazy(() => import('../application/sections/alert_form/alert_add')); + return ( + + + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_edit_alert_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_edit_alert_flyout.tsx new file mode 100644 index 000000000000..7966ffc798bc --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/get_edit_alert_flyout.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { lazy, Suspense } from 'react'; +import type { AlertEditProps } from '../application/sections/alert_form/alert_edit'; + +export const getEditAlertFlyoutLazy = (props: AlertEditProps) => { + const AlertEditFlyoutLazy = lazy(() => import('../application/sections/alert_form/alert_edit')); + return ( + + + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/kibana/kibana_react.mock.ts index ce1d9887bbb2..1e68ebcb930b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/kibana/kibana_react.mock.ts @@ -23,7 +23,6 @@ export const createStartServicesMock = (): TriggersAndActionsUiServices => { get: jest.fn(), list: jest.fn(), } as AlertTypeRegistryContract, - notifications: core.notifications, dataPlugin: jest.fn(), navigateToApp: jest.fn(), alerts: { diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 6955a9432614..e6680e9d4473 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -6,7 +6,6 @@ import { Plugin } from './plugin'; -export { AlertsContextProvider, AlertsContextValue } from './application/context/alerts_context'; export { AlertAdd } from './application/sections/alert_form'; export { AlertEdit, diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index 365eac9c031b..f2c8957400fa 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -28,6 +28,10 @@ import type { ConnectorAddFlyoutProps } from './application/sections/action_conn import type { ConnectorEditFlyoutProps } from './application/sections/action_connector_form/connector_edit_flyout'; import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout'; import { getEditConnectorFlyoutLazy } from './common/get_edit_connector_flyout'; +import { getAddAlertFlyoutLazy } from './common/get_add_alert_flyout'; +import { getEditAlertFlyoutLazy } from './common/get_edit_alert_flyout'; +import { AlertAddProps } from './application/sections/alert_form/alert_add'; +import { AlertEditProps } from './application/sections/alert_form/alert_edit'; export interface TriggersAndActionsUIPublicPluginSetup { actionTypeRegistry: TypeRegistry; @@ -39,10 +43,16 @@ export interface TriggersAndActionsUIPublicPluginStart { alertTypeRegistry: TypeRegistry; getAddConnectorFlyout: ( props: Omit - ) => ReactElement | null; + ) => ReactElement; getEditConnectorFlyout: ( props: Omit - ) => ReactElement | null; + ) => ReactElement; + getAddAlertFlyout: ( + props: Omit + ) => ReactElement; + getEditAlertFlyout: ( + props: Omit + ) => ReactElement; } interface PluginsSetup { @@ -152,6 +162,24 @@ export class Plugin actionTypeRegistry: this.actionTypeRegistry, }); }, + getAddAlertFlyout: ( + props: Omit + ) => { + return getAddAlertFlyoutLazy({ + ...props, + actionTypeRegistry: this.actionTypeRegistry, + alertTypeRegistry: this.alertTypeRegistry, + }); + }, + getEditAlertFlyout: ( + props: Omit + ) => { + return getEditAlertFlyoutLazy({ + ...props, + actionTypeRegistry: this.actionTypeRegistry, + alertTypeRegistry: this.alertTypeRegistry, + }); + }, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 8c69643f19b8..1e11102516fc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -6,6 +6,8 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import type { DocLinksStart } from 'kibana/public'; import { ComponentType } from 'react'; +import { ChartsPluginSetup } from 'src/plugins/charts/public'; +import { DataPublicPluginStart } from 'src/plugins/data/public'; import { ActionGroup, AlertActionParam } from '../../alerts/common'; import { ActionType } from '../../actions/common'; import { TypeRegistry } from './application/type_registry'; @@ -158,7 +160,7 @@ export interface AlertTableItem extends Alert { export interface AlertTypeParamsExpressionProps< AlertParamsType = unknown, - AlertsContextValue = unknown + MetaData = Record > { alertParams: AlertParamsType; alertInterval: string; @@ -166,12 +168,14 @@ export interface AlertTypeParamsExpressionProps< setAlertParams: (property: string, value: any) => void; setAlertProperty: (key: Key, value: Alert[Key] | null) => void; errors: IErrorObject; - alertsContext: AlertsContextValue; defaultActionGroupId: string; actionGroups: ActionGroup[]; + metadata?: MetaData; + charts: ChartsPluginSetup; + data: DataPublicPluginStart; } -export interface AlertTypeModel { +export interface AlertTypeModel { id: string; name: string | JSX.Element; description: string; @@ -180,9 +184,7 @@ export interface AlertTypeModel validate: (alertParams: AlertParamsType) => ValidationResult; alertParamsExpression: | React.FunctionComponent - | React.LazyExoticComponent< - ComponentType> - >; + | React.LazyExoticComponent>>; requiresAppContext: boolean; defaultActionMessage?: string; } diff --git a/x-pack/plugins/uptime/public/apps/uptime_app.tsx b/x-pack/plugins/uptime/public/apps/uptime_app.tsx index 9535cfdb8c8b..9bbcde041a79 100644 --- a/x-pack/plugins/uptime/public/apps/uptime_app.tsx +++ b/x-pack/plugins/uptime/public/apps/uptime_app.tsx @@ -25,10 +25,7 @@ import { import { CommonlyUsedRange } from '../components/common/uptime_date_picker'; import { setBasePath } from '../state/actions'; import { PageRouter } from '../routes'; -import { - UptimeAlertsContextProvider, - UptimeAlertsFlyoutWrapper, -} from '../components/overview/alerts'; +import { UptimeAlertsFlyoutWrapper } from '../components/overview/alerts'; import { store } from '../state'; import { kibanaService } from '../state/kibana_service'; import { ScopedHistory } from '../../../../../src/core/public'; @@ -110,16 +107,14 @@ const Application = (props: UptimeAppProps) => { - - - -
- - -
-
-
-
+ + +
+ + +
+
+
diff --git a/x-pack/plugins/uptime/public/components/common/alerts/uptime_edit_alert_flyout.tsx b/x-pack/plugins/uptime/public/components/common/alerts/uptime_edit_alert_flyout.tsx index cdc3632545c5..4dec97f6558d 100644 --- a/x-pack/plugins/uptime/public/components/common/alerts/uptime_edit_alert_flyout.tsx +++ b/x-pack/plugins/uptime/public/components/common/alerts/uptime_edit_alert_flyout.tsx @@ -4,8 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { Alert, AlertEdit } from '../../../../../../plugins/triggers_actions_ui/public'; +import React, { useMemo } from 'react'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { + Alert, + TriggersAndActionsUIPublicPluginStart, +} from '../../../../../../plugins/triggers_actions_ui/public'; interface Props { alertFlyoutVisible: boolean; @@ -13,6 +17,10 @@ interface Props { setAlertFlyoutVisibility: React.Dispatch>; } +interface KibanaDeps { + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; +} + export const UptimeEditAlertFlyoutComponent = ({ alertFlyoutVisible, initialAlert, @@ -21,5 +29,16 @@ export const UptimeEditAlertFlyoutComponent = ({ const onClose = () => { setAlertFlyoutVisibility(false); }; - return alertFlyoutVisible ? : null; + const { triggersActionsUi } = useKibana().services; + + const EditAlertFlyout = useMemo( + () => + triggersActionsUi.getEditAlertFlyout({ + initialAlert, + onClose, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + return <>{alertFlyoutVisible && EditAlertFlyout}; }; diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_integerations.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_integerations.test.tsx index 10b3a90414eb..95774e320816 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_integerations.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_integerations.test.tsx @@ -8,7 +8,10 @@ import React from 'react'; import { MLIntegrationComponent } from '../ml_integeration'; import { renderWithRouter, shallowWithRouter } from '../../../../lib'; import * as redux from 'react-redux'; +import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; +import { coreMock } from 'src/core/public/mocks'; +const core = coreMock.createStart(); describe('ML Integrations', () => { beforeEach(() => { const spy = jest.spyOn(redux, 'useDispatch'); @@ -24,7 +27,13 @@ describe('ML Integrations', () => { }); it('renders without errors', () => { - const wrapper = renderWithRouter(); + const wrapper = renderWithRouter( + + + + ); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_job_link.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_job_link.test.tsx index fbd66e7077a7..581fe1cbf9b1 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_job_link.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_job_link.test.tsx @@ -5,9 +5,12 @@ */ import React from 'react'; +import { coreMock } from 'src/core/public/mocks'; import { renderWithRouter, shallowWithRouter } from '../../../../lib'; import { MLJobLink } from '../ml_job_link'; +import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; +const core = coreMock.createStart(); describe('ML JobLink', () => { it('shallow renders without errors', () => { const wrapper = shallowWithRouter( @@ -18,7 +21,11 @@ describe('ML JobLink', () => { it('renders without errors', () => { const wrapper = renderWithRouter( - + + + ); expect(wrapper).toMatchSnapshot(); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_manage_job.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_manage_job.test.tsx index 841c577a4014..0db6841c32aa 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_manage_job.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_manage_job.test.tsx @@ -5,10 +5,13 @@ */ import React from 'react'; +import { coreMock } from 'src/core/public/mocks'; import { ManageMLJobComponent } from '../manage_ml_job'; import * as redux from 'react-redux'; import { renderWithRouter, shallowWithRouter } from '../../../../lib'; +import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; +const core = coreMock.createStart(); describe('Manage ML Job', () => { it('shallow renders without errors', () => { jest.spyOn(redux, 'useSelector').mockReturnValue(true); @@ -25,7 +28,11 @@ describe('Manage ML Job', () => { jest.spyOn(redux, 'useSelector').mockReturnValue(true); const wrapper = renderWithRouter( - + + + ); expect(wrapper).toMatchSnapshot(); }); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/index.ts b/x-pack/plugins/uptime/public/components/overview/alerts/index.ts index 5ca0f4c3fe8a..cb13c76122c9 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/index.ts +++ b/x-pack/plugins/uptime/public/components/overview/alerts/index.ts @@ -6,6 +6,5 @@ export { AlertMonitorStatusComponent } from './alert_monitor_status'; export { ToggleAlertFlyoutButtonComponent } from './toggle_alert_flyout_button'; -export { UptimeAlertsContextProvider } from './uptime_alerts_context_provider'; export { UptimeAlertsFlyoutWrapperComponent } from './uptime_alerts_flyout_wrapper'; export * from './alerts_containers'; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx deleted file mode 100644 index 6b919eb8a085..000000000000 --- a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx +++ /dev/null @@ -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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { - HttpStart, - NotificationsStart, - IUiSettingsClient, - DocLinksStart, - ApplicationStart, -} from 'src/core/public'; -import { DataPublicPluginStart } from '../../../../../../../src/plugins/data/public'; -import { ChartsPluginStart } from '../../../../../../../src/plugins/charts/public'; -import { - AlertsContextProvider, - TriggersAndActionsUIPublicPluginStart, -} from '../../../../../../plugins/triggers_actions_ui/public'; -import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; - -interface KibanaDeps { - http: HttpStart; - notifications: NotificationsStart; - uiSettings: IUiSettingsClient; - docLinks: DocLinksStart; - application: ApplicationStart; - - data: DataPublicPluginStart; - charts: ChartsPluginStart; - triggersActionsUi: TriggersAndActionsUIPublicPluginStart; -} - -export const UptimeAlertsContextProvider: React.FC = ({ children }) => { - const { - services: { - data: { fieldFormats }, - http, - charts, - notifications, - triggersActionsUi: { actionTypeRegistry, alertTypeRegistry }, - uiSettings, - docLinks, - application: { capabilities }, - }, - } = useKibana(); - - return ( - - {children} - - ); -}; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx index de4e67cfe8ed..7995cf88df9b 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { AlertAdd } from '../../../../../../plugins/triggers_actions_ui/public'; +import React, { useMemo } from 'react'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { TriggersAndActionsUIPublicPluginStart } from '../../../../../../plugins/triggers_actions_ui/public'; interface Props { alertFlyoutVisible: boolean; @@ -13,18 +14,29 @@ interface Props { setAlertFlyoutVisibility: React.Dispatch>; } +interface KibanaDeps { + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; +} + export const UptimeAlertsFlyoutWrapperComponent = ({ alertFlyoutVisible, alertTypeId, setAlertFlyoutVisibility, -}: Props) => ( - -); +}: Props) => { + const { triggersActionsUi } = useKibana().services; + + const AddAlertFlyout = useMemo( + () => + triggersActionsUi.getAddAlertFlyout({ + consumer: 'uptime', + addFlyoutVisible: alertFlyoutVisible, + setAddFlyoutVisibility: setAlertFlyoutVisibility, + alertTypeId, + canChangeTrigger: !alertTypeId, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [alertFlyoutVisible, alertTypeId] + ); + + return <>{AddAlertFlyout}; +};