Skip to content

Commit 97164a7

Browse files
authored
[Time to Visualize] Embeddable Error Handling Without ReplacePanel (#82201) (#82742)
Fixed embeddable error handling so that fatal errors are caught and displayed with an errorEmbeddable no matter when they occur.
1 parent fc89b7a commit 97164a7

21 files changed

Lines changed: 181 additions & 32 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) &gt; [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) &gt; [fatalError](./kibana-plugin-plugins-embeddable-public.embeddable.fatalerror.md)
4+
5+
## Embeddable.fatalError property
6+
7+
<b>Signature:</b>
8+
9+
```typescript
10+
fatalError?: Error;
11+
```

docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddable.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export declare abstract class Embeddable<TEmbeddableInput extends EmbeddableInpu
2020
2121
| Property | Modifiers | Type | Description |
2222
| --- | --- | --- | --- |
23+
| [fatalError](./kibana-plugin-plugins-embeddable-public.embeddable.fatalerror.md) | | <code>Error</code> | |
2324
| [id](./kibana-plugin-plugins-embeddable-public.embeddable.id.md) | | <code>string</code> | |
2425
| [input](./kibana-plugin-plugins-embeddable-public.embeddable.input.md) | | <code>TEmbeddableInput</code> | |
2526
| [isContainer](./kibana-plugin-plugins-embeddable-public.embeddable.iscontainer.md) | | <code>boolean</code> | |
@@ -43,6 +44,7 @@ export declare abstract class Embeddable<TEmbeddableInput extends EmbeddableInpu
4344
| [getOutput$()](./kibana-plugin-plugins-embeddable-public.embeddable.getoutput_.md) | | |
4445
| [getRoot()](./kibana-plugin-plugins-embeddable-public.embeddable.getroot.md) | | Returns the top most parent embeddable, or itself if this embeddable is not within a parent. |
4546
| [getTitle()](./kibana-plugin-plugins-embeddable-public.embeddable.gettitle.md) | | |
47+
| [onFatalError(e)](./kibana-plugin-plugins-embeddable-public.embeddable.onfatalerror.md) | | |
4648
| [reload()](./kibana-plugin-plugins-embeddable-public.embeddable.reload.md) | | Reload will be called when there is a request to refresh the data or view, even if the input data did not change. |
4749
| [render(el)](./kibana-plugin-plugins-embeddable-public.embeddable.render.md) | | |
4850
| [supportedTriggers()](./kibana-plugin-plugins-embeddable-public.embeddable.supportedtriggers.md) | | |
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) &gt; [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) &gt; [onFatalError](./kibana-plugin-plugins-embeddable-public.embeddable.onfatalerror.md)
4+
5+
## Embeddable.onFatalError() method
6+
7+
<b>Signature:</b>
8+
9+
```typescript
10+
protected onFatalError(e: Error): void;
11+
```
12+
13+
## Parameters
14+
15+
| Parameter | Type | Description |
16+
| --- | --- | --- |
17+
| e | <code>Error</code> | |
18+
19+
<b>Returns:</b>
20+
21+
`void`
22+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) &gt; [IEmbeddable](./kibana-plugin-plugins-embeddable-public.iembeddable.md) &gt; [fatalError](./kibana-plugin-plugins-embeddable-public.iembeddable.fatalerror.md)
4+
5+
## IEmbeddable.fatalError property
6+
7+
If this embeddable has encountered a fatal error, that error will be stored here
8+
9+
<b>Signature:</b>
10+
11+
```typescript
12+
fatalError?: Error;
13+
```

docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.iembeddable.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface IEmbeddable<I extends EmbeddableInput = EmbeddableInput, O exte
1515
| Property | Type | Description |
1616
| --- | --- | --- |
1717
| [enhancements](./kibana-plugin-plugins-embeddable-public.iembeddable.enhancements.md) | <code>object</code> | Extra abilities added to Embeddable by <code>*_enhanced</code> plugins. |
18+
| [fatalError](./kibana-plugin-plugins-embeddable-public.iembeddable.fatalerror.md) | <code>Error</code> | If this embeddable has encountered a fatal error, that error will be stored here |
1819
| [id](./kibana-plugin-plugins-embeddable-public.iembeddable.id.md) | <code>string</code> | A unique identifier for this embeddable. Mainly only used by containers to map their Panel States to a child embeddable instance. |
1920
| [isContainer](./kibana-plugin-plugins-embeddable-public.iembeddable.iscontainer.md) | <code>boolean</code> | Is this embeddable an instance of a Container class, can it contain nested embeddables? |
2021
| [parent](./kibana-plugin-plugins-embeddable-public.iembeddable.parent.md) | <code>IContainer</code> | If this embeddable is nested inside a container, this will contain a reference to its parent. |

src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { coreMock } from '../../../../../core/public/mocks';
3535
import { CoreStart } from 'kibana/public';
3636
import { AddToLibraryAction } from '.';
3737
import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks';
38-
import { ViewMode } from '../../../../embeddable/public';
38+
import { ErrorEmbeddable, ViewMode } from '../../../../embeddable/public';
3939

4040
const { setup, doStart } = embeddablePluginMock.createInstance();
4141
setup.registerEmbeddableFactory(
@@ -86,6 +86,16 @@ beforeEach(async () => {
8686
}
8787
});
8888

