Allow editing of APM rules (#106598)

By pulling out most of the things that depend on the URL into where we open the flyout and passing them in as metadata props, we can make it so editing rules while in Stack Management.

You cannot edit a rule's service name, transaction type, or environment once it has been created (#106786 has been created to allow editing of these other values), but all other values can be edited.

In order for useFetcher to work outside of the APM plugin, it has been changed to use useKibana instead of useApmContext for toast notifications. The notifications API from useKibana is slightly different and allows passing a react element instead of a mount point as the body.

Fixes #76316.
This commit is contained in:
Nathan L Smith 2021-07-27 10:05:39 -05:00 committed by GitHub
parent 76989b57eb
commit a6211f86f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 206 additions and 224 deletions

View file

@ -14,6 +14,10 @@ import {
import { getInitialAlertValues } from '../get_initial_alert_values';
import { ApmPluginStartDeps } from '../../../plugin';
import { useServiceName } from '../../../hooks/use_service_name';
import { useApmParams } from '../../../hooks/use_apm_params';
import { AlertMetadata } from '../helper';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values';
interface Props {
addFlyoutVisible: boolean;
@ -23,7 +27,17 @@ interface Props {
export function AlertingFlyout(props: Props) {
const { addFlyoutVisible, setAddFlyoutVisibility, alertType } = props;
const serviceName = useServiceName();
const { query } = useApmParams('/*');
const {
urlParams: { start, end },
} = useUrlParams();
const environment =
'environment' in query ? query.environment : ENVIRONMENT_ALL.value;
const transactionType =
'transactionType' in query ? query.transactionType : undefined;
const { services } = useKibana<ApmPluginStartDeps>();
const initialValues = getInitialAlertValues(alertType, serviceName);
@ -40,9 +54,26 @@ export function AlertingFlyout(props: Props) {
alertTypeId: alertType,
canChangeTrigger: false,
initialValues,
metadata: {
environment,
serviceName,
transactionType,
start,
end,
} as AlertMetadata,
}),
/* eslint-disable-next-line react-hooks/exhaustive-deps */
[alertType, onCloseAddFlyout, services.triggersActionsUi]
[
alertType,
environment,
onCloseAddFlyout,
services.triggersActionsUi,
serviceName,
transactionType,
environment,
start,
end,
]
);
return <>{addFlyoutVisible && addAlertFlyout}</>;
}

View file

@ -5,44 +5,47 @@
* 2.0.
*/
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { ErrorCountAlertTrigger } from '.';
import { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context';
import {
mockApmPluginContextValue,
MockApmPluginContextWrapper,
} from '../../../context/apm_plugin/mock_apm_plugin_context';
import React, { useState } from 'react';
import { AlertParams, ErrorCountAlertTrigger } from '.';
import { CoreStart } from '../../../../../../../src/core/public';
import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public';
const KibanaReactContext = createKibanaReactContext(({
notifications: { toasts: { add: () => {} } },
} as unknown) as Partial<CoreStart>);
export default {
title: 'app/ErrorCountAlertTrigger',
title: 'alerting/ErrorCountAlertTrigger',
component: ErrorCountAlertTrigger,
decorators: [
(Story: React.ComponentClass) => (
<MockApmPluginContextWrapper
value={(mockApmPluginContextValue as unknown) as ApmPluginContextValue}
>
<MemoryRouter>
<KibanaReactContext.Provider>
<div style={{ width: 400 }}>
<Story />
</div>
</MemoryRouter>
</MockApmPluginContextWrapper>
</KibanaReactContext.Provider>
),
],
};
export function Example() {
const params = {
const [params, setParams] = useState<AlertParams>({
serviceName: 'testServiceName',
environment: 'testEnvironment',
threshold: 2,
window: '5m',
};
windowSize: 5,
windowUnit: 'm',
});
function setAlertParams(property: string, value: any) {
setParams({ ...params, [property]: value });
}
return (
<ErrorCountAlertTrigger
alertParams={params as any}
setAlertParams={() => undefined}
setAlertProperty={() => undefined}
alertParams={params}
setAlertParams={setAlertParams}
setAlertProperty={() => {}}
/>
);
}

View file

@ -6,19 +6,16 @@
*/
import { i18n } from '@kbn/i18n';
import { defaults, omit } from 'lodash';
import React from 'react';
import { defaults } from 'lodash';
import { ForLastExpression } from '../../../../../triggers_actions_ui/public';
import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values';
import { asInteger } from '../../../../common/utils/formatters';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher';
import { useFetcher } from '../../../hooks/use_fetcher';
import { ChartPreview } from '../chart_preview';
import { EnvironmentField, IsAboveField, ServiceField } from '../fields';
import { getAbsoluteTimeRange } from '../helper';
import { AlertMetadata, getAbsoluteTimeRange } from '../helper';
import { ServiceAlertTrigger } from '../service_alert_trigger';
import { useServiceName } from '../../../hooks/use_service_name';
export interface AlertParams {
windowSize: number;
@ -30,33 +27,26 @@ export interface AlertParams {
interface Props {
alertParams: AlertParams;
metadata?: AlertMetadata;
setAlertParams: (key: string, value: any) => void;
setAlertProperty: (key: string, value: any) => void;
}
export function ErrorCountAlertTrigger(props: Props) {
const { setAlertParams, setAlertProperty, alertParams } = props;
const { alertParams, metadata, setAlertParams, setAlertProperty } = props;
const serviceNameFromUrl = useServiceName();
const { urlParams } = useUrlParams();
const { start, end, environment: environmentFromUrl } = urlParams;
const { environmentOptions } = useEnvironmentsFetcher({
serviceName: serviceNameFromUrl,
start,
end,
serviceName: metadata?.serviceName,
start: metadata?.start,
end: metadata?.end,
});
const params = defaults(
{
...alertParams,
},
{ ...omit(metadata, ['start', 'end']), ...alertParams },
{
threshold: 25,
windowSize: 1,
windowUnit: 'm',
environment: environmentFromUrl || ENVIRONMENT_ALL.value,
serviceName: serviceNameFromUrl,
}
);

View file

@ -36,13 +36,17 @@ export function EnvironmentField({
options: EuiSelectOption[];
onChange: (event: React.ChangeEvent<HTMLSelectElement>) => void;
}) {
return (
<PopoverExpression
value={getEnvironmentLabel(currentValue)}
title={i18n.translate('xpack.apm.alerting.fields.environment', {
const title = i18n.translate('xpack.apm.alerting.fields.environment', {
defaultMessage: 'Environment',
})}
>
});
// "1" means "All" is the only option and we should not show a select.
if (options.length === 1) {
return <EuiExpression description={title} value={currentValue} />;
}
return (
<PopoverExpression value={getEnvironmentLabel(currentValue)} title={title}>
<EuiSelect
defaultValue={currentValue}
options={options}

View file

@ -7,6 +7,14 @@
import datemath from '@elastic/datemath';
export interface AlertMetadata {
environment: string;
serviceName?: string;
transactionType?: string;
start?: string;
end?: string;
}
export function getAbsoluteTimeRange(windowSize: number, windowUnit: string) {
const now = new Date().toISOString();

View file

@ -71,7 +71,7 @@ export function registerApmAlerts(
validate: () => ({
errors: [],
}),
requiresAppContext: true,
requiresAppContext: false,
defaultActionMessage: i18n.translate(
'xpack.apm.alertTypes.errorCount.defaultActionMessage',
{
@ -126,7 +126,7 @@ export function registerApmAlerts(
validate: () => ({
errors: [],
}),
requiresAppContext: true,
requiresAppContext: false,
defaultActionMessage: i18n.translate(
'xpack.apm.alertTypes.transactionDuration.defaultActionMessage',
{
@ -182,7 +182,7 @@ export function registerApmAlerts(
validate: () => ({
errors: [],
}),
requiresAppContext: true,
requiresAppContext: false,
defaultActionMessage: i18n.translate(
'xpack.apm.alertTypes.transactionErrorRate.defaultActionMessage',
{
@ -237,7 +237,7 @@ export function registerApmAlerts(
validate: () => ({
errors: [],
}),
requiresAppContext: true,
requiresAppContext: false,
defaultActionMessage: i18n.translate(
'xpack.apm.alertTypes.transactionDurationAnomaly.defaultActionMessage',
{

View file

@ -6,69 +6,51 @@
*/
import { Story } from '@storybook/react';
import { cloneDeep, merge } from 'lodash';
import React, { ComponentType } from 'react';
import { MemoryRouter, Route } from 'react-router-dom';
import { TransactionDurationAlertTrigger } from '.';
import { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context';
import {
mockApmPluginContextValue,
MockApmPluginContextWrapper,
} from '../../../context/apm_plugin/mock_apm_plugin_context';
import { ApmServiceContextProvider } from '../../../context/apm_service/apm_service_context';
import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider';
import React, { ComponentType, useState } from 'react';
import { AlertParams, TransactionDurationAlertTrigger } from '.';
import { CoreStart } from '../../../../../../../src/core/public';
import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public';
const KibanaReactContext = createKibanaReactContext(({
notifications: { toasts: { add: () => {} } },
} as unknown) as Partial<CoreStart>);
export default {
title: 'alerting/TransactionDurationAlertTrigger',
component: TransactionDurationAlertTrigger,
decorators: [
(StoryComponent: ComponentType) => {
const contextMock = (merge(cloneDeep(mockApmPluginContextValue), {
core: {
http: {
get: (endpoint: string) => {
if (endpoint === '/api/apm/environments') {
return Promise.resolve({ environments: ['production'] });
} else {
return Promise.resolve({
transactionTypes: ['request'],
});
}
},
},
},
}) as unknown) as ApmPluginContextValue;
return (
<KibanaReactContext.Provider>
<div style={{ width: 400 }}>
<MemoryRouter initialEntries={['/services/test-service-name']}>
<Route path="/services/:serviceName">
<MockApmPluginContextWrapper value={contextMock}>
<MockUrlParamsContextProvider>
<ApmServiceContextProvider>
<StoryComponent />
</ApmServiceContextProvider>
</MockUrlParamsContextProvider>
</MockApmPluginContextWrapper>
</Route>
</MemoryRouter>
</div>
</KibanaReactContext.Provider>
);
},
],
};
export const Example: Story = () => {
const params = {
threshold: 1500,
const [params, setParams] = useState<AlertParams>({
aggregationType: 'avg' as const,
window: '5m',
};
environment: 'testEnvironment',
serviceName: 'testServiceName',
threshold: 1500,
transactionType: 'testTransactionType',
windowSize: 5,
windowUnit: 'm',
});
function setAlertParams(property: string, value: any) {
setParams({ ...params, [property]: value });
}
return (
<TransactionDurationAlertTrigger
alertParams={params as any}
setAlertParams={() => undefined}
setAlertProperty={() => undefined}
alertParams={params}
setAlertParams={setAlertParams}
setAlertProperty={() => {}}
/>
);
};

View file

@ -7,18 +7,16 @@
import { EuiSelect } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { map, defaults } from 'lodash';
import { defaults, map, omit } from 'lodash';
import React from 'react';
import { CoreStart } from '../../../../../../../src/core/public';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { ForLastExpression } from '../../../../../triggers_actions_ui/public';
import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values';
import { getDurationFormatter } from '../../../../common/utils/formatters';
import { getTransactionType } from '../../../context/apm_service/apm_service_context';
import { useServiceAgentNameFetcher } from '../../../context/apm_service/use_service_agent_name_fetcher';
import { useServiceTransactionTypesFetcher } from '../../../context/apm_service/use_service_transaction_types_fetcher';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher';
import { useFetcher } from '../../../hooks/use_fetcher';
import { useServiceName } from '../../../hooks/use_service_name';
import { createCallApmApi } from '../../../services/rest/createCallApmApi';
import {
getMaxY,
getResponseTimeTickFormatter,
@ -30,18 +28,18 @@ import {
ServiceField,
TransactionTypeField,
} from '../fields';
import { getAbsoluteTimeRange } from '../helper';
import { AlertMetadata, getAbsoluteTimeRange } from '../helper';
import { ServiceAlertTrigger } from '../service_alert_trigger';
import { PopoverExpression } from '../service_alert_trigger/popover_expression';
interface AlertParams {
export interface AlertParams {
aggregationType: 'avg' | '95th' | '99th';
environment: string;
serviceName: string;
threshold: number;
transactionType: string;
windowSize: number;
windowUnit: string;
threshold: number;
aggregationType: 'avg' | '95th' | '99th';
serviceName: string;
transactionType: string;
environment: string;
}
const TRANSACTION_ALERT_AGGREGATION_TYPES = {
@ -67,48 +65,38 @@ const TRANSACTION_ALERT_AGGREGATION_TYPES = {
interface Props {
alertParams: AlertParams;
metadata?: AlertMetadata;
setAlertParams: (key: string, value: any) => void;
setAlertProperty: (key: string, value: any) => void;
}
export function TransactionDurationAlertTrigger(props: Props) {
const { setAlertParams, alertParams, setAlertProperty } = props;
const { urlParams } = useUrlParams();
const { services } = useKibana();
const { alertParams, metadata, setAlertParams, setAlertProperty } = props;
const { start, end, environment: environmentFromUrl } = urlParams;
const serviceNameFromUrl = useServiceName();
createCallApmApi(services as CoreStart);
const transactionTypes = useServiceTransactionTypesFetcher(
serviceNameFromUrl
metadata?.serviceName
);
const { agentName } = useServiceAgentNameFetcher(serviceNameFromUrl);
const transactionTypeFromUrl = getTransactionType({
transactionType: urlParams.transactionType,
transactionTypes,
agentName,
});
const params = defaults(
{
...omit(metadata, ['start', 'end']),
...alertParams,
},
{
aggregationType: 'avg',
environment: environmentFromUrl || ENVIRONMENT_ALL.value,
threshold: 1500,
windowSize: 5,
windowUnit: 'm',
transactionType: transactionTypeFromUrl,
serviceName: serviceNameFromUrl,
}
);
const { environmentOptions } = useEnvironmentsFetcher({
serviceName: params.serviceName,
start,
end,
start: metadata?.start,
end: metadata?.end,
});
const { data } = useFetcher(
@ -155,7 +143,7 @@ export function TransactionDurationAlertTrigger(props: Props) {
/>
);
if (!transactionTypes.length || !params.serviceName) {
if (!params.serviceName) {
return null;
}

View file

@ -6,84 +6,67 @@
*/
import { i18n } from '@kbn/i18n';
import { defaults, omit } from 'lodash';
import React from 'react';
import { defaults } from 'lodash';
import { ANOMALY_SEVERITY } from '../../../../common/ml_constants';
import { useServiceTransactionTypesFetcher } from '../../../context/apm_service/use_service_transaction_types_fetcher';
import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import {
EnvironmentField,
ServiceField,
TransactionTypeField,
} from '../fields';
import { AlertMetadata } from '../helper';
import { ServiceAlertTrigger } from '../service_alert_trigger';
import { PopoverExpression } from '../service_alert_trigger/popover_expression';
import {
AnomalySeverity,
SelectAnomalySeverity,
} from './select_anomaly_severity';
import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values';
import {
EnvironmentField,
ServiceField,
TransactionTypeField,
} from '../fields';
import { useServiceName } from '../../../hooks/use_service_name';
import { useServiceTransactionTypesFetcher } from '../../../context/apm_service/use_service_transaction_types_fetcher';
import { useServiceAgentNameFetcher } from '../../../context/apm_service/use_service_agent_name_fetcher';
import { getTransactionType } from '../../../context/apm_service/apm_service_context';
interface AlertParams {
windowSize: number;
windowUnit: string;
serviceName?: string;
transactionType?: string;
environment: string;
anomalySeverityType:
| ANOMALY_SEVERITY.CRITICAL
| ANOMALY_SEVERITY.MAJOR
| ANOMALY_SEVERITY.MINOR
| ANOMALY_SEVERITY.WARNING;
environment: string;
serviceName?: string;
transactionType?: string;
windowSize: number;
windowUnit: string;
}
interface Props {
alertParams: AlertParams;
metadata?: AlertMetadata;
setAlertParams: (key: string, value: any) => void;
setAlertProperty: (key: string, value: any) => void;
}
export function TransactionDurationAnomalyAlertTrigger(props: Props) {
const { setAlertParams, alertParams, setAlertProperty } = props;
const { urlParams } = useUrlParams();
const serviceNameFromUrl = useServiceName();
const { alertParams, metadata, setAlertParams, setAlertProperty } = props;
const transactionTypes = useServiceTransactionTypesFetcher(
serviceNameFromUrl
metadata?.serviceName
);
const { agentName } = useServiceAgentNameFetcher(serviceNameFromUrl);
const transactionTypeFromUrl = getTransactionType({
transactionType: urlParams.transactionType,
transactionTypes,
agentName,
});
const { start, end, environment: environmentFromUrl } = urlParams;
const params = defaults(
{
...omit(metadata, ['start', 'end']),
...alertParams,
},
{
windowSize: 15,
windowUnit: 'm',
transactionType: transactionTypeFromUrl,
environment: environmentFromUrl || ENVIRONMENT_ALL.value,
anomalySeverityType: ANOMALY_SEVERITY.CRITICAL,
serviceName: serviceNameFromUrl,
}
);
const { environmentOptions } = useEnvironmentsFetcher({
serviceName: params.serviceName,
start,
end,
start: metadata?.start,
end: metadata?.end,
});
const fields = [

View file

@ -5,14 +5,16 @@
* 2.0.
*/
import { defaults, omit } from 'lodash';
import React from 'react';
import { defaults } from 'lodash';
import { CoreStart } from '../../../../../../../src/core/public';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { ForLastExpression } from '../../../../../triggers_actions_ui/public';
import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values';
import { asPercent } from '../../../../common/utils/formatters';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { useServiceTransactionTypesFetcher } from '../../../context/apm_service/use_service_transaction_types_fetcher';
import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher';
import { useFetcher } from '../../../hooks/use_fetcher';
import { createCallApmApi } from '../../../services/rest/createCallApmApi';
import { ChartPreview } from '../chart_preview';
import {
EnvironmentField,
@ -20,12 +22,8 @@ import {
ServiceField,
TransactionTypeField,
} from '../fields';
import { getAbsoluteTimeRange } from '../helper';
import { AlertMetadata, getAbsoluteTimeRange } from '../helper';
import { ServiceAlertTrigger } from '../service_alert_trigger';
import { useServiceName } from '../../../hooks/use_service_name';
import { useServiceTransactionTypesFetcher } from '../../../context/apm_service/use_service_transaction_types_fetcher';
import { useServiceAgentNameFetcher } from '../../../context/apm_service/use_service_agent_name_fetcher';
import { getTransactionType } from '../../../context/apm_service/apm_service_context';
interface AlertParams {
windowSize: number;
@ -38,45 +36,34 @@ interface AlertParams {
interface Props {
alertParams: AlertParams;
metadata?: AlertMetadata;
setAlertParams: (key: string, value: any) => void;
setAlertProperty: (key: string, value: any) => void;
}
export function TransactionErrorRateAlertTrigger(props: Props) {
const { setAlertParams, alertParams, setAlertProperty } = props;
const { urlParams } = useUrlParams();
const { services } = useKibana();
const { alertParams, metadata, setAlertParams, setAlertProperty } = props;
const serviceNameFromUrl = useServiceName();
createCallApmApi(services as CoreStart);
const transactionTypes = useServiceTransactionTypesFetcher(
serviceNameFromUrl
metadata?.serviceName
);
const { agentName } = useServiceAgentNameFetcher(serviceNameFromUrl);
const transactionTypeFromUrl = getTransactionType({
transactionType: urlParams.transactionType,
transactionTypes,
agentName,
});
const { start, end, environment: environmentFromUrl } = urlParams;
const params = defaults(
{ ...alertParams },
{ ...omit(metadata, ['start', 'end']), ...alertParams },
{
threshold: 30,
windowSize: 5,
windowUnit: 'm',
transactionType: transactionTypeFromUrl,
environment: environmentFromUrl || ENVIRONMENT_ALL.value,
serviceName: serviceNameFromUrl,
}
);
const { environmentOptions } = useEnvironmentsFetcher({
serviceName: params.serviceName,
start,
end,
start: metadata?.start,
end: metadata?.end,
});
const thresholdAsPercent = (params.threshold ?? 0) / 100;
@ -106,10 +93,6 @@ export function TransactionErrorRateAlertTrigger(props: Props) {
]
);
if (params.serviceName && !transactionTypes.length) {
return null;
}
const fields = [
<ServiceField value={params.serviceName} />,
<TransactionTypeField

View file

@ -32,8 +32,11 @@ import { fromQuery } from '../../shared/Links/url_helpers';
import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider';
import { uiSettingsServiceMock } from '../../../../../../../src/core/public/mocks';
const uiSettings = uiSettingsServiceMock.create().setup({} as any);
const KibanaReactContext = createKibanaReactContext(({
uiSettings: { get: () => true },
notifications: { toasts: { add: () => {} } },
uiSettings,
usageCollection: { reportUiCounter: () => {} },
} as unknown) as Partial<CoreStart>);
@ -48,8 +51,6 @@ const location = {
search: fromQuery(mockParams),
};
const uiSettings = uiSettingsServiceMock.create().setup({} as any);
function Wrapper({ children }: { children?: ReactNode }) {
const value = ({
...mockApmPluginContextValue,
@ -64,11 +65,7 @@ function Wrapper({ children }: { children?: ReactNode }) {
return (
<MemoryRouter initialEntries={[location]}>
<KibanaReactContext.Provider
services={{
uiSettings,
}}
>
<KibanaReactContext.Provider>
<MockApmPluginContextWrapper value={value}>
<MockUrlParamsContextProvider params={mockParams}>
{children}

View file

@ -6,22 +6,35 @@
*/
import { renderHook, RenderHookResult } from '@testing-library/react-hooks';
import React, { ReactNode } from 'react';
import { CoreStart } from '../../../../../src/core/public';
import { createKibanaReactContext } from '../../../../../src/plugins/kibana_react/public';
import { delay } from '../utils/testHelpers';
import { FetcherResult, useFetcher } from './use_fetcher';
import { MockApmPluginContextWrapper } from '../context/apm_plugin/mock_apm_plugin_context';
import { ApmPluginContextValue } from '../context/apm_plugin/apm_plugin_context';
// Wrap the hook with a provider so it can useApmPluginContext
const wrapper = MockApmPluginContextWrapper;
// Wrap the hook with a provider so it can useKibana
const KibanaReactContext = createKibanaReactContext(({
notifications: { toasts: { add: () => {}, danger: () => {} } },
} as unknown) as Partial<CoreStart>);
interface WrapperProps {
children?: ReactNode;
callback: () => Promise<string>;
args: string[];
}
function wrapper({ children }: WrapperProps) {
return <KibanaReactContext.Provider>{children}</KibanaReactContext.Provider>;
}
describe('useFetcher', () => {
describe('when resolving after 500ms', () => {
let hook: RenderHookResult<
{ children?: React.ReactNode; value?: ApmPluginContextValue },
WrapperProps,
FetcherResult<string> & {
refetch: () => void;
}
>;
beforeEach(() => {
jest.useFakeTimers();
async function fn() {
@ -66,14 +79,15 @@ describe('useFetcher', () => {
describe('when throwing after 500ms', () => {
let hook: RenderHookResult<
{ children?: React.ReactNode; value?: ApmPluginContextValue },
FetcherResult<void> & {
WrapperProps,
FetcherResult<string> & {
refetch: () => void;
}
>;
beforeEach(() => {
jest.useFakeTimers();
async function fn() {
async function fn(): Promise<string> {
await delay(500);
throw new Error('Something went wrong');
}

View file

@ -8,13 +8,12 @@
import { i18n } from '@kbn/i18n';
import React, { useEffect, useMemo, useState } from 'react';
import { IHttpFetchError } from 'src/core/public';
import { toMountPoint } from '../../../../../src/plugins/kibana_react/public';
import {
callApmApi,
AutoAbortedAPMClient,
} from '../services/rest/createCallApmApi';
import { useApmPluginContext } from '../context/apm_plugin/use_apm_plugin_context';
import { useKibana } from '../../../../../src/plugins/kibana_react/public';
import { useUrlParams } from '../context/url_params_context/use_url_params';
import {
AutoAbortedAPMClient,
callApmApi,
} from '../services/rest/createCallApmApi';
export enum FETCH_STATUS {
LOADING = 'loading',
@ -68,7 +67,7 @@ export function useFetcher<TReturn>(
showToastOnError?: boolean;
} = {}
): FetcherResult<InferResponseType<TReturn>> & { refetch: () => void } {
const { notifications } = useApmPluginContext().core;
const { notifications } = useKibana();
const { preservePreviousData = true, showToastOnError = true } = options;
const [result, setResult] = useState<
FetcherResult<InferResponseType<TReturn>>
@ -124,12 +123,12 @@ export function useFetcher<TReturn>(
'response' in err ? getDetailsFromErrorResponse(err) : err.message;
if (showToastOnError) {
notifications.toasts.addDanger({
notifications.toasts.danger({
title: i18n.translate('xpack.apm.fetcher.error.title', {
defaultMessage: `Error while fetching resource`,
}),
text: toMountPoint(
body: (
<div>
<h5>
{i18n.translate('xpack.apm.fetcher.error.status', {