Skip to content

Commit

Permalink
Add option in context menu; add error handling when fetching embeddab…
Browse files Browse the repository at this point in the history
…les in flyout

Signed-off-by: Tyler Ohlsen <[email protected]>
  • Loading branch information
ohltyler committed Feb 8, 2023
1 parent a496f75 commit bb4b445
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 27 deletions.
1 change: 1 addition & 0 deletions src/plugins/vis_augmenter/public/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
*/

export { OPEN_EVENTS_FLYOUT_ACTION, OpenEventsFlyoutAction } from './open_events_flyout_action';
export { VIEW_EVENTS_OPTION_ACTION, ViewEventsOptionAction } from './view_events_option_action';
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,28 @@

import { i18n } from '@osd/i18n';
import { CoreStart } from 'opensearch-dashboards/public';
import {
Action,
ActionExecutionContext,
IncompatibleActionError,
} from '../../../ui_actions/public';
import { Action, IncompatibleActionError } from '../../../ui_actions/public';
import { AugmentVisContext } from '../triggers';
import { openViewEventsFlyout } from './open_events_flyout';

export const OPEN_EVENTS_FLYOUT_ACTION = 'OPEN_EVENTS_FLYOUT_ACTION';

/**
* This action is identical to VIEW_EVENTS_OPTION_ACTION, but with different context.
* This is because the chart doesn't persist the embeddable, which is the default
* context used by the CONTEXT_MENU_TRIGGER. Because of that, we need a separate
* one that can be persisted in the chart - in this case, the AugmentVisContext,
* which is just a saved object ID.
*/

