Skip to content

Commit 7be06dd

Browse files
committed
Merge branch 'master' into expose-fatal-errors-start
2 parents b0a0917 + 3e567b5 commit 7be06dd

File tree

61 files changed

+1205
-585
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1205
-585
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CoreSetup](./kibana-plugin-server.coresetup.md) &gt; [getStartServices](./kibana-plugin-server.coresetup.getstartservices.md)
4+
5+
## CoreSetup.getStartServices() method
6+
7+
Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed `start`<!-- -->. This should only be used inside handlers registered during `setup` that will only be executed after `start` lifecycle.
8+
9+
<b>Signature:</b>
10+
11+
```typescript
12+
getStartServices(): Promise<[CoreStart, TPluginsStart]>;
13+
```
14+
<b>Returns:</b>
15+
16+
`Promise<[CoreStart, TPluginsStart]>`
17+
Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,32 @@
1-
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2-
3-
[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CoreSetup](./kibana-plugin-server.coresetup.md)
4-
5-
## CoreSetup interface
6-
7-
Context passed to the plugins `setup` method.
8-
9-
<b>Signature:</b>
10-
11-
```typescript
12-
export interface CoreSetup
13-
```
14-
15-
## Properties
16-
17-
| Property | Type | Description |
18-
| --- | --- | --- |
19-
| [capabilities](./kibana-plugin-server.coresetup.capabilities.md) | <code>CapabilitiesSetup</code> | [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) |
20-
| [context](./kibana-plugin-server.coresetup.context.md) | <code>ContextSetup</code> | [ContextSetup](./kibana-plugin-server.contextsetup.md) |
21-
| [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | <code>ElasticsearchServiceSetup</code> | [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) |
22-
| [http](./kibana-plugin-server.coresetup.http.md) | <code>HttpServiceSetup</code> | [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) |
23-
| [savedObjects](./kibana-plugin-server.coresetup.savedobjects.md) | <code>SavedObjectsServiceSetup</code> | [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) |
24-
| [uiSettings](./kibana-plugin-server.coresetup.uisettings.md) | <code>UiSettingsServiceSetup</code> | [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) |
25-
| [uuid](./kibana-plugin-server.coresetup.uuid.md) | <code>UuidServiceSetup</code> | [UuidServiceSetup](./kibana-plugin-server.uuidservicesetup.md) |
26-
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CoreSetup](./kibana-plugin-server.coresetup.md)
4+
5+
## CoreSetup interface
6+
7+
Context passed to the plugins `setup` method.
8+
9+
<b>Signature:</b>
10+
11+
```typescript
12+
export interface CoreSetup<TPluginsStart extends object = object>
13+
```
14+
15+
## Properties
16+
17+
| Property | Type | Description |
18+
| --- | --- | --- |
19+
| [capabilities](./kibana-plugin-server.coresetup.capabilities.md) | <code>CapabilitiesSetup</code> | [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) |
20+
| [context](./kibana-plugin-server.coresetup.context.md) | <code>ContextSetup</code> | [ContextSetup](./kibana-plugin-server.contextsetup.md) |
21+
| [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | <code>ElasticsearchServiceSetup</code> | [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) |
22+
| [http](./kibana-plugin-server.coresetup.http.md) | <code>HttpServiceSetup</code> | [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) |
23+
| [savedObjects](./kibana-plugin-server.coresetup.savedobjects.md) | <code>SavedObjectsServiceSetup</code> | [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) |
24+
| [uiSettings](./kibana-plugin-server.coresetup.uisettings.md) | <code>UiSettingsServiceSetup</code> | [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) |
25+
| [uuid](./kibana-plugin-server.coresetup.uuid.md) | <code>UuidServiceSetup</code> | [UuidServiceSetup](./kibana-plugin-server.uuidservicesetup.md) |
26+
27+
## Methods
28+
29+
| Method | Description |
30+
| --- | --- |
31+
| [getStartServices()](./kibana-plugin-server.coresetup.getstartservices.md) | Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed <code>start</code>. This should only be used inside handlers registered during <code>setup</code> that will only be executed after <code>start</code> lifecycle. |
32+

src/core/public/application/__snapshots__/application_service.test.ts.snap

Lines changed: 84 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/public/application/application_service.test.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -525,17 +525,7 @@ describe('#start()', () => {
525525
const { getComponent } = await service.start(startDeps);
526526

527527
expect(() => shallow(createElement(getComponent))).not.toThrow();
528-
expect(getComponent()).toMatchInlineSnapshot(`
529-
<AppRouter
530-
history={
531-
Object {
532-
"push": [MockFunction],
533-
}
534-
}
535-
mounters={Map {}}
536-
setAppLeaveHandler={[Function]}
537-
/>
538-
`);
528+
expect(getComponent()).toMatchSnapshot();
539529
});
540530

541531
it('renders null when in legacy mode', async () => {

src/core/public/application/application_service.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import React from 'react';
2121
import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs';
22-
import { map, takeUntil } from 'rxjs/operators';
22+
import { map, shareReplay, takeUntil } from 'rxjs/operators';
2323
import { createBrowserHistory, History } from 'history';
2424

2525
import { InjectedMetadataSetup } from '../injected_metadata';
@@ -256,6 +256,11 @@ export class ApplicationService {
256256
)
257257
.subscribe(apps => applications$.next(apps));
258258

259+
const applicationStatuses$ = applications$.pipe(
260+
map(apps => new Map([...apps.entries()].map(([id, app]) => [id, app.status!]))),
261+
shareReplay(1)
262+
);
263+
259264
return {
260265
applications$,
261266
capabilities,
@@ -264,11 +269,6 @@ export class ApplicationService {
264269
getUrlForApp: (appId, { path }: { path?: string } = {}) =>
265270
getAppUrl(availableMounters, appId, path),
266271
navigateToApp: async (appId, { path, state }: { path?: string; state?: any } = {}) => {
267-
const app = applications$.value.get(appId);
268-
if (app && app.status !== AppStatus.accessible) {
269-
// should probably redirect to the error page instead
270-
throw new Error(`Trying to navigate to an inaccessible application: ${appId}`);
271-
}
272272
if (await this.shouldNavigate(overlays)) {
273273
this.appLeaveHandlers.delete(this.currentAppId$.value!);
274274
this.navigate!(getAppUrl(availableMounters, appId, path), state);
@@ -283,6 +283,7 @@ export class ApplicationService {
283283
<AppRouter
284284
history={this.history}
285285
mounters={availableMounters}
286+
appStatuses$={applicationStatuses$}
286287
setAppLeaveHandler={this.setAppLeaveHandler}
287288
/>
288289
);

src/core/public/application/integration_tests/router.test.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@
1818
*/
1919

2020
import React from 'react';
21+
import { BehaviorSubject } from 'rxjs';
2122
import { createMemoryHistory, History, createHashHistory } from 'history';
2223

2324
import { AppRouter, AppNotFound } from '../ui';
2425
import { EitherApp, MockedMounterMap, MockedMounterTuple } from '../test_types';
2526
import { createRenderer, createAppMounter, createLegacyAppMounter } from './utils';
27+
import { AppStatus } from '../types';
2628

2729
describe('AppContainer', () => {
2830
let mounters: MockedMounterMap<EitherApp>;
2931
let history: History;
32+
let appStatuses$: BehaviorSubject<Map<string, AppStatus>>;
3033
let update: ReturnType<typeof createRenderer>;
3134

3235
const navigate = (path: string) => {
@@ -38,19 +41,34 @@ describe('AppContainer', () => {
3841
new Map([...mounters].map(([appId, { mounter }]) => [appId, mounter]));
3942
const setAppLeaveHandlerMock = () => undefined;
4043

44+
const mountersToAppStatus$ = () => {
45+
return new BehaviorSubject(
46+
new Map(
47+
[...mounters.keys()].map(id => [
48+
id,
49+
id.startsWith('disabled') ? AppStatus.inaccessible : AppStatus.accessible,
50+
])
51+
)
52+
);
53+
};
54+
4155
beforeEach(() => {
4256
mounters = new Map([
4357
createAppMounter('app1', '<span>App 1</span>'),
4458
createLegacyAppMounter('legacyApp1', jest.fn()),
4559
createAppMounter('app2', '<div>App 2</div>'),
4660
createLegacyAppMounter('baseApp:legacyApp2', jest.fn()),
4761
createAppMounter('app3', '<div>App 3</div>', '/custom/path'),
62+
createAppMounter('disabledApp', '<div>Disabled app</div>'),
63+
createLegacyAppMounter('disabledLegacyApp', jest.fn()),
4864
] as Array<MockedMounterTuple<EitherApp>>);
4965
history = createMemoryHistory();
66+
appStatuses$ = mountersToAppStatus$();
5067
update = createRenderer(
5168
<AppRouter
5269
history={history}
5370
mounters={mockMountersToMounters()}
71+
appStatuses$={appStatuses$}
5472
setAppLeaveHandler={setAppLeaveHandlerMock}
5573
/>
5674
);
@@ -89,6 +107,7 @@ describe('AppContainer', () => {
89107
<AppRouter
90108
history={history}
91109
mounters={mockMountersToMounters()}
110+
appStatuses$={mountersToAppStatus$()}
92111
setAppLeaveHandler={setAppLeaveHandlerMock}
93112
/>
94113
);
@@ -107,6 +126,7 @@ describe('AppContainer', () => {
107126
<AppRouter
108127
history={history}
109128
mounters={mockMountersToMounters()}
129+
appStatuses$={mountersToAppStatus$()}
110130
setAppLeaveHandler={setAppLeaveHandlerMock}
111131
/>
112132
);
@@ -147,6 +167,7 @@ describe('AppContainer', () => {
147167
<AppRouter
148168
history={history}
149169
mounters={mockMountersToMounters()}
170+
appStatuses$={mountersToAppStatus$()}
150171
setAppLeaveHandler={setAppLeaveHandlerMock}
151172
/>
152173
);
@@ -202,4 +223,16 @@ describe('AppContainer', () => {
202223

203224
expect(dom?.exists(AppNotFound)).toBe(true);
204225
});
226+
227+
it('displays error page if app is inaccessible', async () => {
228+
const dom = await navigate('/app/disabledApp');
229+
230+
expect(dom?.exists(AppNotFound)).toBe(true);
231+
});
232+
233+
it('displays error page if legacy app is inaccessible', async () => {
234+
const dom = await navigate('/app/disabledLegacyApp');
235+
236+
expect(dom?.exists(AppNotFound)).toBe(true);
237+
});
205238
});

src/core/public/application/ui/app_container.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,26 @@ import React, {
2626
MutableRefObject,
2727
} from 'react';
2828

29-
import { AppUnmount, Mounter, AppLeaveHandler } from '../types';
29+
import { AppLeaveHandler, AppStatus, AppUnmount, Mounter } from '../types';
3030
import { AppNotFound } from './app_not_found_screen';
3131

3232
interface Props {
3333
appId: string;
3434
mounter?: Mounter;
35+
appStatus: AppStatus;
3536
setAppLeaveHandler: (appId: string, handler: AppLeaveHandler) => void;
3637
}
3738

3839
export const AppContainer: FunctionComponent<Props> = ({
3940
mounter,
4041
appId,
4142
setAppLeaveHandler,
43+
appStatus,
4244
}: Props) => {
4345
const [appNotFound, setAppNotFound] = useState(false);
4446
const elementRef = useRef<HTMLDivElement>(null);
4547
const unmountRef: MutableRefObject<AppUnmount | null> = useRef<AppUnmount>(null);
48+
// const appStatus = useObservable(appStatus$);
4649

4750
useLayoutEffect(() => {
4851
const unmount = () => {
@@ -52,7 +55,7 @@ export const AppContainer: FunctionComponent<Props> = ({
5255
}
5356
};
5457
const mount = async () => {
55-
if (!mounter) {
58+
if (!mounter || appStatus !== AppStatus.accessible) {
5659
return setAppNotFound(true);
5760
}
5861

@@ -71,7 +74,7 @@ export const AppContainer: FunctionComponent<Props> = ({
7174

7275
mount();
7376
return unmount;
74-
}, [appId, mounter, setAppLeaveHandler]);
77+
}, [appId, appStatus, mounter, setAppLeaveHandler]);
7578

7679
return (
7780
<Fragment>

src/core/public/application/ui/app_not_found_screen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import React from 'react';
2222
import { FormattedMessage } from '@kbn/i18n/react';
2323

2424
export const AppNotFound = () => (
25-
<EuiPage style={{ minHeight: '100%' }}>
25+
<EuiPage style={{ minHeight: '100%' }} data-test-subj="appNotFoundPageContent">
2626
<EuiPageBody>
2727
<EuiPageContent verticalPosition="center" horizontalPosition="center">
2828
<EuiEmptyPrompt

0 commit comments

Comments
 (0)