Skip to content

Commit

Permalink
Deprecate application context
Browse files Browse the repository at this point in the history
  • Loading branch information
joshdover committed Dec 11, 2019
1 parent 8f16031 commit 73d22f1
Show file tree
Hide file tree
Showing 25 changed files with 263 additions and 78 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
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
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
37 changes: 28 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,23 @@ 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: isAppMountDeprecated(app.mount)
? this.mountContext!.createHandler(plugin, app.mount)
: 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 +160,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 +187,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;
}
2 changes: 2 additions & 0 deletions src/core/public/application/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export { Capabilities } from './capabilities';
export {
App,
AppBase,
AppMount,
AppMountDeprecated,
AppUnmount,
AppMountContext,
AppMountParameters,
Expand Down
4 changes: 2 additions & 2 deletions src/core/public/application/integration_tests/router.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { BehaviorSubject } from 'rxjs';

import { I18nProvider } from '@kbn/i18n/react';

import { AppMounter, LegacyApp, AppMountParameters } from '../types';
import { AppMount, LegacyApp, AppMountParameters } from '../types';
import { httpServiceMock } from '../../http/http_service.mock';
import { AppRouter, AppNotFound } from '../ui';

Expand All @@ -35,7 +35,7 @@ const createMountHandler = (htmlString: string) =>
});

describe('AppContainer', () => {
let apps: Map<string, jest.Mock<ReturnType<AppMounter>, Parameters<AppMounter>>>;
let apps: Map<string, jest.Mock<ReturnType<AppMount>, Parameters<AppMount>>>;
let legacyApps: Map<string, LegacyApp>;
let history: History;
let router: ReactWrapper;
Expand Down
Loading

0 comments on commit 73d22f1

Please sign in to comment.