export class OpenEventsFlyoutAction implements Action<AugmentVisContext> {
public readonly type = OPEN_EVENTS_FLYOUT_ACTION;
public readonly id = OPEN_EVENTS_FLYOUT_ACTION;
public order = 1;

constructor(private core: CoreStart) {}

public getIconType(context: ActionExecutionContext<AugmentVisContext>) {
public getIconType() {
return undefined;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { i18n } from '@osd/i18n';
import { get } from 'lodash';
import { CoreStart } from 'opensearch-dashboards/public';
import { VisualizeEmbeddable } from '../../../visualizations/public';
import { EmbeddableContext } from '../../../embeddable/public';
import { EuiIconType } from '@elastic/eui/src/components/icon/icon';
import { Action, IncompatibleActionError } from '../../../ui_actions/public';
import { openViewEventsFlyout } from './open_events_flyout';

export const VIEW_EVENTS_OPTION_ACTION = 'VIEW_EVENTS_OPTION_ACTION';

export class ViewEventsOptionAction implements Action<EmbeddableContext> {
public readonly type = VIEW_EVENTS_OPTION_ACTION;
public readonly id = VIEW_EVENTS_OPTION_ACTION;
public order = 1;

constructor(private core: CoreStart) {}

public getIconType(): EuiIconType {
return 'apmTrace';
}

public getDisplayName() {
return i18n.translate('dashboard.actions.viewEvents.displayName', {
defaultMessage: 'View Events',
});
}

public async isCompatible({ embeddable }: EmbeddableContext) {
// TODO: add the logic for compatibility here, probably from some helper fn.
// see https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268
return true;
}

public async execute({ embeddable }: EmbeddableContext) {
if (!(await this.isCompatible({ embeddable }))) {
throw new IncompatibleActionError();
}

const visEmbeddable = embeddable as VisualizeEmbeddable;
const savedObjectId = get(visEmbeddable.getInput(), 'savedObjectId', '');

openViewEventsFlyout({
core: this.core,
savedObjectId,
});
}
}
25 changes: 25 additions & 0 deletions src/plugins/vis_augmenter/public/components/error_flyout_body.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { EuiFlyoutBody, EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui';

interface Props {
errorMessage: string;
}

export function ErrorFlyoutBody(props: Props) {
return (
<EuiFlyoutBody>
<EuiFlexGroup>
<EuiFlexItem grow={true}>
<EuiCallOut color="danger" iconType="alert">
{props.errorMessage}
</EuiCallOut>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutBody>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import React from 'react';
import { EuiFlyoutBody, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';

export function LoadingFlyout() {
export function LoadingFlyoutBody() {
return (
<EuiFlyoutBody>
<EuiFlexGroup justifyContent="spaceAround">
Expand Down
8 changes: 8 additions & 0 deletions src/plugins/vis_augmenter/public/components/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,11 @@
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { ErrorEmbeddable } from '../../../embeddable/public';

export const getErrorMessage = (errorEmbeddable: ErrorEmbeddable): string => {
return errorEmbeddable.error instanceof Error
? errorEmbeddable.error.message
: errorEmbeddable.error;
};
47 changes: 33 additions & 14 deletions src/plugins/vis_augmenter/public/components/view_events_flyout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
EuiTitle,
EuiFlexGroup,
EuiFlexItem,
EuiText,
} from '@elastic/eui';
import { getEmbeddable, getQueryService } from '../services';
import './styles.scss';
Expand All @@ -21,9 +20,12 @@ import { TimeRange } from '../../../data/common';
import { BaseVisItem } from './base_vis_item';
import { isPointInTimeEventsVisLayer, PointInTimeEventsVisLayer, VisLayer } from '../../common';
import { DateRangeItem } from './date_range_item';
import { LoadingFlyout } from './loading_flyout';
import { LoadingFlyoutBody } from './loading_flyout_body';
import { ErrorFlyoutBody } from './error_flyout_body';
import { EventsPanel } from './events_panel';
import { TimelinePanel } from './timeline_panel';
import { ErrorEmbeddable } from '../../../embeddable/public';
import { getErrorMessage } from './utils';

interface Props {
onClose: () => void;
Expand Down Expand Up @@ -51,6 +53,7 @@ export function ViewEventsFlyout(props: Props) {
>(undefined);
const [timeRange, setTimeRange] = useState<TimeRange | undefined>(undefined);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);

const embeddableVisFactory = getEmbeddable().getEmbeddableFactory('visualization');

Expand Down Expand Up @@ -83,7 +86,12 @@ export function ViewEventsFlyout(props: Props) {
const embeddable = (await embeddableVisFactory?.createFromSavedObject(
props.savedObjectId,
contextInput
)) as VisualizeEmbeddable;
)) as VisualizeEmbeddable | ErrorEmbeddable;

if (embeddable instanceof ErrorEmbeddable) {
throw getErrorMessage(embeddable);
}

embeddable.updateInput({
// @ts-ignore
refreshConfig: {
Expand All @@ -97,8 +105,8 @@ export function ViewEventsFlyout(props: Props) {
embeddable.reload();

setVisEmbeddable(embeddable);
} catch (err) {
console.log(err);
} catch (err: any) {
setErrorMessage(String(err));
}
}

Expand Down Expand Up @@ -129,7 +137,11 @@ export function ViewEventsFlyout(props: Props) {
...contextInput,
visLayerResourceIds: [visLayer.pluginResource.id as string],
} as VisualizeInput
)) as VisualizeEmbeddable;
)) as VisualizeEmbeddable | ErrorEmbeddable;

if (eventEmbeddable instanceof ErrorEmbeddable) {
throw getErrorMessage(eventEmbeddable);
}

eventEmbeddable.updateInput({
// @ts-ignore
Expand All @@ -151,8 +163,8 @@ export function ViewEventsFlyout(props: Props) {
);
setEventVisEmbeddablesMap(map);
}
} catch (err) {
console.log(err);
} catch (err: any) {
setErrorMessage(String(err));
}
}

Expand All @@ -169,7 +181,12 @@ export function ViewEventsFlyout(props: Props) {
const embeddable = (await embeddableVisFactory?.createFromSavedObject(
props.savedObjectId,
contextInput
)) as VisualizeEmbeddable;
)) as VisualizeEmbeddable | ErrorEmbeddable;

if (embeddable instanceof ErrorEmbeddable) {
throw getErrorMessage(embeddable);
}

embeddable.updateInput({
// @ts-ignore
refreshConfig: {
Expand All @@ -178,8 +195,8 @@ export function ViewEventsFlyout(props: Props) {
},
});
setTimelineVisEmbeddable(embeddable);
} catch (err) {
console.log(err);
} catch (err: any) {
setErrorMessage(String(err));
}
}

Expand Down Expand Up @@ -210,11 +227,13 @@ export function ViewEventsFlyout(props: Props) {
<EuiFlyout size="l" onClose={props.onClose}>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="l">
<h1>{isLoading ? <>&nbsp;</> : `${visEmbeddable.getTitle()}`}</h1>
<h1>{isLoading || errorMessage ? <>&nbsp;</> : `${visEmbeddable.getTitle()}`}</h1>
</EuiTitle>
</EuiFlyoutHeader>
{isLoading ? (
<LoadingFlyout />
{errorMessage ? (
<ErrorFlyoutBody errorMessage={errorMessage} />
) : isLoading ? (
<LoadingFlyoutBody />
) : (
<EuiFlyoutBody>
<EuiFlexGroup className="view-events-flyout__content" direction="column">
Expand Down
20 changes: 14 additions & 6 deletions src/plugins/vis_augmenter/public/ui_actions_bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@
*/

import { CoreStart } from 'opensearch-dashboards/public';
import { OpenEventsFlyoutAction, OPEN_EVENTS_FLYOUT_ACTION } from './actions';
import {
OpenEventsFlyoutAction,
ViewEventsOptionAction,
OPEN_EVENTS_FLYOUT_ACTION,
VIEW_EVENTS_OPTION_ACTION,
} from './actions';
import { AugmentVisContext, openEventsFlyoutTrigger } from './triggers';
import { OPEN_EVENTS_FLYOUT_TRIGGER } from '../../ui_actions/public';
import { CONTEXT_MENU_TRIGGER, EmbeddableContext } from '../../embeddable/public';
import { getUiActions } from './services';

// Overridding the mappings defined in UIActions plugin so that
Expand All @@ -18,17 +24,19 @@ declare module '../../ui_actions/public' {

export interface ActionContextMapping {
[OPEN_EVENTS_FLYOUT_ACTION]: AugmentVisContext;
[VIEW_EVENTS_OPTION_ACTION]: EmbeddableContext;
}
}

/**
* These fns initialize VisAugmenter plugin with initial set of
* triggers and actions, and mapping any triggers -> actions
*/

export const registerTriggersAndActions = (core: CoreStart) => {
const openEventsFlyoutAction = new OpenEventsFlyoutAction(core);
const viewEventsOptionAction = new ViewEventsOptionAction(core);

getUiActions().registerAction(openEventsFlyoutAction);
getUiActions().registerAction(viewEventsOptionAction);
getUiActions().registerTrigger(openEventsFlyoutTrigger);
// Opening View Events flyout from the chart
getUiActions().addTriggerAction(OPEN_EVENTS_FLYOUT_TRIGGER, openEventsFlyoutAction);
// Opening View Events flyout from the context menu
getUiActions().addTriggerAction(CONTEXT_MENU_TRIGGER, viewEventsOptionAction);
};

0 comments on commit bb4b445

Please sign in to comment.