89+
test('Add to library is incompatible with Error Embeddables', async () => {
90+
const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts });
91+
const errorEmbeddable = new ErrorEmbeddable(
92+
'Wow what an awful error',
93+
{ id: ' 404' },
94+
embeddable.getRoot() as IContainer
95+
);
96+
expect(await action.isCompatible({ embeddable: errorEmbeddable })).toBe(false);
97+
});
98+
8999
test('Add to library is compatible when embeddable on dashboard has value type input', async () => {
90100
const action = new AddToLibraryAction({ toasts: coreStart.notifications.toasts });
91101
embeddable.updateInput(await embeddable.getInputAsValueType());

src/plugins/dashboard/public/application/actions/add_to_library_action.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
PanelNotFoundError,
2727
EmbeddableInput,
2828
isReferenceOrValueEmbeddable,
29+
isErrorEmbeddable,
2930
} from '../../../../embeddable/public';
3031
import { NotificationsStart } from '../../../../../core/public';
3132
import { DashboardPanelState, DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '..';
@@ -61,7 +62,8 @@ export class AddToLibraryAction implements ActionByType<typeof ACTION_ADD_TO_LIB
6162

6263
public async isCompatible({ embeddable }: AddToLibraryActionContext) {
6364
return Boolean(
64-
embeddable.getInput()?.viewMode !== ViewMode.VIEW &&
65+
!isErrorEmbeddable(embeddable) &&
66+
embeddable.getInput()?.viewMode !== ViewMode.VIEW &&
6567
embeddable.getRoot() &&
6668
embeddable.getRoot().isContainer &&
6769
embeddable.getRoot().type === DASHBOARD_CONTAINER_TYPE &&

src/plugins/dashboard/public/application/actions/clone_panel_action.test.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
import { isErrorEmbeddable, IContainer } from '../../embeddable_plugin';
19+
import { isErrorEmbeddable, IContainer, ErrorEmbeddable } from '../../embeddable_plugin';
2020
import { DashboardContainer, DashboardPanelState } from '../embeddable';
2121
import { getSampleDashboardInput, getSampleDashboardPanel } from '../test_helpers';
2222
import {
@@ -86,6 +86,16 @@ beforeEach(async () => {
8686
}
8787
});
8888

89+
test('Clone is incompatible with Error Embeddables', async () => {
90+
const action = new ClonePanelAction(coreStart);
91+
const errorEmbeddable = new ErrorEmbeddable(
92+
'Wow what an awful error',
93+
{ id: ' 404' },
94+
embeddable.getRoot() as IContainer
95+
);
96+
expect(await action.isCompatible({ embeddable: errorEmbeddable })).toBe(false);
97+
});
98+
8999
test('Clone adds a new embeddable', async () => {
90100
const dashboard = embeddable.getRoot() as IContainer;
91101
const originalPanelCount = Object.keys(dashboard.getInput().panels).length;

src/plugins/dashboard/public/application/actions/clone_panel_action.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
PanelNotFoundError,
2929
EmbeddableInput,
3030
SavedObjectEmbeddableInput,
31+
isErrorEmbeddable,
3132
} from '../../../../embeddable/public';
3233
import {
3334
placePanelBeside,
@@ -66,7 +67,8 @@ export class ClonePanelAction implements ActionByType<typeof ACTION_CLONE_PANEL>
6667

6768
public async isCompatible({ embeddable }: ClonePanelActionContext) {
6869
return Boolean(
69-
embeddable.getInput()?.viewMode !== ViewMode.VIEW &&
70+
!isErrorEmbeddable(embeddable) &&
71+
embeddable.getInput()?.viewMode !== ViewMode.VIEW &&
7072
embeddable.getRoot() &&
7173
embeddable.getRoot().isContainer &&
7274
embeddable.getRoot().type === DASHBOARD_CONTAINER_TYPE

src/plugins/dashboard/public/application/actions/library_notification_action.test.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { coreMock } from '../../../../../core/public/mocks';
3030
import { CoreStart } from 'kibana/public';
3131
import { LibraryNotificationAction, UnlinkFromLibraryAction } from '.';
3232
import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks';
33-
import { ViewMode } from '../../../../embeddable/public';
33+
import { ErrorEmbeddable, IContainer, ViewMode } from '../../../../embeddable/public';
3434

3535
const { setup, doStart } = embeddablePluginMock.createInstance();
3636
setup.registerEmbeddableFactory(
@@ -87,6 +87,16 @@ beforeEach(async () => {
8787
embeddable.updateInput({ viewMode: ViewMode.EDIT });
8888
});
8989

90+
test('Notification is incompatible with Error Embeddables', async () => {
91+
const action = new LibraryNotificationAction(unlinkAction);
92+
const errorEmbeddable = new ErrorEmbeddable(
93+
'Wow what an awful error',
94+
{ id: ' 404' },
95+
embeddable.getRoot() as IContainer
96+
);
97+
expect(await action.isCompatible({ embeddable: errorEmbeddable })).toBe(false);
98+
});
99+
90100
test('Notification is shown when embeddable on dashboard has reference type input', async () => {
91101
const action = new LibraryNotificationAction(unlinkAction);
92102
embeddable.updateInput(await embeddable.getInputAsRefType());

0 commit comments

Comments
 (0)