Skip to content

Commit

Permalink
Add getStartServices API (#50231)
Browse files Browse the repository at this point in the history
  • Loading branch information
joshdover authored Dec 12, 2019
1 parent deee1c0 commit 2d36989
Show file tree
Hide file tree
Showing 39 changed files with 388 additions and 94 deletions.
2 changes: 1 addition & 1 deletion docs/development/core/public/kibana-plugin-public.app.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ export interface App extends AppBase
| Property | Type | Description |
| --- | --- | --- |
| [chromeless](./kibana-plugin-public.app.chromeless.md) | <code>boolean</code> | Hide the UI chrome when the application is mounted. Defaults to <code>false</code>. Takes precedence over chrome service visibility settings. |
| [mount](./kibana-plugin-public.app.mount.md) | <code>(context: AppMountContext, params: AppMountParameters) =&gt; AppUnmount &#124; Promise&lt;AppUnmount&gt;</code> | A mount function called when the user navigates to this app's route. |
| [mount](./kibana-plugin-public.app.mount.md) | <code>AppMount &#124; AppMountDeprecated</code> | A mount function called when the user navigates to this app's route. May have signature of [AppMount](./kibana-plugin-public.appmount.md) or [AppMountDeprecated](./kibana-plugin-public.appmountdeprecated.md)<!-- -->. |
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@

## App.mount property

A mount function called when the user navigates to this app's route.
A mount function called when the user navigates to this app's route. May have signature of [AppMount](./kibana-plugin-public.appmount.md) or [AppMountDeprecated](./kibana-plugin-public.appmountdeprecated.md)<!-- -->.

<b>Signature:</b>

```typescript
mount: (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise<AppUnmount>;
mount: AppMount | AppMountDeprecated;
```

## Remarks

When function has two arguments, it will be called with a [context](./kibana-plugin-public.appmountcontext.md) as the first argument. This behavior is \*\*deprecated\*\*, and consumers should instead use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md)<!-- -->.

Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ export interface ApplicationSetup
| Method | Description |
| --- | --- |
| [register(app)](./kibana-plugin-public.applicationsetup.register.md) | Register an mountable application to the system. |
| [registerMountContext(contextName, provider)](./kibana-plugin-public.applicationsetup.registermountcontext.md) | Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. |
| [registerMountContext(contextName, provider)](./kibana-plugin-public.applicationsetup.registermountcontext.md) | Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md)<!-- -->. |

Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@

## ApplicationSetup.registerMountContext() method

Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context.
> Warning: This API is now obsolete.
>
>
Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md)<!-- -->.

<b>Signature:</b>

```typescript
registerMountContext<T extends keyof AppMountContext>(contextName: T, provider: IContextProvider<App['mount'], T>): void;
registerMountContext<T extends keyof AppMountContext>(contextName: T, provider: IContextProvider<AppMountDeprecated, T>): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| contextName | <code>T</code> | The key of [AppMountContext](./kibana-plugin-public.appmountcontext.md) this provider's return value should be attached to. |
| provider | <code>IContextProvider&lt;App['mount'], T&gt;</code> | A [IContextProvider](./kibana-plugin-public.icontextprovider.md) function |
| provider | <code>IContextProvider&lt;AppMountDeprecated, T&gt;</code> | A [IContextProvider](./kibana-plugin-public.icontextprovider.md) function |
<b>Returns:</b>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ export interface ApplicationStart
| --- | --- |
| [getUrlForApp(appId, options)](./kibana-plugin-public.applicationstart.geturlforapp.md) | Returns a relative URL to a given app, including the global base path. |
| [navigateToApp(appId, options)](./kibana-plugin-public.applicationstart.navigatetoapp.md) | Navigiate to a given app |
| [registerMountContext(contextName, provider)](./kibana-plugin-public.applicationstart.registermountcontext.md) | Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. |
| [registerMountContext(contextName, provider)](./kibana-plugin-public.applicationstart.registermountcontext.md) | Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md)<!-- -->. |

Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@

## ApplicationStart.registerMountContext() method

Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context.
> Warning: This API is now obsolete.
>
>
Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md)<!-- -->.

<b>Signature:</b>

```typescript
registerMountContext<T extends keyof AppMountContext>(contextName: T, provider: IContextProvider<App['mount'], T>): void;
registerMountContext<T extends keyof AppMountContext>(contextName: T, provider: IContextProvider<AppMountDeprecated, T>): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| contextName | <code>T</code> | The key of [AppMountContext](./kibana-plugin-public.appmountcontext.md) this provider's return value should be attached to. |
| provider | <code>IContextProvider&lt;App['mount'], T&gt;</code> | A [IContextProvider](./kibana-plugin-public.icontextprovider.md) function |
| provider | <code>IContextProvider&lt;AppMountDeprecated, T&gt;</code> | A [IContextProvider](./kibana-plugin-public.icontextprovider.md) function |
<b>Returns:</b>
Expand Down
13 changes: 13 additions & 0 deletions docs/development/core/public/kibana-plugin-public.appmount.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [AppMount](./kibana-plugin-public.appmount.md)

