Skip to content

Commit

Permalink
[uiActions] display toast when action.execute throws (elastic#198571)
Browse files Browse the repository at this point in the history
Follow up to elastic#198517.

This PR updates Ui actions to be more robust and safely handle runtime
errors.

---------

Co-authored-by: Elastic Machine <[email protected]>
Co-authored-by: kibanamachine <[email protected]>
(cherry picked from commit 853b59c)
  • Loading branch information
nreese committed Nov 18, 2024
1 parent 5904f8b commit 2cf045f
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 4 deletions.
38 changes: 38 additions & 0 deletions src/plugins/ui_actions/public/actions/action_internal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,42 @@ describe('ActionInternal', () => {
const action = new ActionInternal(defaultActionDef);
expect(action.id).toBe('test-action');
});

describe('displays toasts when execute function throws', () => {
const addWarningMock = jest.fn();
beforeAll(() => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('../services').getNotifications = () => ({
toasts: {
addWarning: addWarningMock,
},
});
});

beforeEach(() => {
addWarningMock.mockReset();
});

test('execute function is sync', async () => {
const action = new ActionInternal({
id: 'test-action',
execute: () => {
throw new Error('');
},
});
await action.execute({});
expect(addWarningMock).toBeCalledTimes(1);
});

test('execute function is async', async () => {
const action = new ActionInternal({
id: 'test-action',
execute: async () => {
throw new Error('');
},
});
await action.execute({});
expect(addWarningMock).toBeCalledTimes(1);
});
});
});
15 changes: 13 additions & 2 deletions src/plugins/ui_actions/public/actions/action_internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

import * as React from 'react';
import type { Presentable, PresentableGrouping } from '@kbn/ui-actions-browser/src/types';
import { i18n } from '@kbn/i18n';
import { Action, ActionDefinition, ActionMenuItemProps } from './action';
import { getNotifications } from '../services';

/**
* @internal
Expand Down Expand Up @@ -45,8 +47,17 @@ export class ActionInternal<Context extends object = object>
}
}

public execute(context: Context) {
return this.definition.execute(context);
public async execute(context: Context) {
try {
return await this.definition.execute(context);
} catch (e) {
getNotifications()?.toasts.addWarning(
i18n.translate('uiActions.execute.unhandledErrorMsg', {
defaultMessage: `Unable to execute action, error: {errorMessage}`,
values: { errorMessage: e.message },
})
);
}
}

public getIconType(context: Context): string | undefined {
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/ui_actions/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
addPanelMenuTrigger,
} from '@kbn/ui-actions-browser/src/triggers';
import { UiActionsService } from './service';
import { setAnalytics, setI18n, setTheme } from './services';
import { setAnalytics, setI18n, setNotifications, setTheme } from './services';

export type UiActionsPublicSetup = Pick<
UiActionsService,
Expand Down Expand Up @@ -60,6 +60,7 @@ export class UiActionsPlugin
public start(core: CoreStart): UiActionsPublicStart {
setAnalytics(core.analytics);
setI18n(core.i18n);
setNotifications(core.notifications);
setTheme(core.theme);
return this.service;
}
Expand Down
4 changes: 3 additions & 1 deletion src/plugins/ui_actions/public/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { AnalyticsServiceStart, I18nStart, ThemeServiceSetup } from '@kbn/core/public';
import { AnalyticsServiceStart, CoreStart, I18nStart, ThemeServiceSetup } from '@kbn/core/public';
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';

export const [getAnalytics, setAnalytics] = createGetterSetter<AnalyticsServiceStart>('Analytics');
export const [getI18n, setI18n] = createGetterSetter<I18nStart>('I18n');
export const [getNotifications, setNotifications] =
createGetterSetter<CoreStart['notifications']>('Notifications');
export const [getTheme, setTheme] = createGetterSetter<ThemeServiceSetup>('Theme');

0 comments on commit 2cf045f

Please sign in to comment.