Skip to content

Commit

Permalink
Disabled actions (#51975)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
streamich authored Dec 4, 2019
1 parent 085a2af commit 73651a1
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 52 deletions.
5 changes: 5 additions & 0 deletions src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
102 changes: 101 additions & 1 deletion src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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>();
Expand Down Expand Up @@ -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();

Expand Down
21 changes: 15 additions & 6 deletions src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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 => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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 = ({
Expand Down

0 comments on commit 73651a1

Please sign in to comment.