## AppMount type

A mount function called when the user navigates to this app's route.

<b>Signature:</b>

```typescript
export declare type AppMount = (params: AppMountParameters) => AppUnmount | Promise<AppUnmount>;
```
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

## AppMountContext interface

The context object received when applications are mounted to the DOM.
> Warning: This API is now obsolete.
>
>
The context object received when applications are mounted to the DOM. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md)<!-- -->.

<b>Signature:</b>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [AppMountDeprecated](./kibana-plugin-public.appmountdeprecated.md)

## AppMountDeprecated type

> Warning: This API is now obsolete.
>
>
A mount function called when the user navigates to this app's route.

<b>Signature:</b>

```typescript
export declare type AppMountDeprecated = (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise<AppUnmount>;
```

## Remarks

When function has two arguments, it will be called with a [context](./kibana-plugin-public.appmountcontext.md) as the first argument. This behavior is \*\*deprecated\*\*, and consumers should instead use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md)<!-- -->.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

## CoreSetup.context property

> Warning: This API is now obsolete.
>
>
[ContextSetup](./kibana-plugin-public.contextsetup.md)

<b>Signature:</b>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [CoreSetup](./kibana-plugin-public.coresetup.md) &gt; [getStartServices](./kibana-plugin-public.coresetup.getstartservices.md)

## CoreSetup.getStartServices() method

Allows plugins to get access to APIs available in start inside async handlers, such as [App.mount](./kibana-plugin-public.app.mount.md)<!-- -->. Promise will not resolve until Core and plugin dependencies have completed `start`<!-- -->.

<b>Signature:</b>

```typescript
getStartServices(): Promise<[CoreStart, TPluginsStart]>;
```
<b>Returns:</b>

`Promise<[CoreStart, TPluginsStart]>`

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Core services exposed to the `Plugin` setup lifecycle
<b>Signature:</b>

```typescript
export interface CoreSetup
export interface CoreSetup<TPluginsStart extends object = object>
```

## Properties
Expand All @@ -24,3 +24,9 @@ export interface CoreSetup
| [notifications](./kibana-plugin-public.coresetup.notifications.md) | <code>NotificationsSetup</code> | [NotificationsSetup](./kibana-plugin-public.notificationssetup.md) |
| [uiSettings](./kibana-plugin-public.coresetup.uisettings.md) | <code>IUiSettingsClient</code> | [IUiSettingsClient](./kibana-plugin-public.iuisettingsclient.md) |

## Methods

| Method | Description |
| --- | --- |
| [getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md) | Allows plugins to get access to APIs available in start inside async handlers, such as [App.mount](./kibana-plugin-public.app.mount.md)<!-- -->. Promise will not resolve until Core and plugin dependencies have completed <code>start</code>. |

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Setup interface exposed to the legacy platform via the `ui/new_platform` module.
<b>Signature:</b>

