mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Disabled actions (#51975)
* feat: disable actions from SIEM by using `disabledActions` list * feat: filter out actions specified in `disabledActions` input prop * test: 💍 remove legacy test * chore: 🤖 remove unused import * test: 💍 add disabledActions prop tests
This commit is contained in:
parent
085a2af8ec
commit
73651a1b28
6 changed files with 125 additions and 52 deletions
|
@ -28,6 +28,11 @@ export interface EmbeddableInput {
|
|||
id: string;
|
||||
lastReloadRequestTime?: number;
|
||||
hidePanelTitles?: boolean;
|
||||
|
||||
/**
|
||||
* List of action IDs that this embeddable should not render.
|
||||
*/
|
||||
disabledActions?: string[];
|
||||
}
|
||||
|
||||
export interface EmbeddableOutput {
|
||||
|
|
|
@ -25,7 +25,7 @@ import { nextTick } from 'test_utils/enzyme_helpers';
|
|||
import { findTestSubject } from '@elastic/eui/lib/test';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { CONTEXT_MENU_TRIGGER } from '../triggers';
|
||||
import { IAction, ITrigger } from 'src/plugins/ui_actions/public';
|
||||
import { IAction, ITrigger, IUiActionsApi } from 'src/plugins/ui_actions/public';
|
||||
import { Trigger, GetEmbeddableFactory, ViewMode } from '../types';
|
||||
import { EmbeddableFactory, isErrorEmbeddable } from '../embeddables';
|
||||
import { EmbeddablePanel } from './embeddable_panel';
|
||||
|
@ -42,6 +42,7 @@ import {
|
|||
} from '../test_samples/embeddables/contact_card/contact_card_embeddable';
|
||||
// eslint-disable-next-line
|
||||
import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks';
|
||||
import { EuiBadge } from '@elastic/eui';
|
||||
|
||||
const actionRegistry = new Map<string, IAction>();
|
||||
const triggerRegistry = new Map<string, ITrigger>();
|
||||
|
@ -174,6 +175,105 @@ test('HelloWorldContainer in view mode hides edit mode actions', async () => {
|
|||
expect(findTestSubject(component, `embeddablePanelAction-${editModeAction.id}`).length).toBe(0);
|
||||
});
|
||||
|
||||
const renderInEditModeAndOpenContextMenu = async (
|
||||
embeddableInputs: any,
|
||||
getActions: IUiActionsApi['getTriggerCompatibleActions'] = () => Promise.resolve([])
|
||||
) => {
|
||||
const inspector = inspectorPluginMock.createStartContract();
|
||||
|
||||
const container = new HelloWorldContainer({ id: '123', panels: {}, viewMode: ViewMode.VIEW }, {
|
||||
getEmbeddableFactory,
|
||||
} as any);
|
||||
|
||||
const embeddable = await container.addNewEmbeddable<
|
||||
ContactCardEmbeddableInput,
|
||||
ContactCardEmbeddableOutput,
|
||||
ContactCardEmbeddable
|
||||
>(CONTACT_CARD_EMBEDDABLE, embeddableInputs);
|
||||
|
||||
const component = mount(
|
||||
<I18nProvider>
|
||||
<EmbeddablePanel
|
||||
embeddable={embeddable}
|
||||
getActions={getActions}
|
||||
getAllEmbeddableFactories={(() => []) as any}
|
||||
getEmbeddableFactory={(() => undefined) as any}
|
||||
notifications={{} as any}
|
||||
overlays={{} as any}
|
||||
inspector={inspector}
|
||||
SavedObjectFinder={() => null}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
||||
findTestSubject(component, 'embeddablePanelToggleMenuIcon').simulate('click');
|
||||
await nextTick();
|
||||
component.update();
|
||||
|
||||
return { component };
|
||||
};
|
||||
|
||||
test('HelloWorldContainer in edit mode hides disabledActions', async () => {
|
||||
const action = {
|
||||
id: 'FOO',
|
||||
type: 'FOO',
|
||||
getIconType: () => undefined,
|
||||
getDisplayName: () => 'foo',
|
||||
isCompatible: async () => true,
|
||||
execute: async () => {},
|
||||
};
|
||||
const getActions = () => Promise.resolve([action]);
|
||||
|
||||
const { component: component1 } = await renderInEditModeAndOpenContextMenu(
|
||||
{
|
||||
firstName: 'Bob',
|
||||
},
|
||||
getActions
|
||||
);
|
||||
const { component: component2 } = await renderInEditModeAndOpenContextMenu(
|
||||
{
|
||||
firstName: 'Bob',
|
||||
disabledActions: ['FOO'],
|
||||
},
|
||||
getActions
|
||||
);
|
||||
|
||||
const fooContextMenuActionItem1 = findTestSubject(component1, 'embeddablePanelAction-FOO');
|
||||
const fooContextMenuActionItem2 = findTestSubject(component2, 'embeddablePanelAction-FOO');
|
||||
|
||||
expect(fooContextMenuActionItem1.length).toBe(1);
|
||||
expect(fooContextMenuActionItem2.length).toBe(0);
|
||||
});
|
||||
|
||||
test('HelloWorldContainer hides disabled badges', async () => {
|
||||
const action = {
|
||||
id: 'BAR',
|
||||
type: 'BAR',
|
||||
getIconType: () => undefined,
|
||||
getDisplayName: () => 'bar',
|
||||
isCompatible: async () => true,
|
||||
execute: async () => {},
|
||||
};
|
||||
const getActions = () => Promise.resolve([action]);
|
||||
|
||||
const { component: component1 } = await renderInEditModeAndOpenContextMenu(
|
||||
{
|
||||
firstName: 'Bob',
|
||||
},
|
||||
getActions
|
||||
);
|
||||
const { component: component2 } = await renderInEditModeAndOpenContextMenu(
|
||||
{
|
||||
firstName: 'Bob',
|
||||
disabledActions: ['BAR'],
|
||||
},
|
||||
getActions
|
||||
);
|
||||
|
||||
expect(component1.find(EuiBadge).length).toBe(1);
|
||||
expect(component2.find(EuiBadge).length).toBe(0);
|
||||
});
|
||||
|
||||
test('HelloWorldContainer in edit mode shows edit mode actions', async () => {
|
||||
const inspector = inspectorPluginMock.createStartContract();
|
||||
|
||||
|
|
|
@ -91,15 +91,19 @@ export class EmbeddablePanel extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
private async refreshBadges() {
|
||||
const badges = await this.props.getActions(PANEL_BADGE_TRIGGER, {
|
||||
let badges: IAction[] = await this.props.getActions(PANEL_BADGE_TRIGGER, {
|
||||
embeddable: this.props.embeddable,
|
||||
});
|
||||
if (!this.mounted) return;
|
||||
|
||||
if (this.mounted) {
|
||||
this.setState({
|
||||
badges,
|
||||
});
|
||||
const { disabledActions } = this.props.embeddable.getInput();
|
||||
if (disabledActions) {
|
||||
badges = badges.filter(badge => disabledActions.indexOf(badge.id) === -1);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
badges,
|
||||
});
|
||||
}
|
||||
|
||||
public UNSAFE_componentWillMount() {
|
||||
|
@ -200,10 +204,15 @@ export class EmbeddablePanel extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
private getActionContextMenuPanel = async () => {
|
||||
const actions = await this.props.getActions(CONTEXT_MENU_TRIGGER, {
|
||||
let actions = await this.props.getActions(CONTEXT_MENU_TRIGGER, {
|
||||
embeddable: this.props.embeddable,
|
||||
});
|
||||
|
||||
const { disabledActions } = this.props.embeddable.getInput();
|
||||
if (disabledActions) {
|
||||
actions = actions.filter(action => disabledActions.indexOf(action.id) === -1);
|
||||
}
|
||||
|
||||
const createGetUserData = (overlays: OverlayStart) =>
|
||||
async function getUserData(context: { embeddable: IEmbeddable }) {
|
||||
return new Promise<{ title: string | undefined }>(resolve => {
|
||||
|
|
|
@ -23,7 +23,7 @@ import { Loader } from '../loader';
|
|||
import { useStateToaster } from '../toasters';
|
||||
import { Embeddable } from './embeddable';
|
||||
import { EmbeddableHeader } from './embeddable_header';
|
||||
import { createEmbeddable, displayErrorToast, setupEmbeddablesAPI } from './embedded_map_helpers';
|
||||
import { createEmbeddable, displayErrorToast } from './embedded_map_helpers';
|
||||
import { IndexPatternsMissingPrompt } from './index_patterns_missing_prompt';
|
||||
import { MapToolTip } from './map_tool_tip/map_tool_tip';
|
||||
import * as i18n from './translations';
|
||||
|
@ -104,17 +104,6 @@ export const EmbeddedMapComponent = ({
|
|||
const plugins = useKibanaPlugins();
|
||||
const core = useKibanaCore();
|
||||
|
||||
// Setup embeddables API (i.e. detach extra actions) useEffect
|
||||
useEffect(() => {
|
||||
try {
|
||||
setupEmbeddablesAPI(plugins);
|
||||
} catch (e) {
|
||||
displayErrorToast(i18n.ERROR_CONFIGURING_EMBEDDABLES_API, e.message, dispatchToaster);
|
||||
setIsLoading(false);
|
||||
setIsError(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Initial Load useEffect
|
||||
useEffect(() => {
|
||||
let isSubscribed = true;
|
||||
|
|
|
@ -4,10 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { createEmbeddable, displayErrorToast, setupEmbeddablesAPI } from './embedded_map_helpers';
|
||||
import { createEmbeddable, displayErrorToast } from './embedded_map_helpers';
|
||||
import { createUiNewPlatformMock } from 'ui/new_platform/__mocks__/helpers';
|
||||
import { createPortalNode } from 'react-reverse-portal';
|
||||
import { PluginsStart } from 'ui/new_platform/new_platform';
|
||||
|
||||
jest.mock('ui/new_platform');
|
||||
jest.mock('../../lib/settings/use_kibana_ui_setting');
|
||||
|
@ -45,13 +44,6 @@ describe('embedded_map_helpers', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('setupEmbeddablesAPI', () => {
|
||||
test('detaches extra UI actions', () => {
|
||||
setupEmbeddablesAPI((npStart.plugins as unknown) as PluginsStart);
|
||||
expect(npStart.plugins.uiActions.detachAction).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createEmbeddable', () => {
|
||||
test('attaches refresh action', async () => {
|
||||
const setQueryMock = jest.fn();
|
||||
|
|
|
@ -7,14 +7,8 @@
|
|||
import uuid from 'uuid';
|
||||
import React from 'react';
|
||||
import { OutPortal, PortalNode } from 'react-reverse-portal';
|
||||
import { PluginsStart } from 'ui/new_platform/new_platform';
|
||||
|
||||
import { ActionToaster, AppToast } from '../toasters';
|
||||
import {
|
||||
CONTEXT_MENU_TRIGGER,
|
||||
PANEL_BADGE_TRIGGER,
|
||||
ViewMode,
|
||||
} from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public';
|
||||
import { ViewMode } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public';
|
||||
import {
|
||||
IndexPatternMapping,
|
||||
MapEmbeddable,
|
||||
|
@ -53,23 +47,6 @@ export const displayErrorToast = (
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Temporary Embeddables API configuration override until ability to edit actions is addressed:
|
||||
* https://github.com/elastic/kibana/issues/43643
|
||||
*
|
||||
* @param plugins new platform plugins
|
||||
*
|
||||
* @throws Error if trigger/action doesn't exist
|
||||
*/
|
||||
export const setupEmbeddablesAPI = (plugins: PluginsStart) => {
|
||||
try {
|
||||
plugins.uiActions.detachAction(CONTEXT_MENU_TRIGGER, 'CUSTOM_TIME_RANGE');
|
||||
plugins.uiActions.detachAction(PANEL_BADGE_TRIGGER, 'CUSTOM_TIME_RANGE_BADGE');
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates MapEmbeddable with provided initial configuration
|
||||
*
|
||||
|
@ -115,6 +92,7 @@ export const createEmbeddable = async (
|
|||
openTOCDetails: [],
|
||||
hideFilterActions: false,
|
||||
mapCenter: { lon: -1.05469, lat: 15.96133, zoom: 1 },
|
||||
disabledActions: ['CUSTOM_TIME_RANGE', 'CUSTOM_TIME_RANGE_BADGE'],
|
||||
};
|
||||
|
||||
const renderTooltipContent = ({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue