Skip to content

Commit

Permalink
Add plugin start contract to getStartServices return value (#61216)
Browse files Browse the repository at this point in the history
* add plugin own contract as third element of getStartServices result

* adapt plugins code

* update tests

* revert unknown to use void again

* update generated doc

* fix UT

* update mock to allow non-object `pluginStartContract`

* add @typeparam documentation
  • Loading branch information
pgayvallet authored Mar 26, 2020
1 parent 9d0d3f7 commit 468de51
Showing 47 changed files with 178 additions and 93 deletions.
Original file line number Diff line number Diff line change
@@ -9,5 +9,5 @@
<b>Signature:</b>

```typescript
getStartServices: StartServicesAccessor<TPluginsStart>;
getStartServices: StartServicesAccessor<TPluginsStart, TStart>;
```
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ Core services exposed to the `Plugin` setup lifecycle
<b>Signature:</b>

```typescript
export interface CoreSetup<TPluginsStart extends object = object>
export interface CoreSetup<TPluginsStart extends object = object, TStart = unknown>
```

## Properties
@@ -19,7 +19,7 @@ export interface CoreSetup<TPluginsStart extends object = object>
| [application](./kibana-plugin-core-public.coresetup.application.md) | <code>ApplicationSetup</code> | [ApplicationSetup](./kibana-plugin-core-public.applicationsetup.md) |
| [context](./kibana-plugin-core-public.coresetup.context.md) | <code>ContextSetup</code> | [ContextSetup](./kibana-plugin-core-public.contextsetup.md) |
| [fatalErrors](./kibana-plugin-core-public.coresetup.fatalerrors.md) | <code>FatalErrorsSetup</code> | [FatalErrorsSetup](./kibana-plugin-core-public.fatalerrorssetup.md) |
| [getStartServices](./kibana-plugin-core-public.coresetup.getstartservices.md) | <code>StartServicesAccessor&lt;TPluginsStart&gt;</code> | [StartServicesAccessor](./kibana-plugin-core-public.startservicesaccessor.md) |
| [getStartServices](./kibana-plugin-core-public.coresetup.getstartservices.md) | <code>StartServicesAccessor&lt;TPluginsStart, TStart&gt;</code> | [StartServicesAccessor](./kibana-plugin-core-public.startservicesaccessor.md) |
| [http](./kibana-plugin-core-public.coresetup.http.md) | <code>HttpSetup</code> | [HttpSetup](./kibana-plugin-core-public.httpsetup.md) |
| [injectedMetadata](./kibana-plugin-core-public.coresetup.injectedmetadata.md) | <code>{</code><br/><code> getInjectedVar: (name: string, defaultValue?: any) =&gt; unknown;</code><br/><code> }</code> | exposed temporarily until https://github.com/elastic/kibana/issues/41990 done use \*only\* to retrieve config values. There is no way to set injected values in the new platform. Use the legacy platform API instead. |
| [notifications](./kibana-plugin-core-public.coresetup.notifications.md) | <code>NotificationsSetup</code> | [NotificationsSetup](./kibana-plugin-core-public.notificationssetup.md) |
Original file line number Diff line number Diff line change
@@ -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<any>
export interface LegacyCoreSetup extends CoreSetup<any, any>
```
## Remarks
Original file line number Diff line number Diff line change
@@ -7,14 +7,14 @@
<b>Signature:</b>

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

## Parameters

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

<b>Returns:</b>
Original file line number Diff line number Diff line change
@@ -9,5 +9,5 @@ Allows plugins to get access to APIs available in start inside async handlers, s
<b>Signature:</b>

```typescript
export declare type StartServicesAccessor<TPluginsStart extends object = object> = () => Promise<[CoreStart, TPluginsStart]>;
export declare type StartServicesAccessor<TPluginsStart extends object = object, TStart = unknown> = () => Promise<[CoreStart, TPluginsStart, TStart]>;
```
Original file line number Diff line number Diff line change
@@ -9,5 +9,5 @@
<b>Signature:</b>

```typescript
getStartServices: StartServicesAccessor<TPluginsStart>;
getStartServices: StartServicesAccessor<TPluginsStart, TStart>;
```
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ Context passed to the plugins `setup` method.
<b>Signature:</b>

```typescript
export interface CoreSetup<TPluginsStart extends object = object>
export interface CoreSetup<TPluginsStart extends object = object, TStart = unknown>
```

## Properties
@@ -19,7 +19,7 @@ export interface CoreSetup<TPluginsStart extends object = object>
| [capabilities](./kibana-plugin-core-server.coresetup.capabilities.md) | <code>CapabilitiesSetup</code> | [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) |
| [context](./kibana-plugin-core-server.coresetup.context.md) | <code>ContextSetup</code> | [ContextSetup](./kibana-plugin-core-server.contextsetup.md) |
| [elasticsearch](./kibana-plugin-core-server.coresetup.elasticsearch.md) | <code>ElasticsearchServiceSetup</code> | [ElasticsearchServiceSetup](./kibana-plugin-core-server.elasticsearchservicesetup.md) |
| [getStartServices](./kibana-plugin-core-server.coresetup.getstartservices.md) | <code>StartServicesAccessor&lt;TPluginsStart&gt;</code> | [StartServicesAccessor](./kibana-plugin-core-server.startservicesaccessor.md) |
| [getStartServices](./kibana-plugin-core-server.coresetup.getstartservices.md) | <code>StartServicesAccessor&lt;TPluginsStart, TStart&gt;</code> | [StartServicesAccessor](./kibana-plugin-core-server.startservicesaccessor.md) |
| [http](./kibana-plugin-core-server.coresetup.http.md) | <code>HttpServiceSetup</code> | [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) |
| [metrics](./kibana-plugin-core-server.coresetup.metrics.md) | <code>MetricsServiceSetup</code> | [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) |
| [savedObjects](./kibana-plugin-core-server.coresetup.savedobjects.md) | <code>SavedObjectsServiceSetup</code> | [SavedObjectsServiceSetup](./kibana-plugin-core-server.savedobjectsservicesetup.md) |
Original file line number Diff line number Diff line change
@@ -9,5 +9,5 @@ Allows plugins to get access to APIs available in start inside async handlers. P
<b>Signature:</b>

```typescript
export declare type StartServicesAccessor<TPluginsStart extends object = object> = () => Promise<[CoreStart, TPluginsStart]>;
export declare type StartServicesAccessor<TPluginsStart extends object = object, TStart = unknown> = () => Promise<[CoreStart, TPluginsStart, TStart]>;
```
9 changes: 3 additions & 6 deletions examples/alerting_example/public/plugin.tsx
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@
* under the License.
*/

import { Plugin, CoreSetup, AppMountParameters, CoreStart } from 'kibana/public';
import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public';
import { PluginSetupContract as AlertingSetup } from '../../../x-pack/plugins/alerting/public';
import { ChartsPluginStart } from '../../../src/plugins/charts/public';
import { TriggersAndActionsUIPublicPluginSetup } from '../../../x-pack/plugins/triggers_actions_ui/public';
@@ -43,17 +43,14 @@ export interface AlertingExamplePublicStartDeps {

export class AlertingExamplePlugin implements Plugin<Setup, Start, AlertingExamplePublicSetupDeps> {
public setup(
core: CoreSetup<AlertingExamplePublicStartDeps>,
core: CoreSetup<AlertingExamplePublicStartDeps, Start>,
{ alerting, triggers_actions_ui }: AlertingExamplePublicSetupDeps
) {
core.application.register({
id: 'AlertingExample',
title: 'Alerting Example',
async mount(params: AppMountParameters) {
const [coreStart, depsStart]: [
CoreStart,
AlertingExamplePublicStartDeps
] = await core.getStartServices();
const [coreStart, depsStart] = await core.getStartServices();
const { renderApp } = await import('./application');
return renderApp(coreStart, depsStart, params);
},
5 changes: 4 additions & 1 deletion examples/bfetch_explorer/public/plugin.tsx
Original file line number Diff line number Diff line change
@@ -34,7 +34,10 @@ export interface BfetchExplorerStartPlugins {
}

export class BfetchExplorerPlugin implements Plugin {
public setup(core: CoreSetup<BfetchExplorerStartPlugins>, plugins: BfetchExplorerSetupPlugins) {
public setup(
core: CoreSetup<BfetchExplorerStartPlugins, void>,
plugins: BfetchExplorerSetupPlugins
) {
const double = plugins.bfetch.batchedFunction<{ num: number }, { num: number }>({
url: '/bfetch_explorer/double',
});
13 changes: 11 additions & 2 deletions examples/embeddable_examples/public/index.ts
Original file line number Diff line number Diff line change
@@ -26,9 +26,18 @@ export {
export { ListContainer, LIST_CONTAINER } from './list_container';
export { TODO_EMBEDDABLE } from './todo';

import { EmbeddableExamplesPlugin } from './plugin';
import {
EmbeddableExamplesPlugin,
EmbeddableExamplesSetupDependencies,
EmbeddableExamplesStartDependencies,
} from './plugin';

export { SearchableListContainer, SEARCHABLE_LIST_CONTAINER } from './searchable_list_container';
export { MULTI_TASK_TODO_EMBEDDABLE } from './multi_task_todo';

export const plugin: PluginInitializer<void, void> = () => new EmbeddableExamplesPlugin();
export const plugin: PluginInitializer<
void,
void,
EmbeddableExamplesSetupDependencies,
EmbeddableExamplesStartDependencies
> = () => new EmbeddableExamplesPlugin();
4 changes: 2 additions & 2 deletions examples/embeddable_examples/public/plugin.ts
Original file line number Diff line number Diff line change
@@ -28,11 +28,11 @@ import {
} from './searchable_list_container';
import { LIST_CONTAINER, ListContainerFactory } from './list_container';

interface EmbeddableExamplesSetupDependencies {
export interface EmbeddableExamplesSetupDependencies {
embeddable: EmbeddableSetup;
}

interface EmbeddableExamplesStartDependencies {
export interface EmbeddableExamplesStartDependencies {
embeddable: EmbeddableStart;
}

2 changes: 1 addition & 1 deletion examples/search_explorer/public/plugin.tsx
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public';
import { AppPluginStartDependencies } from './types';

export class SearchExplorerPlugin implements Plugin {
public setup(core: CoreSetup<AppPluginStartDependencies>) {
public setup(core: CoreSetup<AppPluginStartDependencies, void>) {
core.application.register({
id: 'searchExplorer',
title: 'Search Explorer',
18 changes: 12 additions & 6 deletions src/core/public/index.ts
Original file line number Diff line number Diff line change
@@ -177,13 +177,18 @@ export { MountPoint, UnmountCallback, PublicUiSettingsParams } from './types';
/**
* Core services exposed to the `Plugin` setup lifecycle
*
* @typeParam TPluginsStart - the type of the consuming plugin's start dependencies. Should be the same
* as the consuming {@link Plugin}'s `TPluginsStart` type. Used by `getStartServices`.
* @typeParam TStart - the type of the consuming plugin's start contract. Should be the same as the
* consuming {@link Plugin}'s `TStart` type. Used by `getStartServices`.
*
* @public
*
* @internalRemarks We document the properties with \@link tags to improve
* navigation in the generated docs until there's a fix for
* https://github.com/Microsoft/web-build-tools/issues/1237
*/
export interface CoreSetup<TPluginsStart extends object = object> {
export interface CoreSetup<TPluginsStart extends object = object, TStart = unknown> {
/** {@link ApplicationSetup} */
application: ApplicationSetup;
/**
@@ -209,7 +214,7 @@ export interface CoreSetup<TPluginsStart extends object = object> {
getInjectedVar: (name: string, defaultValue?: any) => unknown;
};
/** {@link StartServicesAccessor} */
getStartServices: StartServicesAccessor<TPluginsStart>;
getStartServices: StartServicesAccessor<TPluginsStart, TStart>;
}

/**
@@ -219,9 +224,10 @@ export interface CoreSetup<TPluginsStart extends object = object> {
*
* @public
*/
export type StartServicesAccessor<TPluginsStart extends object = object> = () => Promise<
[CoreStart, TPluginsStart]
>;
export type StartServicesAccessor<
TPluginsStart extends object = object,
TStart = unknown
> = () => Promise<[CoreStart, TPluginsStart, TStart]>;

/**
* Core services exposed to the `Plugin` start lifecycle
@@ -274,7 +280,7 @@ export interface CoreStart {
* @public
* @deprecated
*/
export interface LegacyCoreSetup extends CoreSetup<any> {
export interface LegacyCoreSetup extends CoreSetup<any, any> {
/** @deprecated */
injectedMetadata: InjectedMetadataSetup;
}
4 changes: 2 additions & 2 deletions src/core/public/legacy/legacy_service.ts
Original file line number Diff line number Diff line change
@@ -57,7 +57,7 @@ export class LegacyPlatformService {
public readonly legacyId = Symbol();
private bootstrapModule?: BootstrapModule;
private targetDomElement?: HTMLElement;
private readonly startDependencies$ = new Subject<[LegacyCoreStart, object]>();
private readonly startDependencies$ = new Subject<[LegacyCoreStart, object, {}]>();
private readonly startDependencies = this.startDependencies$.pipe(first()).toPromise();

constructor(private readonly params: LegacyPlatformParams) {}
@@ -129,7 +129,7 @@ export class LegacyPlatformService {
},
};

this.startDependencies$.next([legacyCore, plugins]);
this.startDependencies$.next([legacyCore, plugins, {}]);

// Inject parts of the new platform into parts of the legacy platform
// so that legacy APIs/modules can mimic their new platform counterparts
15 changes: 12 additions & 3 deletions src/core/public/mocks.ts
Original file line number Diff line number Diff line change
@@ -49,13 +49,22 @@ export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';
export { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock';
export { scopedHistoryMock } from './application/scoped_history.mock';

function createCoreSetupMock({ basePath = '' } = {}) {
function createCoreSetupMock({
basePath = '',
pluginStartDeps = {},
pluginStartContract,
}: {
basePath?: string;
pluginStartDeps?: object;
pluginStartContract?: any;
} = {}) {
const mock = {
application: applicationServiceMock.createSetupContract(),
context: contextServiceMock.createSetupContract(),
fatalErrors: fatalErrorsServiceMock.createSetupContract(),
getStartServices: jest.fn<Promise<[ReturnType<typeof createCoreStartMock>, object]>, []>(() =>
Promise.resolve([createCoreStartMock({ basePath }), {}])
getStartServices: jest.fn<Promise<[ReturnType<typeof createCoreStartMock>, object, any]>, []>(
() =>
Promise.resolve([createCoreStartMock({ basePath }), pluginStartDeps, pluginStartContract])
),
http: httpServiceMock.createSetupContract({ basePath }),
notifications: notificationServiceMock.createSetupContract(),
7 changes: 6 additions & 1 deletion src/core/public/plugins/plugin.test.ts
Original file line number Diff line number Diff line change
@@ -109,13 +109,18 @@ describe('PluginWrapper', () => {
test("`start` resolves `startDependencies` Promise after plugin's start", async () => {
expect.assertions(2);

const pluginStartContract = {
someApi: () => 'foo',
};

let startDependenciesResolved = false;
mockPluginLoader.mockResolvedValueOnce(() => ({
setup: jest.fn(),
start: async () => {
// Add small delay to ensure startDependencies is not resolved until after the plugin instance's start resolves.
await new Promise(resolve => setTimeout(resolve, 10));
expect(startDependenciesResolved).toBe(false);
return pluginStartContract;
},
}));
await plugin.load(addBasePath);
@@ -127,7 +132,7 @@ describe('PluginWrapper', () => {
// called.
const startDependenciesCheck = plugin.startDependencies.then(res => {
startDependenciesResolved = true;
expect(res).toEqual([context, deps]);
expect(res).toEqual([context, deps, pluginStartContract]);
});
await plugin.start(context, deps);
await startDependenciesCheck;
8 changes: 4 additions & 4 deletions src/core/public/plugins/plugin.ts
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ export interface Plugin<
TPluginsSetup extends object = object,
TPluginsStart extends object = object
> {
setup(core: CoreSetup<TPluginsStart>, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
setup(core: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
stop?(): void;
}
@@ -72,7 +72,7 @@ export class PluginWrapper<
private initializer?: PluginInitializer<TSetup, TStart, TPluginsSetup, TPluginsStart>;
private instance?: Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;

private readonly startDependencies$ = new Subject<[CoreStart, TPluginsStart]>();
private readonly startDependencies$ = new Subject<[CoreStart, TPluginsStart, TStart]>();
public readonly startDependencies = this.startDependencies$.pipe(first()).toPromise();

constructor(
@@ -105,7 +105,7 @@ export class PluginWrapper<
* @param plugins The dictionary where the key is the dependency name and the value
* is the contract returned by the dependency's `setup` function.
*/
public async setup(setupContext: CoreSetup<TPluginsStart>, plugins: TPluginsSetup) {
public async setup(setupContext: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup) {
this.instance = await this.createPluginInstance();

return await this.instance.setup(setupContext, plugins);
@@ -125,7 +125,7 @@ export class PluginWrapper<

const startContract = await this.instance.start(startContext, plugins);

this.startDependencies$.next([startContext, plugins]);
this.startDependencies$.next([startContext, plugins, startContract]);

return startContract;
}
2 changes: 1 addition & 1 deletion src/core/public/plugins/plugins_service.ts
Original file line number Diff line number Diff line change
@@ -53,7 +53,7 @@ export interface PluginsServiceStart {
*/
export class PluginsService implements CoreService<PluginsServiceSetup, PluginsServiceStart> {
/** Plugin wrappers in topological order. */
private readonly plugins = new Map<PluginName, PluginWrapper<unknown, Record<string, unknown>>>();
private readonly plugins = new Map<PluginName, PluginWrapper<unknown, unknown>>();
private readonly pluginDependencies = new Map<PluginName, PluginName[]>();

private readonly satupPlugins: PluginName[] = [];
10 changes: 5 additions & 5 deletions src/core/public/public.api.md
Original file line number Diff line number Diff line change
@@ -371,15 +371,15 @@ export interface CoreContext {
}

// @public
export interface CoreSetup<TPluginsStart extends object = object> {
export interface CoreSetup<TPluginsStart extends object = object, TStart = unknown> {
// (undocumented)
application: ApplicationSetup;
// @deprecated (undocumented)
context: ContextSetup;
// (undocumented)
fatalErrors: FatalErrorsSetup;
// (undocumented)
getStartServices: StartServicesAccessor<TPluginsStart>;
getStartServices: StartServicesAccessor<TPluginsStart, TStart>;
// (undocumented)
http: HttpSetup;
// @deprecated
@@ -807,7 +807,7 @@ export interface IUiSettingsClient {
}

// @public @deprecated
export interface LegacyCoreSetup extends CoreSetup<any> {
export interface LegacyCoreSetup extends CoreSetup<any, any> {
// Warning: (ae-forgotten-export) The symbol "InjectedMetadataSetup" needs to be exported by the entry point index.d.ts
//
// @deprecated (undocumented)
@@ -907,7 +907,7 @@ export interface PackageInfo {
// @public
export interface Plugin<TSetup = void, TStart = void, TPluginsSetup extends object = object, TPluginsStart extends object = object> {
// (undocumented)
setup(core: CoreSetup<TPluginsStart>, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
setup(core: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
// (undocumented)
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
// (undocumented)
@@ -1237,7 +1237,7 @@ export class SimpleSavedObject<T = unknown> {
}

// @public
export type StartServicesAccessor<TPluginsStart extends object = object> = () => Promise<[CoreStart, TPluginsStart]>;
export type StartServicesAccessor<TPluginsStart extends object = object, TStart = unknown> = () => Promise<[CoreStart, TPluginsStart, TStart]>;

// @public
export type StringValidation = StringValidationRegex | StringValidationRegexString;
Loading

0 comments on commit 468de51

Please sign in to comment.