```typescript
export interface LegacyCoreSetup extends CoreSetup
export interface LegacyCoreSetup extends CoreSetup<any>
```
## Properties
Expand Down
4 changes: 3 additions & 1 deletion docs/development/core/public/kibana-plugin-public.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [AppBase](./kibana-plugin-public.appbase.md) | |
| [ApplicationSetup](./kibana-plugin-public.applicationsetup.md) | |
| [ApplicationStart](./kibana-plugin-public.applicationstart.md) | |
| [AppMountContext](./kibana-plugin-public.appmountcontext.md) | The context object received when applications are mounted to the DOM. |
| [AppMountContext](./kibana-plugin-public.appmountcontext.md) | The context object received when applications are mounted to the DOM. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md)<!-- -->. |
| [AppMountParameters](./kibana-plugin-public.appmountparameters.md) | |
| [Capabilities](./kibana-plugin-public.capabilities.md) | The read-only set of capabilities available for the current UI session. Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID, and the boolean is a flag indicating if the capability is enabled or disabled. |
| [ChromeBadge](./kibana-plugin-public.chromebadge.md) | |
Expand Down Expand Up @@ -98,6 +98,8 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->

| Type Alias | Description |
| --- | --- |
| [AppMount](./kibana-plugin-public.appmount.md) | A mount function called when the user navigates to this app's route. |
| [AppMountDeprecated](./kibana-plugin-public.appmountdeprecated.md) | A mount function called when the user navigates to this app's route. |
| [AppUnmount](./kibana-plugin-public.appunmount.md) | A function called when an application should be unmounted from the page. This function should be synchronous. |
| [ChromeBreadcrumb](./kibana-plugin-public.chromebreadcrumb.md) | |
| [ChromeHelpExtensionMenuCustomLink](./kibana-plugin-public.chromehelpextensionmenucustomlink.md) | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
<b>Signature:</b>

```typescript
setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
setup(core: CoreSetup<TPluginsStart>, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| core | <code>CoreSetup</code> | |
| core | <code>CoreSetup&lt;TPluginsStart&gt;</code> | |
| plugins | <code>TPluginsSetup</code> | |

<b>Returns:</b>
Expand Down
36 changes: 25 additions & 11 deletions src/core/public/application/application_service.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ describe('#setup()', () => {
const service = new ApplicationService();
const context = contextServiceMock.createSetupContract();
const setup = service.setup({ context });
setup.register(Symbol(), { id: 'app1' } as any);
setup.register(Symbol(), { id: 'app1', mount: jest.fn() } as any);
expect(() =>
setup.register(Symbol(), { id: 'app1' } as any)
setup.register(Symbol(), { id: 'app1', mount: jest.fn() } as any)
).toThrowErrorMatchingInlineSnapshot(
`"An application is already registered with the id \\"app1\\""`
);
Expand All @@ -51,6 +51,18 @@ describe('#setup()', () => {
setup.register(Symbol(), { id: 'app1' } as any)
).toThrowErrorMatchingInlineSnapshot(`"Applications cannot be registered after \\"setup\\""`);
});

it('logs a warning when registering a deprecated app mount', async () => {
const consoleWarnSpy = jest.spyOn(console, 'warn');
const service = new ApplicationService();
const context = contextServiceMock.createSetupContract();
const setup = service.setup({ context });
setup.register(Symbol(), { id: 'app1', mount: (ctx: any, params: any) => {} } as any);
expect(consoleWarnSpy).toHaveBeenCalledWith(
`App [app1] is using deprecated mount context. Use core.getStartServices() instead.`
);
consoleWarnSpy.mockRestore();
});
});

describe('registerLegacyApp', () => {
Expand Down Expand Up @@ -100,20 +112,21 @@ describe('#start()', () => {
const service = new ApplicationService();
const context = contextServiceMock.createSetupContract();
const setup = service.setup({ context });
setup.register(Symbol(), { id: 'app1' } as any);
setup.register(Symbol(), { id: 'app1', mount: jest.fn() } as any);
setup.registerLegacyApp({ id: 'app2' } as any);

const http = httpServiceMock.createStartContract();
const injectedMetadata = injectedMetadataServiceMock.createStartContract();
const startContract = await service.start({ http, injectedMetadata });

expect(startContract.availableApps).toMatchInlineSnapshot(`
Map {
"app1" => Object {
"id": "app1",
},
}
`);
Map {
"app1" => Object {
"id": "app1",
"mount": [MockFunction],
},
}
`);
expect(startContract.availableLegacyApps).toMatchInlineSnapshot(`
Map {
"app2" => Object {
Expand All @@ -127,14 +140,15 @@ describe('#start()', () => {
const service = new ApplicationService();
const context = contextServiceMock.createSetupContract();
const setup = service.setup({ context });
setup.register(Symbol(), { id: 'app1' } as any);
const app1 = { id: 'app1', mount: jest.fn() };
setup.register(Symbol(), app1 as any);

const http = httpServiceMock.createStartContract();
const injectedMetadata = injectedMetadataServiceMock.createStartContract();
await service.start({ http, injectedMetadata });

expect(MockCapabilitiesService.start).toHaveBeenCalledWith({
apps: new Map([['app1', { id: 'app1' }]]),
apps: new Map([['app1', app1]]),
legacyApps: new Map(),
http,
});
Expand Down
35 changes: 26 additions & 9 deletions src/core/public/application/application_service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import { ContextSetup, IContextContainer } from '../context';
import {
App,
LegacyApp,
AppMounter,
AppMount,
AppMountDeprecated,
InternalApplicationSetup,
InternalApplicationStart,
} from './types';
Expand All @@ -50,7 +51,7 @@ interface StartDeps {

interface AppBox {
app: App;
mount: AppMounter;
mount: AppMount;
}

/**
Expand All @@ -61,7 +62,7 @@ export class ApplicationService {
private readonly apps$ = new BehaviorSubject<ReadonlyMap<string, AppBox>>(new Map());
private readonly legacyApps$ = new BehaviorSubject<ReadonlyMap<string, LegacyApp>>(new Map());
private readonly capabilities = new CapabilitiesService();
private mountContext?: IContextContainer<App['mount']>;
private mountContext?: IContextContainer<AppMountDeprecated>;

public setup({ context }: SetupDeps): InternalApplicationSetup {
this.mountContext = context.createContextContainer();
Expand All @@ -75,10 +76,21 @@ export class ApplicationService {
throw new Error(`Applications cannot be registered after "setup"`);
}

const appBox: AppBox = {
app,
mount: this.mountContext!.createHandler(plugin, app.mount),
};
let appBox: AppBox;
if (isAppMountDeprecated(app.mount)) {
// eslint-disable-next-line no-console
console.warn(
`App [${app.id}] is using deprecated mount context. Use core.getStartServices() instead.`
);

appBox = {
app,
mount: this.mountContext!.createHandler(plugin, app.mount),
};
} else {
appBox = { app, mount: app.mount };
}

this.apps$.next(new Map([...this.apps$.value.entries(), [app.id, appBox]]));
},
registerLegacyApp: (app: LegacyApp) => {
Expand Down Expand Up @@ -146,15 +158,15 @@ export class ApplicationService {
}

// Filter only available apps and map to just the mount function.
const appMounters = new Map<string, AppMounter>(
const appMounts = new Map<string, AppMount>(
[...this.apps$.value]
.filter(([id]) => availableApps.has(id))
.map(([id, { mount }]) => [id, mount])
);

return (
<AppRouter
apps={appMounters}
apps={appMounts}
legacyApps={availableLegacyApps}
basePath={http.basePath}
currentAppId$={currentAppId$}
Expand All @@ -173,3 +185,8 @@ const appPath = (appId: string, { path }: { path?: string } = {}): string =>
path
? `/app/${appId}/${path.replace(/^\//, '')}` // Remove preceding slash from path if present
: `/app/${appId}`;

function isAppMountDeprecated(mount: (...args: any[]) => any): mount is AppMountDeprecated {
// Mount functions with two arguments are assumed to expect deprecated `context` object.
return mount.length === 2;
}
Loading

0 comments on commit 2d36989

Please sign in to comment.