mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[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
This commit is contained in:
parent
a6a8cc2175
commit
6757b95b1e
73 changed files with 862 additions and 1172 deletions
|
@ -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<AlwaysFiringParams, AlertsContextValue>
|
||||
AlertTypeParamsExpressionProps<AlwaysFiringParams>
|
||||
> = ({ alertParams, setAlertParams, actionGroups, defaultActionGroupId }) => {
|
||||
const {
|
||||
instances = DEFAULT_INSTANCES_TO_GENERATE,
|
||||
|
|
|
@ -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<boolean>(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 (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -34,27 +35,7 @@ export const CreateAlert = ({
|
|||
onClick={() => setAlertFlyoutVisibility(true)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
http,
|
||||
actionTypeRegistry: triggersActionsUi.actionTypeRegistry,
|
||||
alertTypeRegistry: triggersActionsUi.alertTypeRegistry,
|
||||
toastNotifications,
|
||||
uiSettings,
|
||||
docLinks,
|
||||
charts,
|
||||
dataFieldsFormats: data.fieldFormats,
|
||||
capabilities,
|
||||
}}
|
||||
>
|
||||
<AlertAdd
|
||||
consumer={ALERTING_EXAMPLE_APP_ID}
|
||||
addFlyoutVisible={alertFlyoutVisible}
|
||||
setAddFlyoutVisibility={setAlertFlyoutVisibility}
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>{AddAlertFlyout}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -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 (
|
||||
<RedirectAppLinks application={core.application}>
|
||||
<ApmPluginContext.Provider value={apmPluginContextValue}>
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
http: core.http,
|
||||
docLinks: core.docLinks,
|
||||
capabilities: core.application.capabilities,
|
||||
toastNotifications: core.notifications.toasts,
|
||||
actionTypeRegistry: plugins.triggersActionsUi.actionTypeRegistry,
|
||||
alertTypeRegistry: plugins.triggersActionsUi.alertTypeRegistry,
|
||||
}}
|
||||
>
|
||||
<KibanaContextProvider services={{ ...core, ...plugins }}>
|
||||
<i18nCore.Context>
|
||||
<Router history={history}>
|
||||
<UrlParamsProvider>
|
||||
<LicenseProvider>
|
||||
<App />
|
||||
</LicenseProvider>
|
||||
</UrlParamsProvider>
|
||||
</Router>
|
||||
</i18nCore.Context>
|
||||
</KibanaContextProvider>
|
||||
</AlertsContextProvider>
|
||||
<KibanaContextProvider services={{ ...core, ...startDeps }}>
|
||||
<i18nCore.Context>
|
||||
<Router history={history}>
|
||||
<UrlParamsProvider>
|
||||
<LicenseProvider>
|
||||
<App />
|
||||
</LicenseProvider>
|
||||
</UrlParamsProvider>
|
||||
</Router>
|
||||
</i18nCore.Context>
|
||||
</KibanaContextProvider>
|
||||
</ApmPluginContext.Provider>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
|
@ -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(
|
||||
<ApmAppRoot apmPluginContextValue={apmPluginContextValue} />,
|
||||
<ApmAppRoot
|
||||
apmPluginContextValue={apmPluginContextValue}
|
||||
startDeps={startDeps}
|
||||
/>,
|
||||
element
|
||||
);
|
||||
return () => {
|
||||
|
|
|
@ -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<typeof AlertAdd>;
|
||||
import { TriggersAndActionsUIPublicPluginStart } from '../../../../../triggers_actions_ui/public';
|
||||
|
||||
interface Props {
|
||||
addFlyoutVisible: AlertAddProps['addFlyoutVisible'];
|
||||
setAddFlyoutVisibility: AlertAddProps['setAddFlyoutVisibility'];
|
||||
addFlyoutVisible: boolean;
|
||||
setAddFlyoutVisibility: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
alertType: AlertType | null;
|
||||
}
|
||||
|
||||
interface KibanaDeps {
|
||||
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
|
||||
}
|
||||
|
||||
export function AlertingFlyout(props: Props) {
|
||||
const { addFlyoutVisible, setAddFlyoutVisibility, alertType } = props;
|
||||
return (
|
||||
alertType && (
|
||||
<AlertAdd
|
||||
addFlyoutVisible={addFlyoutVisible}
|
||||
setAddFlyoutVisibility={setAddFlyoutVisibility}
|
||||
consumer="apm"
|
||||
alertTypeId={alertType}
|
||||
canChangeTrigger={false}
|
||||
/>
|
||||
)
|
||||
const {
|
||||
services: { triggersActionsUi },
|
||||
} = useKibana<KibanaDeps>();
|
||||
const addAlertFlyout = useMemo(
|
||||
() =>
|
||||
alertType &&
|
||||
triggersActionsUi.getAddAlertFlyout({
|
||||
consumer: 'apm',
|
||||
addFlyoutVisible,
|
||||
setAddFlyoutVisibility,
|
||||
alertTypeId: alertType,
|
||||
canChangeTrigger: false,
|
||||
}),
|
||||
[addFlyoutVisible, alertType, setAddFlyoutVisibility, triggersActionsUi]
|
||||
);
|
||||
return <>{addAlertFlyout}</>;
|
||||
}
|
||||
|
|
|
@ -138,12 +138,18 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
|
|||
|
||||
async mount(params: AppMountParameters<unknown>) {
|
||||
// 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
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -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<string, any>;
|
||||
validate: (params: any) => ValidationResult;
|
||||
showNoDataResults?: boolean;
|
||||
|
@ -47,12 +46,13 @@ export const AlertPreview: React.FC<Props> = (props) => {
|
|||
alertParams,
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
fetch,
|
||||
alertType,
|
||||
validate,
|
||||
showNoDataResults,
|
||||
groupByDisplayName,
|
||||
} = props;
|
||||
const { http } = useKibana().services;
|
||||
|
||||
const [previewLookbackInterval, setPreviewLookbackInterval] = useState<string>('h');
|
||||
const [isPreviewLoading, setIsPreviewLoading] = useState<boolean>(false);
|
||||
const [previewError, setPreviewError] = useState<any | false>(false);
|
||||
|
@ -70,7 +70,7 @@ export const AlertPreview: React.FC<Props> = (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> = (props) => {
|
|||
}, [
|
||||
alertParams,
|
||||
alertInterval,
|
||||
fetch,
|
||||
alertType,
|
||||
groupByDisplayName,
|
||||
previewLookbackInterval,
|
||||
alertThrottle,
|
||||
showNoDataResults,
|
||||
http,
|
||||
]);
|
||||
|
||||
const previewIntervalError = useMemo(() => {
|
||||
|
|
|
@ -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<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
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<KibanaDeps>();
|
||||
|
||||
const { inventoryPrefill } = useAlertPrefillContext();
|
||||
const { customMetrics } = inventoryPrefill;
|
||||
|
||||
return (
|
||||
<>
|
||||
{triggersActionsUI && (
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
metadata: {
|
||||
options,
|
||||
nodeType,
|
||||
filter,
|
||||
customMetrics,
|
||||
},
|
||||
toastNotifications: services.notifications?.toasts,
|
||||
http: services.http,
|
||||
docLinks: services.docLinks,
|
||||
capabilities: services.application.capabilities,
|
||||
actionTypeRegistry: triggersActionsUI.actionTypeRegistry,
|
||||
alertTypeRegistry: triggersActionsUI.alertTypeRegistry,
|
||||
}}
|
||||
>
|
||||
<AlertAdd
|
||||
addFlyoutVisible={visible!}
|
||||
setAddFlyoutVisibility={setVisible}
|
||||
alertTypeId={METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID}
|
||||
canChangeTrigger={false}
|
||||
consumer={'infrastructure'}
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
)}
|
||||
</>
|
||||
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}</>;
|
||||
};
|
||||
|
|
|
@ -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<AlertContextMeta> = {
|
||||
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(
|
||||
<Expressions
|
||||
alertsContext={context}
|
||||
alertInterval="1m"
|
||||
alertThrottle="1m"
|
||||
alertParams={alertParams as any}
|
||||
errors={[]}
|
||||
setAlertParams={(key, value) => 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' }]}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -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<AlertContextMeta>;
|
||||
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> = (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<number | undefined>(1);
|
||||
const [timeUnit, setTimeUnit] = useState<Unit>('m');
|
||||
|
@ -221,7 +214,7 @@ export const Expressions: React.FC<Props> = (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> = (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> = (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> = (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> = (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> = (props) => {
|
|||
fullWidth
|
||||
display="rowCompressed"
|
||||
>
|
||||
{(alertsContext.metadata && (
|
||||
{(metadata && (
|
||||
<MetricsExplorerKueryBar
|
||||
derivedIndexPattern={derivedIndexPattern}
|
||||
onSubmit={onFilterChange}
|
||||
|
@ -394,7 +386,6 @@ export const Expressions: React.FC<Props> = (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<InventoryMetricConditions>): void;
|
||||
alertsContextMetadata: AlertsContextValue<AlertContextMeta>['metadata'];
|
||||
fields: IFieldType[];
|
||||
}
|
||||
|
||||
|
|
|
@ -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<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
interface KibanaDeps {
|
||||
notifications: NotificationsStart;
|
||||
http: HttpStart;
|
||||
docLinks: DocLinksStart;
|
||||
application: ApplicationStart;
|
||||
}
|
||||
|
||||
export const AlertFlyout = (props: Props) => {
|
||||
const { triggersActionsUI } = useContext(TriggerActionsContext);
|
||||
const { services } = useKibana<KibanaDeps>();
|
||||
|
||||
return (
|
||||
<>
|
||||
{triggersActionsUI && (
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
metadata: {
|
||||
isInternal: true,
|
||||
},
|
||||
toastNotifications: services.notifications.toasts,
|
||||
http: services.http,
|
||||
docLinks: services.docLinks,
|
||||
capabilities: services.application.capabilities,
|
||||
actionTypeRegistry: triggersActionsUI.actionTypeRegistry,
|
||||
alertTypeRegistry: triggersActionsUI.alertTypeRegistry,
|
||||
}}
|
||||
>
|
||||
<AlertAdd
|
||||
addFlyoutVisible={props.visible!}
|
||||
setAddFlyoutVisibility={props.setVisible}
|
||||
alertTypeId={LOG_DOCUMENT_COUNT_ALERT_TYPE_ID}
|
||||
canChangeTrigger={false}
|
||||
consumer={'logs'}
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
)}
|
||||
</>
|
||||
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}</>;
|
||||
};
|
||||
|
|
|
@ -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<AlertParams>;
|
||||
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<CriteriaWrapperProps> = (props) => {
|
|||
fields,
|
||||
errors,
|
||||
alertParams,
|
||||
context,
|
||||
sourceId,
|
||||
isRatio = false,
|
||||
} = props;
|
||||
|
@ -108,7 +105,6 @@ const CriteriaWrapper: React.FC<CriteriaWrapperProps> = (props) => {
|
|||
>
|
||||
<CriterionPreview
|
||||
alertParams={alertParams}
|
||||
context={context}
|
||||
chartCriterion={criterion}
|
||||
sourceId={sourceId}
|
||||
showThreshold={!isRatio}
|
||||
|
@ -127,7 +123,6 @@ interface RatioCriteriaProps {
|
|||
fields: SharedProps['fields'];
|
||||
criteria: RatioCriteriaType;
|
||||
errors: Errors['criteria'];
|
||||
context: SharedProps['context'];
|
||||
sourceId: SharedProps['sourceId'];
|
||||
updateCriteria: (criteria: AlertParams['criteria']) => 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;
|
||||
}
|
||||
|
|
|
@ -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<AlertParams>;
|
||||
context: AlertsContext;
|
||||
chartCriterion: Partial<Criterion>;
|
||||
sourceId: string;
|
||||
showThreshold: boolean;
|
||||
|
@ -59,7 +58,6 @@ interface Props {
|
|||
|
||||
export const CriterionPreview: React.FC<Props> = ({
|
||||
alertParams,
|
||||
context,
|
||||
chartCriterion,
|
||||
sourceId,
|
||||
showThreshold,
|
||||
|
@ -91,7 +89,6 @@ export const CriterionPreview: React.FC<Props> = ({
|
|||
? 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<Props> = ({
|
|||
|
||||
interface ChartProps {
|
||||
buckets: number;
|
||||
context: AlertsContext;
|
||||
sourceId: string;
|
||||
threshold?: Threshold;
|
||||
chartAlertParams: GetLogAlertsChartPreviewDataAlertParamsSubset;
|
||||
|
@ -111,13 +107,13 @@ interface ChartProps {
|
|||
|
||||
const CriterionPreviewChart: React.FC<ChartProps> = ({
|
||||
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<ChartProps> = ({
|
|||
hasError,
|
||||
chartPreviewData: series,
|
||||
} = useChartPreviewData({
|
||||
context,
|
||||
sourceId,
|
||||
alertParams: chartAlertParams,
|
||||
buckets,
|
||||
|
|
|
@ -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<LogsContextMeta>;
|
||||
interface Props {
|
||||
errors: Errors;
|
||||
alertParams: Partial<AlertParams>;
|
||||
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> = (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> = (props) => {
|
|||
<Editor {...props} sourceId={sourceId} />
|
||||
</SourceStatusWrapper>
|
||||
) : (
|
||||
<LogSourceProvider sourceId={sourceId} fetch={props.alertsContext.http.fetch}>
|
||||
<LogSourceProvider sourceId={sourceId} fetch={http!.fetch}>
|
||||
<SourceStatusWrapper {...props}>
|
||||
<Editor {...props} sourceId={sourceId} />
|
||||
</SourceStatusWrapper>
|
||||
|
@ -139,7 +137,7 @@ export const SourceStatusWrapper: React.FC<Props> = (props) => {
|
|||
};
|
||||
|
||||
export const Editor: React.FC<Props> = (props) => {
|
||||
const { setAlertParams, alertParams, errors, alertsContext, sourceId } = props;
|
||||
const { setAlertParams, alertParams, errors, sourceId } = props;
|
||||
const [hasSetDefaults, setHasSetDefaults] = useState<boolean>(false);
|
||||
const { sourceStatus } = useLogSourceContext();
|
||||
useMount(() => {
|
||||
|
@ -228,7 +226,6 @@ export const Editor: React.FC<Props> = (props) => {
|
|||
criteria={alertParams.criteria}
|
||||
errors={errors.criteria}
|
||||
alertParams={alertParams}
|
||||
context={alertsContext}
|
||||
sourceId={sourceId}
|
||||
updateCriteria={updateCriteria}
|
||||
/>
|
||||
|
|
|
@ -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
|
||||
) => {
|
||||
|
|
|
@ -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<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
interface KibanaDeps {
|
||||
notifications: NotificationsStart;
|
||||
http: HttpStart;
|
||||
docLinks: DocLinksStart;
|
||||
application: ApplicationStart;
|
||||
}
|
||||
|
||||
export const AlertFlyout = (props: Props) => {
|
||||
const { triggersActionsUI } = useContext(TriggerActionsContext);
|
||||
const { services } = useKibana<KibanaDeps>();
|
||||
|
||||
return (
|
||||
<>
|
||||
{triggersActionsUI && (
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
metadata: {
|
||||
currentOptions: props.options,
|
||||
series: props.series,
|
||||
},
|
||||
toastNotifications: services.notifications.toasts,
|
||||
http: services.http,
|
||||
docLinks: services.docLinks,
|
||||
capabilities: services.application.capabilities,
|
||||
actionTypeRegistry: triggersActionsUI.actionTypeRegistry,
|
||||
alertTypeRegistry: triggersActionsUI.alertTypeRegistry,
|
||||
}}
|
||||
>
|
||||
<AlertAdd
|
||||
addFlyoutVisible={props.visible!}
|
||||
setAddFlyoutVisibility={props.setVisible}
|
||||
alertTypeId={METRIC_THRESHOLD_ALERT_TYPE_ID}
|
||||
canChangeTrigger={false}
|
||||
consumer={'infrastructure'}
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
)}
|
||||
</>
|
||||
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}</>;
|
||||
};
|
||||
|
|
|
@ -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<AlertContextMeta> = {
|
||||
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(
|
||||
<Expressions
|
||||
alertsContext={context}
|
||||
alertInterval="1m"
|
||||
alertThrottle="1m"
|
||||
alertParams={alertParams}
|
||||
errors={[]}
|
||||
setAlertParams={(key, value) => Reflect.set(alertParams, key, value)}
|
||||
setAlertProperty={() => {}}
|
||||
metadata={{
|
||||
currentOptions,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -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<AlertContextMeta>;
|
||||
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> = (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<number | undefined>(1);
|
||||
|
@ -88,15 +81,15 @@ export const Expressions: React.FC<Props> = (props) => {
|
|||
]);
|
||||
|
||||
const options = useMemo<MetricsExplorerOptions>(() => {
|
||||
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> = (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> = (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> = (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> = (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<HTMLInputElement>) => onFilterChange(e.target.value),
|
||||
|
@ -291,7 +284,6 @@ export const Expressions: React.FC<Props> = (props) => {
|
|||
>
|
||||
<ExpressionChart
|
||||
expression={e}
|
||||
context={alertsContext}
|
||||
derivedIndexPattern={derivedIndexPattern}
|
||||
source={source}
|
||||
filterQuery={alertParams.filterQueryText}
|
||||
|
@ -361,7 +353,7 @@ export const Expressions: React.FC<Props> = (props) => {
|
|||
fullWidth
|
||||
display="rowCompressed"
|
||||
>
|
||||
{(alertsContext.metadata && (
|
||||
{(metadata && (
|
||||
<MetricsExplorerKueryBar
|
||||
derivedIndexPattern={derivedIndexPattern}
|
||||
onChange={debouncedOnFilterChange}
|
||||
|
@ -407,7 +399,6 @@ export const Expressions: React.FC<Props> = (props) => {
|
|||
alertParams={pick(alertParams, 'criteria', 'groupBy', 'filterQuery', 'sourceId')}
|
||||
showNoDataResults={alertParams.alertOnNoData}
|
||||
validate={validateMetricThreshold}
|
||||
fetch={alertsContext.http.fetch}
|
||||
groupByDisplayName={groupByPreviewDisplayName}
|
||||
/>
|
||||
<EuiSpacer size={'m'} />
|
||||
|
|
|
@ -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';
|
||||
|
||||
const mockStartServices = mockCoreMock.createStart();
|
||||
jest.mock('../../../hooks/use_kibana', () => ({
|
||||
useKibanaContextForPlugin: () => ({
|
||||
services: {
|
||||
...mockStartServices,
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
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,
|
||||
response: MetricsExplorerResponse | null,
|
||||
filterQuery?: string,
|
||||
groupBy?: string
|
||||
) {
|
||||
const mocks = coreMock.createSetup();
|
||||
const startMocks = coreMock.createStart();
|
||||
const [
|
||||
{
|
||||
application: { capabilities },
|
||||
},
|
||||
] = await mocks.getStartServices();
|
||||
|
||||
const context: AlertsContextValue<AlertContextMeta> = {
|
||||
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,
|
||||
},
|
||||
},
|
||||
};
|
||||
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(
|
||||
<ExpressionChart
|
||||
context={context}
|
||||
expression={expression}
|
||||
derivedIndexPattern={derivedIndexPattern}
|
||||
source={source}
|
||||
|
@ -97,7 +84,7 @@ describe('ExpressionChart', () => {
|
|||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<AlertContextMeta>;
|
||||
expression: MetricExpression;
|
||||
derivedIndexPattern: IIndexPattern;
|
||||
source: InfraSource | null;
|
||||
|
@ -62,7 +60,6 @@ const TIME_LABELS = {
|
|||
|
||||
export const ExpressionChart: React.FC<Props> = ({
|
||||
expression,
|
||||
context,
|
||||
derivedIndexPattern,
|
||||
source,
|
||||
filterQuery,
|
||||
|
@ -70,19 +67,20 @@ export const ExpressionChart: React.FC<Props> = ({
|
|||
}) => {
|
||||
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;
|
||||
|
|
|
@ -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<AlertContextMeta>,
|
||||
derivedIndexPattern: IIndexPattern,
|
||||
source: InfraSource | null,
|
||||
filterQuery?: string,
|
||||
|
@ -54,7 +51,6 @@ export const useMetricsExplorerChartData = (
|
|||
derivedIndexPattern,
|
||||
timerange,
|
||||
null,
|
||||
null,
|
||||
context.http.fetch
|
||||
null
|
||||
);
|
||||
};
|
||||
|
|
|
@ -48,7 +48,6 @@ export const useMetricsExplorerState = (
|
|||
currentTimerange,
|
||||
afterKey,
|
||||
refreshSignal,
|
||||
undefined,
|
||||
shouldLoadImmediately
|
||||
);
|
||||
|
||||
|
|
|
@ -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<string, string | null>,
|
||||
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<Error | null>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [data, setData] = useState<MetricsExplorerResponse | null>(null);
|
||||
|
|
|
@ -24,5 +24,5 @@
|
|||
],
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"requiredBundles": ["kibanaUtils", "home", "alerts", "kibanaReact", "licenseManagement", "triggersActionsUi"]
|
||||
"requiredBundles": ["kibanaUtils", "home", "alerts", "kibanaReact", "licenseManagement"]
|
||||
}
|
||||
|
|
|
@ -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<any>;
|
||||
|
||||
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(
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
...monitoringDependencies,
|
||||
}}
|
||||
>
|
||||
<AlertForm
|
||||
alert={initialAlert}
|
||||
dispatch={() => {}}
|
||||
errors={{ name: [], interval: [] }}
|
||||
operation="create"
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
<I18nProvider>
|
||||
<KibanaReactContext.Provider>
|
||||
<AlertForm
|
||||
alert={initialAlert}
|
||||
dispatch={() => {}}
|
||||
errors={{ name: [], interval: [] }}
|
||||
operation="create"
|
||||
actionTypeRegistry={actionTypeRegistry}
|
||||
alertTypeRegistry={alertTypeRegistry}
|
||||
/>
|
||||
</KibanaReactContext.Provider>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
|
|
|
@ -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: 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: Props) => {
|
|||
setIsSaving(false);
|
||||
}
|
||||
|
||||
const flyoutUi = showFlyout ? (
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
http: Legacy.shims.http,
|
||||
actionTypeRegistry: Legacy.shims.actionTypeRegistry,
|
||||
alertTypeRegistry: Legacy.shims.alertTypeRegistry,
|
||||
toastNotifications: Legacy.shims.toastNotifications,
|
||||
uiSettings: Legacy.shims.uiSettings,
|
||||
docLinks: Legacy.shims.docLinks,
|
||||
reloadAlerts: async () => {},
|
||||
capabilities: Legacy.shims.capabilities,
|
||||
}}
|
||||
>
|
||||
<AlertEdit
|
||||
initialAlert={alert.rawAlert}
|
||||
onClose={() => {
|
||||
setShowFlyout(false);
|
||||
showBottomBar();
|
||||
}}
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
) : null;
|
||||
|
||||
const configurationUi = (
|
||||
<Fragment>
|
||||
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center" gutterSize="m">
|
||||
|
|
|
@ -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<any>;
|
||||
isCloud: boolean;
|
||||
triggersActionsUi: TriggersAndActionsUIPublicPluginSetup;
|
||||
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
|
||||
usageCollection: UsageCollectionSetup;
|
||||
kibanaServices: CoreStart & { usageCollection: UsageCollectionSetup };
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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<string | number> | Array<string | boolean>>;
|
||||
triggersActionsUi: TriggersAndActionsUIPublicPluginSetup;
|
||||
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
|
||||
usageCollection: UsageCollectionSetup;
|
||||
}
|
||||
|
|
|
@ -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<GeoContainmentAlertParams, AlertsContextValue> {
|
||||
export function getAlertType(): AlertTypeModel<GeoContainmentAlertParams> {
|
||||
return {
|
||||
id: '.geo-containment',
|
||||
name: i18n.translate('xpack.stackAlerts.geoContainment.name.trackingContainment', {
|
||||
|
|
|
@ -16,13 +16,23 @@ exports[`should render BoundaryIndexExpression 1`] = `
|
|||
labelType="label"
|
||||
>
|
||||
<GeoIndexPatternSelect
|
||||
IndexPatternSelectComponent={null}
|
||||
http={null}
|
||||
IndexPatternSelectComponent={[MockFunction]}
|
||||
includedGeoTypes={
|
||||
Array [
|
||||
"geo_shape",
|
||||
]
|
||||
}
|
||||
indexPatternService={
|
||||
Object {
|
||||
"clearCache": [MockFunction],
|
||||
"createField": [MockFunction],
|
||||
"createFieldList": [MockFunction],
|
||||
"ensureDefaultIndexPattern": [MockFunction],
|
||||
"find": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"make": [Function],
|
||||
}
|
||||
}
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
@ -82,13 +92,23 @@ exports[`should render EntityIndexExpression 1`] = `
|
|||
labelType="label"
|
||||
>
|
||||
<GeoIndexPatternSelect
|
||||
IndexPatternSelectComponent={null}
|
||||
http={null}
|
||||
IndexPatternSelectComponent={[MockFunction]}
|
||||
includedGeoTypes={
|
||||
Array [
|
||||
"geo_point",
|
||||
]
|
||||
}
|
||||
indexPatternService={
|
||||
Object {
|
||||
"clearCache": [MockFunction],
|
||||
"createField": [MockFunction],
|
||||
"createFieldList": [MockFunction],
|
||||
"ensureDefaultIndexPattern": [MockFunction],
|
||||
"find": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"make": [Function],
|
||||
}
|
||||
}
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
@ -154,13 +174,23 @@ exports[`should render EntityIndexExpression w/ invalid flag if invalid 1`] = `
|
|||
labelType="label"
|
||||
>
|
||||
<GeoIndexPatternSelect
|
||||
IndexPatternSelectComponent={null}
|
||||
http={null}
|
||||
IndexPatternSelectComponent={[MockFunction]}
|
||||
includedGeoTypes={
|
||||
Array [
|
||||
"geo_point",
|
||||
]
|
||||
}
|
||||
indexPatternService={
|
||||
Object {
|
||||
"clearCache": [MockFunction],
|
||||
"createField": [MockFunction],
|
||||
"createFieldList": [MockFunction],
|
||||
"ensureDefaultIndexPattern": [MockFunction],
|
||||
"find": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"make": [Function],
|
||||
}
|
||||
}
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
|
|
@ -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<Props> = ({
|
||||
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<KibanaDeps>().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<Props> = ({
|
|||
}}
|
||||
value={boundaryIndexPattern.id}
|
||||
IndexPatternSelectComponent={IndexPatternSelect}
|
||||
indexPatternService={dataIndexPatterns}
|
||||
indexPatternService={data.indexPatterns}
|
||||
http={http}
|
||||
includedGeoTypes={ES_GEO_SHAPE_TYPES}
|
||||
/>
|
||||
|
|
|
@ -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<Props> = ({
|
||||
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<KibanaDeps>().services;
|
||||
const IndexPatternSelect = (data.ui && data.ui.IndexPatternSelect) || null;
|
||||
|
||||
const usePrevious = <T extends unknown>(value: T): T | undefined => {
|
||||
const ref = useRef<T>();
|
||||
|
@ -98,7 +104,7 @@ export const EntityIndexExpression: FunctionComponent<Props> = ({
|
|||
}}
|
||||
value={indexPattern.id}
|
||||
IndexPatternSelectComponent={IndexPatternSelect}
|
||||
indexPatternService={dataIndexPatterns}
|
||||
indexPatternService={data.indexPatterns}
|
||||
http={http}
|
||||
includedGeoTypes={ES_GEO_FIELD_TYPES}
|
||||
/>
|
||||
|
|
|
@ -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 () => {
|
|||
<EntityIndexExpression
|
||||
dateField={'testDateField'}
|
||||
geoField={'testGeoField'}
|
||||
alertsContext={alertsContext}
|
||||
errors={{} as IErrorObject}
|
||||
setAlertParamsDate={() => {}}
|
||||
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 ()
|
|||
<EntityIndexExpression
|
||||
dateField={'testDateField'}
|
||||
geoField={'testGeoField'}
|
||||
alertsContext={alertsContext}
|
||||
errors={{} as IErrorObject}
|
||||
setAlertParamsDate={() => {}}
|
||||
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(
|
||||
<BoundaryIndexExpression
|
||||
alertParams={alertParams}
|
||||
alertsContext={alertsContext}
|
||||
errors={{} as IErrorObject}
|
||||
boundaryIndexPattern={('' as unknown) as IIndexPattern}
|
||||
setBoundaryIndexPattern={() => {}}
|
||||
setBoundaryGeoField={() => {}}
|
||||
setBoundaryNameField={() => {}}
|
||||
boundaryNameField={'testNameField'}
|
||||
data={dataStartMock}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -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<GeoContainmentAlertParams, AlertsContextValue>
|
||||
> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, alertsContext }) => {
|
||||
AlertTypeParamsExpressionProps<GeoContainmentAlertParams>
|
||||
> = ({ 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<
|
|||
<EntityIndexExpression
|
||||
dateField={dateField}
|
||||
geoField={geoField}
|
||||
alertsContext={alertsContext}
|
||||
errors={errors}
|
||||
setAlertParamsDate={(_date) => 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}
|
||||
/>
|
||||
<EntityByExpression
|
||||
errors={errors}
|
||||
|
@ -220,7 +217,6 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent<
|
|||
<EuiSpacer size="s" />
|
||||
<BoundaryIndexExpression
|
||||
alertParams={alertParams}
|
||||
alertsContext={alertsContext}
|
||||
errors={errors}
|
||||
boundaryIndexPattern={boundaryIndexPattern}
|
||||
setBoundaryIndexPattern={setBoundaryIndexPattern}
|
||||
|
@ -233,6 +229,7 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent<
|
|||
: setAlertParams('boundaryNameField', '')
|
||||
}
|
||||
boundaryNameField={boundaryNameField}
|
||||
data={data}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexItem>
|
||||
|
|
|
@ -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<GeoThresholdAlertParams, AlertsContextValue> {
|
||||
export function getAlertType(): AlertTypeModel<GeoThresholdAlertParams> {
|
||||
return {
|
||||
id: '.geo-threshold',
|
||||
name: i18n.translate('xpack.stackAlerts.geoThreshold.name.trackingThreshold', {
|
||||
|
|
|
@ -16,13 +16,23 @@ exports[`should render BoundaryIndexExpression 1`] = `
|
|||
labelType="label"
|
||||
>
|
||||
<GeoIndexPatternSelect
|
||||
IndexPatternSelectComponent={null}
|
||||
http={null}
|
||||
IndexPatternSelectComponent={[MockFunction]}
|
||||
includedGeoTypes={
|
||||
Array [
|
||||
"geo_shape",
|
||||
]
|
||||
}
|
||||
indexPatternService={
|
||||
Object {
|
||||
"clearCache": [MockFunction],
|
||||
"createField": [MockFunction],
|
||||
"createFieldList": [MockFunction],
|
||||
"ensureDefaultIndexPattern": [MockFunction],
|
||||
"find": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"make": [Function],
|
||||
}
|
||||
}
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
@ -82,13 +92,23 @@ exports[`should render EntityIndexExpression 1`] = `
|
|||
labelType="label"
|
||||
>
|
||||
<GeoIndexPatternSelect
|
||||
IndexPatternSelectComponent={null}
|
||||
http={null}
|
||||
IndexPatternSelectComponent={[MockFunction]}
|
||||
includedGeoTypes={
|
||||
Array [
|
||||
"geo_point",
|
||||
]
|
||||
}
|
||||
indexPatternService={
|
||||
Object {
|
||||
"clearCache": [MockFunction],
|
||||
"createField": [MockFunction],
|
||||
"createFieldList": [MockFunction],
|
||||
"ensureDefaultIndexPattern": [MockFunction],
|
||||
"find": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"make": [Function],
|
||||
}
|
||||
}
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
@ -154,13 +174,23 @@ exports[`should render EntityIndexExpression w/ invalid flag if invalid 1`] = `
|
|||
labelType="label"
|
||||
>
|
||||
<GeoIndexPatternSelect
|
||||
IndexPatternSelectComponent={null}
|
||||
http={null}
|
||||
IndexPatternSelectComponent={[MockFunction]}
|
||||
includedGeoTypes={
|
||||
Array [
|
||||
"geo_point",
|
||||
]
|
||||
}
|
||||
indexPatternService={
|
||||
Object {
|
||||
"clearCache": [MockFunction],
|
||||
"createField": [MockFunction],
|
||||
"createFieldList": [MockFunction],
|
||||
"ensureDefaultIndexPattern": [MockFunction],
|
||||
"find": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"make": [Function],
|
||||
}
|
||||
}
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
|
|
@ -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<Props> = ({
|
||||
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<KibanaDeps>().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<Props> = ({
|
|||
}}
|
||||
value={boundaryIndexPattern.id}
|
||||
IndexPatternSelectComponent={IndexPatternSelect}
|
||||
indexPatternService={dataIndexPatterns}
|
||||
indexPatternService={data.indexPatterns}
|
||||
http={http}
|
||||
includedGeoTypes={ES_GEO_SHAPE_TYPES}
|
||||
/>
|
||||
|
|
|
@ -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<Props> = ({
|
||||
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 = <T extends unknown>(value: T): T | undefined => {
|
||||
const ref = useRef<T>();
|
||||
|
@ -98,8 +99,8 @@ export const EntityIndexExpression: FunctionComponent<Props> = ({
|
|||
}}
|
||||
value={indexPattern.id}
|
||||
IndexPatternSelectComponent={IndexPatternSelect}
|
||||
indexPatternService={dataIndexPatterns}
|
||||
http={http}
|
||||
indexPatternService={data.indexPatterns}
|
||||
http={http!}
|
||||
includedGeoTypes={ES_GEO_FIELD_TYPES}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
|
|
@ -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(
|
||||
<EntityIndexExpression
|
||||
dateField={'testDateField'}
|
||||
geoField={'testGeoField'}
|
||||
alertsContext={alertsContext}
|
||||
errors={{} as IErrorObject}
|
||||
setAlertParamsDate={() => {}}
|
||||
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 ()
|
|||
<EntityIndexExpression
|
||||
dateField={'testDateField'}
|
||||
geoField={'testGeoField'}
|
||||
alertsContext={alertsContext}
|
||||
errors={{} as IErrorObject}
|
||||
setAlertParamsDate={() => {}}
|
||||
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(
|
||||
<BoundaryIndexExpression
|
||||
alertParams={alertParams}
|
||||
alertsContext={alertsContext}
|
||||
errors={{} as IErrorObject}
|
||||
boundaryIndexPattern={('' as unknown) as IIndexPattern}
|
||||
setBoundaryIndexPattern={() => {}}
|
||||
setBoundaryGeoField={() => {}}
|
||||
setBoundaryNameField={() => {}}
|
||||
boundaryNameField={'testNameField'}
|
||||
data={dataStartMock}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -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<GeoThresholdAlertParams, AlertsContextValue>
|
||||
> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, alertsContext }) => {
|
||||
AlertTypeParamsExpressionProps<GeoThresholdAlertParams>
|
||||
> = ({ 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<
|
|||
<EntityIndexExpression
|
||||
dateField={dateField}
|
||||
geoField={geoField}
|
||||
alertsContext={alertsContext}
|
||||
errors={errors}
|
||||
setAlertParamsDate={(_date) => 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}
|
||||
/>
|
||||
<EntityByExpression
|
||||
errors={errors}
|
||||
|
@ -347,7 +346,6 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<
|
|||
<EuiSpacer size="s" />
|
||||
<BoundaryIndexExpression
|
||||
alertParams={alertParams}
|
||||
alertsContext={alertsContext}
|
||||
errors={errors}
|
||||
boundaryIndexPattern={boundaryIndexPattern}
|
||||
setBoundaryIndexPattern={setBoundaryIndexPattern}
|
||||
|
@ -360,6 +358,7 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<
|
|||
: setAlertParams('boundaryNameField', '')
|
||||
}
|
||||
boundaryNameField={boundaryNameField}
|
||||
data={data}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexItem>
|
||||
|
|
|
@ -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<IndexThresholdAlertParams, AlertsContextValue>
|
||||
> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, alertsContext }) => {
|
||||
AlertTypeParamsExpressionProps<IndexThresholdAlertParams>
|
||||
> = ({ 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<KibanaDeps>().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}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
|
|
|
@ -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<IndexThresholdAlertParams, AlertsContextValue> {
|
||||
export function getAlertType(): AlertTypeModel<IndexThresholdAlertParams> {
|
||||
return {
|
||||
id: '.index-threshold',
|
||||
name: i18n.translate('xpack.stackAlerts.threshold.ui.alertType.nameText', {
|
||||
|
|
|
@ -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<Props> = ({
|
|||
alertInterval,
|
||||
aggregationTypes,
|
||||
comparators,
|
||||
alertsContext,
|
||||
refreshRateInMilliseconds = DEFAULT_REFRESH_RATE,
|
||||
charts,
|
||||
dataFieldsFormats,
|
||||
}) => {
|
||||
const {
|
||||
index,
|
||||
|
@ -128,8 +129,7 @@ export const ThresholdVisualization: React.FunctionComponent<Props> = ({
|
|||
groupBy,
|
||||
threshold,
|
||||
} = alertParams;
|
||||
const { http, toastNotifications, charts, uiSettings, dataFieldsFormats } = alertsContext;
|
||||
|
||||
const { http, notifications, uiSettings } = useKibana().services;
|
||||
const [loadingState, setLoadingState] = useState<LoadingStateType | null>(null);
|
||||
const [error, setError] = useState<undefined | Error>(undefined);
|
||||
const [visualizationData, setVisualizationData] = useState<Record<string, MetricResult[]>>();
|
||||
|
@ -150,11 +150,11 @@ export const ThresholdVisualization: React.FunctionComponent<Props> = ({
|
|||
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' }
|
||||
|
|
|
@ -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<IndexThr
|
|||
setAlertParams,
|
||||
setAlertProperty,
|
||||
errors,
|
||||
alertsContext,
|
||||
}) => {
|
||||
|
||||
....
|
||||
|
@ -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<AlertTypeParamsExpressionProps<AlertParamsType, AlertsContextValue>>
|
||||
ComponentType<AlertTypeParamsExpressionProps<AlertParamsType>>
|
||||
>;
|
||||
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<boolean>(false);
|
||||
|
||||
|
@ -815,26 +809,22 @@ const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState<boolean>(false);
|
|||
/>
|
||||
</EuiButton>
|
||||
|
||||
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
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
http,
|
||||
actionTypeRegistry: triggersActionsUi.actionTypeRegistry,
|
||||
alertTypeRegistry: triggersActionsUi.alertTypeRegistry,
|
||||
toastNotifications: toasts,
|
||||
uiSettings,
|
||||
docLinks,
|
||||
charts,
|
||||
dataFieldsFormats,
|
||||
metadata: { test: 'some value', fields: ['test'] },
|
||||
}}
|
||||
>
|
||||
<AlertAdd consumer={'watcher'} addFlyoutVisible={alertFlyoutVisible}
|
||||
setAddFlyoutVisibility={setAlertFlyoutVisibility} />
|
||||
</AlertsContextProvider>
|
||||
return <>{AddAlertFlyout}</>;
|
||||
```
|
||||
|
||||
AlertAdd Props definition:
|
||||
getAddAlertFlyout variables definition:
|
||||
```
|
||||
interface AlertAddProps {
|
||||
consumer: string;
|
||||
|
@ -842,6 +832,9 @@ interface AlertAddProps {
|
|||
setAddFlyoutVisibility: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
alertTypeId?: string;
|
||||
canChangeTrigger?: boolean;
|
||||
initialValues?: Partial<Alert>;
|
||||
reloadAlerts?: () => Promise<void>;
|
||||
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<MetaData = Record<string, any>> {
|
||||
reloadAlerts?: () => Promise<void>;
|
||||
http: HttpSetup;
|
||||
alertTypeRegistry: TypeRegistry<AlertTypeModel>;
|
||||
actionTypeRegistry: TypeRegistry<ActionTypeModel>;
|
||||
uiSettings?: IUiSettingsClient;
|
||||
docLinks: DocLinksStart;
|
||||
toastNotifications: Pick<
|
||||
ToastsApi,
|
||||
'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError'
|
||||
>;
|
||||
charts?: ChartsPluginSetup;
|
||||
dataFieldsFormats?: Pick<FieldFormatsRegistry, 'register'>;
|
||||
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<void>;
|
||||
http: HttpSetup;
|
||||
alertTypeRegistry: TypeRegistry<AlertTypeModel>;
|
||||
actionTypeRegistry: TypeRegistry<ActionTypeModel>;
|
||||
uiSettings?: IUiSettingsClient;
|
||||
docLinks: DocLinksStart;
|
||||
toastNotifications: Pick<
|
||||
ToastsApi,
|
||||
'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError'
|
||||
>;
|
||||
charts?: ChartsPluginSetup;
|
||||
dataFieldsFormats?: Pick<FieldFormatsRegistry, 'register'>;
|
||||
}
|
||||
```
|
||||
|
||||
|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<ActionTypeModel>;
|
||||
toastNotifications: Pick<
|
||||
ToastsApi,
|
||||
'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError'
|
||||
>;
|
||||
capabilities: ApplicationStart['capabilities'];
|
||||
docLinks: DocLinksStart;
|
||||
reloadConnectors?: () => Promise<void>;
|
||||
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:
|
||||
|
|
|
@ -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<typeof useKibana>;
|
||||
|
||||
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(
|
||||
<HealthContextProvider>
|
||||
<HealthCheck http={http} docLinks={docLinks} waitForCheck={true}>
|
||||
<HealthCheck waitForCheck={true}>
|
||||
<p>{'shouldnt render'}</p>
|
||||
</HealthCheck>
|
||||
</HealthContextProvider>
|
||||
|
@ -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(
|
||||
<HealthContextProvider>
|
||||
<HealthCheck http={http} docLinks={docLinks} waitForCheck={false}>
|
||||
<HealthCheck waitForCheck={false}>
|
||||
<p>{'should render'}</p>
|
||||
</HealthCheck>
|
||||
</HealthContextProvider>
|
||||
|
@ -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(
|
||||
<HealthContextProvider>
|
||||
<HealthCheck http={http} docLinks={docLinks} waitForCheck={true}>
|
||||
<HealthCheck waitForCheck={true}>
|
||||
<p>{'should render'}</p>
|
||||
</HealthCheck>
|
||||
</HealthContextProvider>
|
||||
|
@ -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(
|
||||
<HealthContextProvider>
|
||||
<HealthCheck http={http} docLinks={docLinks} waitForCheck={true}>
|
||||
<HealthCheck waitForCheck={true}>
|
||||
<p>{'should render'}</p>
|
||||
</HealthCheck>
|
||||
</HealthContextProvider>
|
||||
|
@ -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(
|
||||
<HealthContextProvider>
|
||||
<HealthCheck http={http} docLinks={docLinks} waitForCheck={true}>
|
||||
<HealthCheck waitForCheck={true}>
|
||||
<p>{'should render'}</p>
|
||||
</HealthCheck>
|
||||
</HealthContextProvider>
|
||||
|
@ -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(
|
||||
<HealthContextProvider>
|
||||
<HealthCheck http={http} docLinks={docLinks} waitForCheck={true}>
|
||||
<HealthCheck waitForCheck={true}>
|
||||
<p>{'should render'}</p>
|
||||
</HealthCheck>
|
||||
</HealthContextProvider>
|
||||
|
@ -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"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<DocLinksStart, 'ELASTIC_WEBSITE_URL' | 'DOC_LINK_VERSION'>;
|
||||
http: HttpSetup;
|
||||
inFlyout?: boolean;
|
||||
waitForCheck: boolean;
|
||||
}
|
||||
|
||||
export const HealthCheck: React.FunctionComponent<Props> = ({
|
||||
docLinks,
|
||||
http,
|
||||
children,
|
||||
waitForCheck,
|
||||
inFlyout = false,
|
||||
}) => {
|
||||
const { http, docLinks } = useKibana().services;
|
||||
const { setLoadingHealthCheck } = useHealthContext();
|
||||
const [alertingHealth, setAlertingHealth] = React.useState<Option<AlertingFrameworkHealth>>(none);
|
||||
|
||||
|
@ -66,9 +63,10 @@ export const HealthCheck: React.FunctionComponent<Props> = ({
|
|||
);
|
||||
};
|
||||
|
||||
type PromptErrorProps = Pick<Props, 'docLinks'> & {
|
||||
interface PromptErrorProps {
|
||||
docLinks: Pick<DocLinksStart, 'ELASTIC_WEBSITE_URL' | 'DOC_LINK_VERSION'>;
|
||||
className?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const TlsAndEncryptionError = ({
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
|
|
@ -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<MetaData = Record<string, any>> {
|
||||
reloadAlerts?: () => Promise<void>;
|
||||
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<AlertsContextValue>(null as any);
|
||||
|
||||
export const AlertsContextProvider = ({
|
||||
children,
|
||||
value,
|
||||
}: {
|
||||
value: AlertsContextValue;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return <AlertsContext.Provider value={value}>{children}</AlertsContext.Provider>;
|
||||
};
|
||||
|
||||
export const useAlertsContext = () => {
|
||||
const ctx = useContext(AlertsContext);
|
||||
if (!ctx) {
|
||||
throw new Error('AlertsContext has not been set.');
|
||||
}
|
||||
return ctx;
|
||||
};
|
|
@ -46,7 +46,6 @@ export const TriggersActionsUIHome: React.FunctionComponent<RouteComponentProps<
|
|||
application: { capabilities },
|
||||
setBreadcrumbs,
|
||||
docLinks,
|
||||
http,
|
||||
} = useKibana().services;
|
||||
|
||||
const canShowActions = hasShowActionsCapability(capabilities);
|
||||
|
@ -144,7 +143,7 @@ export const TriggersActionsUIHome: React.FunctionComponent<RouteComponentProps<
|
|||
path={routeToConnectors}
|
||||
component={() => (
|
||||
<HealthContextProvider>
|
||||
<HealthCheck docLinks={docLinks} http={http} waitForCheck={true}>
|
||||
<HealthCheck waitForCheck={true}>
|
||||
<ActionsConnectorsList />
|
||||
</HealthCheck>
|
||||
</HealthContextProvider>
|
||||
|
@ -156,7 +155,7 @@ export const TriggersActionsUIHome: React.FunctionComponent<RouteComponentProps<
|
|||
path={routeToAlerts}
|
||||
component={() => (
|
||||
<HealthContextProvider>
|
||||
<HealthCheck docLinks={docLinks} http={http} inFlyout={true} waitForCheck={true}>
|
||||
<HealthCheck inFlyout={true} waitForCheck={true}>
|
||||
<AlertsList />
|
||||
</HealthCheck>
|
||||
</HealthContextProvider>
|
||||
|
|
|
@ -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<AlertDetailsProps> = ({
|
|||
}) => {
|
||||
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<AlertDetailsProps> = ({
|
|||
/>
|
||||
</EuiButtonEmpty>
|
||||
{editFlyoutVisible && (
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
http,
|
||||
actionTypeRegistry,
|
||||
alertTypeRegistry,
|
||||
toastNotifications: toasts,
|
||||
uiSettings,
|
||||
docLinks,
|
||||
charts,
|
||||
dataFieldsFormats: data.fieldFormats,
|
||||
reloadAlerts: setAlert,
|
||||
capabilities,
|
||||
dataUi: data.ui,
|
||||
dataIndexPatterns: data.indexPatterns,
|
||||
<AlertEdit
|
||||
initialAlert={alert}
|
||||
onClose={() => {
|
||||
setInitialAlert(alert);
|
||||
setEditFlyoutVisibility(false);
|
||||
}}
|
||||
>
|
||||
<AlertEdit
|
||||
initialAlert={alert}
|
||||
onClose={() => {
|
||||
setInitialAlert(alert);
|
||||
setEditFlyoutVisibility(false);
|
||||
}}
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
actionTypeRegistry={actionTypeRegistry}
|
||||
alertTypeRegistry={alertTypeRegistry}
|
||||
reloadAlerts={setAlert}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -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<typeof useKibana>;
|
||||
|
||||
export const TestExpression: React.FunctionComponent<any> = () => {
|
||||
const alertsContext = useAlertsContext();
|
||||
const { metadata } = alertsContext;
|
||||
|
||||
return (
|
||||
<EuiFormLabel>
|
||||
<FormattedMessage
|
||||
defaultMessage="Metadata: {val}. Fields: {fields}."
|
||||
id="xpack.triggersActionsUI.sections.alertAdd.metadataTest"
|
||||
values={{ val: metadata!.test, fields: metadata!.fields.join(' ') }}
|
||||
values={{ val: 'test', fields: '' }}
|
||||
/>
|
||||
</EuiFormLabel>
|
||||
);
|
||||
};
|
||||
|
||||
describe('alert_add', () => {
|
||||
let deps: any;
|
||||
let wrapper: ReactWrapper<any>;
|
||||
|
||||
async function setup(initialValues?: Partial<Alert>) {
|
||||
|
@ -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(
|
||||
<KibanaContextProvider services={{ ...deps }}>
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
reloadAlerts: () => {
|
||||
return new Promise<void>(() => {});
|
||||
},
|
||||
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,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AlertAdd
|
||||
consumer={ALERTS_FEATURE_ID}
|
||||
addFlyoutVisible={true}
|
||||
setAddFlyoutVisibility={() => {}}
|
||||
initialValues={initialValues}
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
</KibanaContextProvider>
|
||||
<AlertAdd
|
||||
consumer={ALERTS_FEATURE_ID}
|
||||
addFlyoutVisible={true}
|
||||
setAddFlyoutVisibility={() => {}}
|
||||
initialValues={initialValues}
|
||||
reloadAlerts={() => {
|
||||
return new Promise<void>(() => {});
|
||||
}}
|
||||
actionTypeRegistry={actionTypeRegistry}
|
||||
alertTypeRegistry={alertTypeRegistry}
|
||||
metadata={{ test: 'some value', fields: ['test'] }}
|
||||
/>
|
||||
);
|
||||
|
||||
// Wait for active space to resolve before requesting the component to update
|
||||
|
|
|
@ -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<MetaData = Record<string, any>> {
|
||||
consumer: string;
|
||||
addFlyoutVisible: boolean;
|
||||
alertTypeRegistry: AlertTypeRegistryContract;
|
||||
actionTypeRegistry: ActionTypeRegistryContract;
|
||||
setAddFlyoutVisibility: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
alertTypeId?: string;
|
||||
canChangeTrigger?: boolean;
|
||||
initialValues?: Partial<Alert>;
|
||||
reloadAlerts?: () => Promise<void>;
|
||||
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 = ({
|
|||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<HealthContextProvider>
|
||||
<HealthCheck docLinks={docLinks} http={http} inFlyout={true} waitForCheck={false}>
|
||||
<HealthCheck inFlyout={true} waitForCheck={false}>
|
||||
<EuiFlyoutBody>
|
||||
<AlertForm
|
||||
alert={alert}
|
||||
|
@ -179,6 +189,9 @@ export const AlertAdd = ({
|
|||
defaultMessage: 'create',
|
||||
}
|
||||
)}
|
||||
actionTypeRegistry={actionTypeRegistry}
|
||||
alertTypeRegistry={alertTypeRegistry}
|
||||
metadata={metadata}
|
||||
/>
|
||||
</EuiFlyoutBody>
|
||||
<AlertAddFooter
|
||||
|
|
|
@ -9,16 +9,16 @@ import { act } from 'react-dom/test-utils';
|
|||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
|
||||
import { ValidationResult, Alert } from '../../../types';
|
||||
import { AlertsContextProvider } from '../../context/alerts_context';
|
||||
import { alertTypeRegistryMock } from '../../alert_type_registry.mock';
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
import AlertEdit from './alert_edit';
|
||||
import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
import { useKibana } from '../../../common/lib/kibana';
|
||||
jest.mock('../../../common/lib/kibana');
|
||||
const actionTypeRegistry = actionTypeRegistryMock.create();
|
||||
const alertTypeRegistry = alertTypeRegistryMock.create();
|
||||
const useKibanaMock = useKibana as jest.Mocked<typeof useKibana>;
|
||||
|
||||
describe('alert_edit', () => {
|
||||
let deps: any;
|
||||
let wrapper: ReactWrapper<any>;
|
||||
let mockedCoreSetup: ReturnType<typeof coreMock.createSetup>;
|
||||
|
||||
|
@ -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(
|
||||
<KibanaContextProvider services={deps}>
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
reloadAlerts: () => {
|
||||
return new Promise<void>(() => {});
|
||||
},
|
||||
http: deps!.http,
|
||||
actionTypeRegistry: deps!.actionTypeRegistry,
|
||||
alertTypeRegistry: deps!.alertTypeRegistry,
|
||||
toastNotifications: deps!.toastNotifications,
|
||||
uiSettings: deps!.uiSettings,
|
||||
docLinks: deps.docLinks,
|
||||
capabilities: deps!.capabilities,
|
||||
}}
|
||||
>
|
||||
<AlertEdit onClose={() => {}} initialAlert={alert} />
|
||||
</AlertsContextProvider>
|
||||
</KibanaContextProvider>
|
||||
<AlertEdit
|
||||
onClose={() => {}}
|
||||
initialAlert={alert}
|
||||
reloadAlerts={() => {
|
||||
return new Promise<void>(() => {});
|
||||
}}
|
||||
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'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<MetaData = Record<string, any>> {
|
||||
initialAlert: Alert;
|
||||
alertTypeRegistry: AlertTypeRegistryContract;
|
||||
actionTypeRegistry: ActionTypeRegistryContract;
|
||||
onClose(): void;
|
||||
reloadAlerts?: () => Promise<void>;
|
||||
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<Alert | undefined> {
|
||||
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) => {
|
|||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<HealthContextProvider>
|
||||
<HealthCheck docLinks={docLinks} http={http} inFlyout={true} waitForCheck={true}>
|
||||
<HealthCheck inFlyout={true} waitForCheck={true}>
|
||||
<EuiFlyoutBody>
|
||||
{hasActionsDisabled && (
|
||||
<Fragment>
|
||||
|
@ -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}
|
||||
/>
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
|
|
|
@ -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: () => <Fragment />,
|
||||
requiresAppContext: true,
|
||||
};
|
||||
const useKibanaMock = useKibana as jest.Mocked<typeof useKibana>;
|
||||
|
||||
describe('alert_form create alert', () => {
|
||||
let wrapper: ReactWrapper<any>;
|
||||
|
@ -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(
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
reloadAlerts: () => {
|
||||
return new Promise<void>(() => {});
|
||||
},
|
||||
http: deps!.http,
|
||||
docLinks: deps.docLinks,
|
||||
actionTypeRegistry: deps!.actionTypeRegistry,
|
||||
alertTypeRegistry: deps!.alertTypeRegistry,
|
||||
toastNotifications: deps!.toastNotifications,
|
||||
uiSettings: deps!.uiSettings,
|
||||
capabilities: deps!.capabilities,
|
||||
}}
|
||||
>
|
||||
<AlertForm
|
||||
alert={initialAlert}
|
||||
dispatch={() => {}}
|
||||
errors={{ name: [], interval: [] }}
|
||||
operation="create"
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
<AlertForm
|
||||
alert={initialAlert}
|
||||
dispatch={() => {}}
|
||||
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(
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
reloadAlerts: () => {
|
||||
return new Promise<void>(() => {});
|
||||
},
|
||||
http: deps!.http,
|
||||
docLinks: deps.docLinks,
|
||||
actionTypeRegistry: deps!.actionTypeRegistry,
|
||||
alertTypeRegistry: deps!.alertTypeRegistry,
|
||||
toastNotifications: deps!.toastNotifications,
|
||||
uiSettings: deps!.uiSettings,
|
||||
capabilities: deps!.capabilities,
|
||||
}}
|
||||
>
|
||||
<AlertForm
|
||||
alert={initialAlert}
|
||||
dispatch={() => {}}
|
||||
errors={{ name: [], interval: [] }}
|
||||
operation="create"
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
<AlertForm
|
||||
alert={initialAlert}
|
||||
dispatch={() => {}}
|
||||
errors={{ name: [], interval: [] }}
|
||||
operation="create"
|
||||
actionTypeRegistry={actionTypeRegistry}
|
||||
alertTypeRegistry={alertTypeRegistry}
|
||||
/>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
|
@ -354,14 +326,6 @@ describe('alert_form', () => {
|
|||
let wrapper: ReactWrapper<any>;
|
||||
|
||||
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(
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
reloadAlerts: () => {
|
||||
return new Promise<void>(() => {});
|
||||
},
|
||||
http: deps!.http,
|
||||
docLinks: deps.docLinks,
|
||||
actionTypeRegistry: deps!.actionTypeRegistry,
|
||||
alertTypeRegistry: deps!.alertTypeRegistry,
|
||||
toastNotifications: deps!.toastNotifications,
|
||||
uiSettings: deps!.uiSettings,
|
||||
capabilities: deps!.capabilities,
|
||||
}}
|
||||
>
|
||||
<AlertForm
|
||||
alert={initialAlert}
|
||||
dispatch={() => {}}
|
||||
errors={{ name: [], interval: [] }}
|
||||
operation="create"
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
<AlertForm
|
||||
alert={initialAlert}
|
||||
dispatch={() => {}}
|
||||
errors={{ name: [], interval: [] }}
|
||||
operation="create"
|
||||
actionTypeRegistry={actionTypeRegistry}
|
||||
alertTypeRegistry={alertTypeRegistry}
|
||||
/>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
|
|
|
@ -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<MetaData = Record<string, any>> {
|
||||
alert: InitialAlert;
|
||||
dispatch: React.Dispatch<AlertReducerAction>;
|
||||
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<AlertTypeModel | null>(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}
|
||||
/>
|
||||
</Suspense>
|
||||
</EuiErrorBoundary>
|
||||
|
|
|
@ -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 (
|
||||
<section data-test-subj="alertsList">
|
||||
<DeleteModalConfirmation
|
||||
onDeleted={async (deleted: string[]) => {
|
||||
onDeleted={async () => {
|
||||
setAlertsToDelete([]);
|
||||
setSelectedIds([]);
|
||||
await loadAlertsData();
|
||||
|
@ -658,29 +653,14 @@ export const AlertsList: React.FunctionComponent = () => {
|
|||
) : (
|
||||
noPermissionPrompt
|
||||
)}
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
reloadAlerts: loadAlertsData,
|
||||
http,
|
||||
actionTypeRegistry,
|
||||
alertTypeRegistry,
|
||||
toastNotifications: toasts,
|
||||
uiSettings,
|
||||
docLinks,
|
||||
charts,
|
||||
dataFieldsFormats: data.fieldFormats,
|
||||
capabilities,
|
||||
dataUi: data.ui,
|
||||
dataIndexPatterns: data.indexPatterns,
|
||||
kibanaFeatures,
|
||||
}}
|
||||
>
|
||||
<AlertAdd
|
||||
consumer={ALERTS_FEATURE_ID}
|
||||
addFlyoutVisible={alertFlyoutVisible}
|
||||
setAddFlyoutVisibility={setAlertFlyoutVisibility}
|
||||
/>
|
||||
</AlertsContextProvider>
|
||||
<AlertAdd
|
||||
consumer={ALERTS_FEATURE_ID}
|
||||
addFlyoutVisible={alertFlyoutVisible}
|
||||
setAddFlyoutVisibility={setAlertFlyoutVisibility}
|
||||
actionTypeRegistry={actionTypeRegistry}
|
||||
alertTypeRegistry={alertTypeRegistry}
|
||||
reloadAlerts={loadAlertsData}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -21,9 +21,6 @@ export function withActionOperations<T>(
|
|||
): React.FunctionComponent<PropsWithOptionalApiHandlers<T>> {
|
||||
return (props: PropsWithOptionalApiHandlers<T>) => {
|
||||
const { http } = useKibana().services;
|
||||
if (!http) {
|
||||
throw new Error('KibanaContext has not been initalized correctly.');
|
||||
}
|
||||
return (
|
||||
<WrappedComponent {...(props as T)} loadActionTypes={async () => loadActionTypes({ http })} />
|
||||
);
|
||||
|
|
|
@ -70,9 +70,6 @@ export function withBulkAlertOperations<T>(
|
|||
): React.FunctionComponent<PropsWithOptionalApiHandlers<T>> {
|
||||
return (props: PropsWithOptionalApiHandlers<T>) => {
|
||||
const { http } = useKibana().services;
|
||||
if (!http) {
|
||||
throw new Error('KibanaContext has not been initalized correctly.');
|
||||
}
|
||||
return (
|
||||
<WrappedComponent
|
||||
{...(props as T)}
|
||||
|
|
|
@ -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 { AlertAddProps } from '../application/sections/alert_form/alert_add';
|
||||
|
||||
export const getAddAlertFlyoutLazy = (props: AlertAddProps) => {
|
||||
const AlertAddFlyoutLazy = lazy(() => import('../application/sections/alert_form/alert_add'));
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<AlertAddFlyoutLazy {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
};
|
|
@ -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 (
|
||||
<Suspense fallback={null}>
|
||||
<AlertEditFlyoutLazy {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
};
|
|
@ -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: {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<ActionTypeModel>;
|
||||
|
@ -39,10 +43,16 @@ export interface TriggersAndActionsUIPublicPluginStart {
|
|||
alertTypeRegistry: TypeRegistry<AlertTypeModel>;
|
||||
getAddConnectorFlyout: (
|
||||
props: Omit<ConnectorAddFlyoutProps, 'actionTypeRegistry'>
|
||||
) => ReactElement<ConnectorAddFlyoutProps> | null;
|
||||
) => ReactElement<ConnectorAddFlyoutProps>;
|
||||
getEditConnectorFlyout: (
|
||||
props: Omit<ConnectorEditFlyoutProps, 'actionTypeRegistry'>
|
||||
) => ReactElement<ConnectorEditFlyoutProps> | null;
|
||||
) => ReactElement<ConnectorEditFlyoutProps>;
|
||||
getAddAlertFlyout: (
|
||||
props: Omit<AlertAddProps, 'actionTypeRegistry' | 'alertTypeRegistry'>
|
||||
) => ReactElement<AlertAddProps>;
|
||||
getEditAlertFlyout: (
|
||||
props: Omit<AlertEditProps, 'actionTypeRegistry' | 'alertTypeRegistry'>
|
||||
) => ReactElement<AlertEditProps>;
|
||||
}
|
||||
|
||||
interface PluginsSetup {
|
||||
|
@ -152,6 +162,24 @@ export class Plugin
|
|||
actionTypeRegistry: this.actionTypeRegistry,
|
||||
});
|
||||
},
|
||||
getAddAlertFlyout: (
|
||||
props: Omit<AlertAddProps, 'actionTypeRegistry' | 'alertTypeRegistry'>
|
||||
) => {
|
||||
return getAddAlertFlyoutLazy({
|
||||
...props,
|
||||
actionTypeRegistry: this.actionTypeRegistry,
|
||||
alertTypeRegistry: this.alertTypeRegistry,
|
||||
});
|
||||
},
|
||||
getEditAlertFlyout: (
|
||||
props: Omit<AlertEditProps, 'actionTypeRegistry' | 'alertTypeRegistry'>
|
||||
) => {
|
||||
return getEditAlertFlyoutLazy({
|
||||
...props,
|
||||
actionTypeRegistry: this.actionTypeRegistry,
|
||||
alertTypeRegistry: this.alertTypeRegistry,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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<string, any>
|
||||
> {
|
||||
alertParams: AlertParamsType;
|
||||
alertInterval: string;
|
||||
|
@ -166,12 +168,14 @@ export interface AlertTypeParamsExpressionProps<
|
|||
setAlertParams: (property: string, value: any) => void;
|
||||
setAlertProperty: <Key extends keyof Alert>(key: Key, value: Alert[Key] | null) => void;
|
||||
errors: IErrorObject;
|
||||
alertsContext: AlertsContextValue;
|
||||
defaultActionGroupId: string;
|
||||
actionGroups: ActionGroup[];
|
||||
metadata?: MetaData;
|
||||
charts: ChartsPluginSetup;
|
||||
data: DataPublicPluginStart;
|
||||
}
|
||||
|
||||
export interface AlertTypeModel<AlertParamsType = any, AlertsContextValue = any> {
|
||||
export interface AlertTypeModel<AlertParamsType = any> {
|
||||
id: string;
|
||||
name: string | JSX.Element;
|
||||
description: string;
|
||||
|
@ -180,9 +184,7 @@ export interface AlertTypeModel<AlertParamsType = any, AlertsContextValue = any>
|
|||
validate: (alertParams: AlertParamsType) => ValidationResult;
|
||||
alertParamsExpression:
|
||||
| React.FunctionComponent<any>
|
||||
| React.LazyExoticComponent<
|
||||
ComponentType<AlertTypeParamsExpressionProps<AlertParamsType, AlertsContextValue>>
|
||||
>;
|
||||
| React.LazyExoticComponent<ComponentType<AlertTypeParamsExpressionProps<AlertParamsType>>>;
|
||||
requiresAppContext: boolean;
|
||||
defaultActionMessage?: string;
|
||||
}
|
||||
|
|
|
@ -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) => {
|
|||
<UptimeSettingsContextProvider {...props}>
|
||||
<UptimeThemeContextProvider darkMode={darkMode}>
|
||||
<UptimeStartupPluginsContextProvider {...startPlugins}>
|
||||
<UptimeAlertsContextProvider>
|
||||
<EuiPage className="app-wrapper-panel " data-test-subj="uptimeApp">
|
||||
<RedirectAppLinks application={core.application}>
|
||||
<main>
|
||||
<UptimeAlertsFlyoutWrapper />
|
||||
<PageRouter />
|
||||
</main>
|
||||
</RedirectAppLinks>
|
||||
</EuiPage>
|
||||
</UptimeAlertsContextProvider>
|
||||
<EuiPage className="app-wrapper-panel " data-test-subj="uptimeApp">
|
||||
<RedirectAppLinks application={core.application}>
|
||||
<main>
|
||||
<UptimeAlertsFlyoutWrapper />
|
||||
<PageRouter />
|
||||
</main>
|
||||
</RedirectAppLinks>
|
||||
</EuiPage>
|
||||
</UptimeStartupPluginsContextProvider>
|
||||
</UptimeThemeContextProvider>
|
||||
</UptimeSettingsContextProvider>
|
||||
|
|
|
@ -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<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
interface KibanaDeps {
|
||||
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
|
||||
}
|
||||
|
||||
export const UptimeEditAlertFlyoutComponent = ({
|
||||
alertFlyoutVisible,
|
||||
initialAlert,
|
||||
|
@ -21,5 +29,16 @@ export const UptimeEditAlertFlyoutComponent = ({
|
|||
const onClose = () => {
|
||||
setAlertFlyoutVisibility(false);
|
||||
};
|
||||
return alertFlyoutVisible ? <AlertEdit initialAlert={initialAlert} onClose={onClose} /> : null;
|
||||
const { triggersActionsUi } = useKibana<KibanaDeps>().services;
|
||||
|
||||
const EditAlertFlyout = useMemo(
|
||||
() =>
|
||||
triggersActionsUi.getEditAlertFlyout({
|
||||
initialAlert,
|
||||
onClose,
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
return <>{alertFlyoutVisible && EditAlertFlyout}</>;
|
||||
};
|
||||
|
|
|
@ -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(<MLIntegrationComponent />);
|
||||
const wrapper = renderWithRouter(
|
||||
<KibanaContextProvider
|
||||
services={{ ...core, triggersActionsUi: { getEditAlertFlyout: jest.fn() } }}
|
||||
>
|
||||
<MLIntegrationComponent />
|
||||
</KibanaContextProvider>
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(
|
||||
<MLJobLink dateRange={{ to: '', from: '' }} basePath="" monitorId="testMonitor" />
|
||||
<KibanaContextProvider
|
||||
services={{ ...core, triggersActionsUi: { getEditAlertFlyout: jest.fn() } }}
|
||||
>
|
||||
<MLJobLink dateRange={{ to: '', from: '' }} basePath="" monitorId="testMonitor" />
|
||||
</KibanaContextProvider>
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -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(
|
||||
<ManageMLJobComponent hasMLJob={true} onEnableJob={jest.fn()} onJobDelete={jest.fn()} />
|
||||
<KibanaContextProvider
|
||||
services={{ ...core, triggersActionsUi: { getEditAlertFlyout: jest.fn() } }}
|
||||
>
|
||||
<ManageMLJobComponent hasMLJob={true} onEnableJob={jest.fn()} onJobDelete={jest.fn()} />
|
||||
</KibanaContextProvider>
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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<KibanaDeps>();
|
||||
|
||||
return (
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
actionTypeRegistry,
|
||||
alertTypeRegistry,
|
||||
charts,
|
||||
docLinks,
|
||||
dataFieldsFormats: fieldFormats,
|
||||
http,
|
||||
toastNotifications: notifications?.toasts,
|
||||
uiSettings,
|
||||
capabilities,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AlertsContextProvider>
|
||||
);
|
||||
};
|
|
@ -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<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
interface KibanaDeps {
|
||||
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
|
||||
}
|
||||
|
||||
export const UptimeAlertsFlyoutWrapperComponent = ({
|
||||
alertFlyoutVisible,
|
||||
alertTypeId,
|
||||
setAlertFlyoutVisibility,
|
||||
}: Props) => (
|
||||
<AlertAdd
|
||||
addFlyoutVisible={alertFlyoutVisible}
|
||||
consumer="uptime"
|
||||
setAddFlyoutVisibility={setAlertFlyoutVisibility}
|
||||
alertTypeId={alertTypeId}
|
||||
// if we don't have an alert type pre-specified, we need to
|
||||
// let the user choose
|
||||
canChangeTrigger={!alertTypeId}
|
||||
/>
|
||||
);
|
||||
}: Props) => {
|
||||
const { triggersActionsUi } = useKibana<KibanaDeps>().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}</>;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue