Skip to content

Commit

Permalink
Expose plugin contracts to Legacy platform (#37218)
Browse files Browse the repository at this point in the history
* expose plugin contracts to Legacy platform

* move mapToObject to src/core/utils
  • Loading branch information
mshustov authored May 28, 2019
1 parent a684756 commit ab6f0a7
Show file tree
Hide file tree
Showing 16 changed files with 168 additions and 117 deletions.
13 changes: 9 additions & 4 deletions src/core/public/core_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { OverlayService } from './overlays';
import { PluginsService } from './plugins';
import { UiSettingsService } from './ui_settings';
import { ApplicationService } from './application';
import { mapToObject } from '../utils/';

interface Params {
rootDomElement: HTMLElement;
Expand Down Expand Up @@ -131,8 +132,8 @@ export class CoreSystem {
};

// Services that do not expose contracts at setup
await this.plugins.setup(core);
await this.legacyPlatform.setup({ core });
const plugins = await this.plugins.setup(core);
await this.legacyPlatform.setup({ core, plugins: mapToObject(plugins.contracts) });

return { fatalErrors: this.fatalErrorsSetup };
} catch (error) {
Expand Down Expand Up @@ -181,8 +182,12 @@ export class CoreSystem {
overlays,
};

await this.plugins.start(core);
await this.legacyPlatform.start({ core, targetDomElement: legacyPlatformTargetDomElement });
const plugins = await this.plugins.start(core);
await this.legacyPlatform.start({
core,
plugins: mapToObject(plugins.contracts),
targetDomElement: legacyPlatformTargetDomElement,
});
} catch (error) {
if (this.fatalErrorsSetup) {
this.fatalErrorsSetup.add(error);
Expand Down
2 changes: 2 additions & 0 deletions src/core/public/legacy/legacy_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ const defaultSetupDeps = {
uiSettings: uiSettingsSetup,
chrome: chromeSetup,
},
plugins: {},
};

const applicationStart = applicationServiceMock.createStartContract();
Expand All @@ -207,6 +208,7 @@ const defaultStartDeps = {
overlays: overlayStart,
},
targetDomElement: document.createElement('div'),
plugins: {},
};

afterEach(() => {
Expand Down
12 changes: 7 additions & 5 deletions src/core/public/legacy/legacy_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ export interface LegacyPlatformParams {

interface SetupDeps {
core: InternalCoreSetup;
plugins: Record<string, unknown>;
}

interface StartDeps {
core: InternalCoreStart;
plugins: Record<string, unknown>;
targetDomElement: HTMLElement;
}

Expand All @@ -52,7 +54,7 @@ export class LegacyPlatformService {

constructor(private readonly params: LegacyPlatformParams) {}

public setup({ core }: SetupDeps) {
public setup({ core, plugins }: SetupDeps) {
const {
application,
i18n,
Expand All @@ -65,7 +67,7 @@ export class LegacyPlatformService {
} = core;
// Inject parts of the new platform into parts of the legacy platform
// so that legacy APIs/modules can mimic their new platform counterparts
require('ui/new_platform').__newPlatformSetup__(core);
require('ui/new_platform').__newPlatformSetup__(core, plugins);
require('ui/metadata').__newPlatformSetup__(injectedMetadata.getLegacyMetadata());
require('ui/i18n').__newPlatformSetup__(i18n.Context);
require('ui/notify/fatal_error').__newPlatformSetup__(fatalErrors);
Expand Down Expand Up @@ -103,14 +105,14 @@ export class LegacyPlatformService {
this.params.requireLegacyFiles();
}

public start({ core, targetDomElement }: StartDeps) {
public start({ core, targetDomElement, plugins }: StartDeps) {
if (!this.bootstrapModule) {
throw new Error('Bootstrap module must be loaded before `start`');
}

this.targetDomElement = targetDomElement;

require('ui/new_platform').__newPlatformStart__(core);
require('ui/new_platform').__newPlatformStart__(core, plugins);
require('ui/capabilities').__newPlatformStart__(core.application.capabilities);

this.bootstrapModule.bootstrap(this.targetDomElement);
Expand All @@ -124,7 +126,7 @@ export class LegacyPlatformService {
const angularRoot = angular.element(this.targetDomElement);
const injector$ = angularRoot.injector();

// if we haven't gotten to the point of bootstraping
// if we haven't gotten to the point of bootstrapping
// angular, injector$ won't be defined
if (!injector$) {
return;
Expand Down
103 changes: 54 additions & 49 deletions src/core/server/legacy/legacy_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,20 @@ const MockLegacyPlatformProxy: jest.Mock<LegacyPlatformProxy> = LegacyPlatformPr
let env: Env;
let config$: BehaviorSubject<Config>;
let setupDeps: {
elasticsearch: ElasticsearchServiceSetup;
http: any;
plugins: PluginsServiceSetup;
core: {
elasticsearch: ElasticsearchServiceSetup;
http: any;
plugins: PluginsServiceSetup;
};
plugins: Record<string, unknown>;
};

let startDeps: {
http: HttpServiceStart;
plugins: PluginsServiceStart;
core: {
http: HttpServiceStart;
plugins: PluginsServiceStart;
};
plugins: Record<string, unknown>;
};

const logger = loggingServiceMock.create();
Expand All @@ -64,27 +70,31 @@ beforeEach(() => {
MockKbnServer.prototype.ready = jest.fn().mockReturnValue(Promise.resolve());

setupDeps = {
elasticsearch: { legacy: {} } as any,
http: {
options: { someOption: 'foo', someAnotherOption: 'bar' },
server: { listener: { addListener: jest.fn() }, route: jest.fn() },
},
plugins: {
contracts: new Map([['plugin-id', 'plugin-value']]),
uiPlugins: {
public: new Map([['plugin-id', {} as DiscoveredPlugin]]),
internal: new Map([['plugin-id', {} as DiscoveredPluginInternal]]),
core: {
elasticsearch: { legacy: {} } as any,
http: {
options: { someOption: 'foo', someAnotherOption: 'bar' },
server: { listener: { addListener: jest.fn() }, route: jest.fn() },
},
plugins: {
contracts: new Map([['plugin-id', 'plugin-value']]),
uiPlugins: {
public: new Map([['plugin-id', {} as DiscoveredPlugin]]),
internal: new Map([['plugin-id', {} as DiscoveredPluginInternal]]),
},
},
},
plugins: { 'plugin-id': 'plugin-value' },
};

startDeps = {
http: {
isListening: () => true,
},
plugins: {
contracts: new Map(),
core: {
http: {
isListening: () => true,
},
plugins: { contracts: new Map() },
},
plugins: {},
};

config$ = new BehaviorSubject<Config>(
Expand All @@ -108,7 +118,7 @@ describe('once LegacyService is set up with connection info', () => {
await legacyService.setup(setupDeps);
await legacyService.start(startDeps);

expect(setupDeps.http.server.route.mock.calls).toMatchSnapshot('proxy route options');
expect(setupDeps.core.http.server.route.mock.calls).toMatchSnapshot('proxy route options');
});

test('proxy route responds with `503` if `kbnServer` is not ready yet.', async () => {
Expand Down Expand Up @@ -137,7 +147,7 @@ describe('once LegacyService is set up with connection info', () => {
};
const mockRequest = { raw: { req: { a: 1 }, res: { b: 2 } } };

const [[{ handler }]] = setupDeps.http.server.route.mock.calls;
const [[{ handler }]] = setupDeps.core.http.server.route.mock.calls;
const response503 = await handler(mockRequest, mockResponseToolkit);

expect(response503).toBe(mockResponse);
Expand Down Expand Up @@ -308,7 +318,7 @@ describe('once LegacyService is set up with connection info', () => {
await legacyService.setup(setupDeps);
await legacyService.start(startDeps);

const [[{ handler }]] = setupDeps.http.server.route.mock.calls;
const [[{ handler }]] = setupDeps.core.http.server.route.mock.calls;
const response = await handler(mockRequest, mockResponseToolkit);

expect(response).toBe(mockResponseToolkit.abandon);
Expand All @@ -327,12 +337,13 @@ describe('once LegacyService is set up with connection info', () => {

describe('once LegacyService is set up without connection info', () => {
const disabledHttpStartDeps = {
http: {
isListening: () => false,
},
plugins: {
contracts: new Map(),
core: {
http: {
isListening: () => false,
},
plugins: { contracts: new Map() },
},
plugins: {},
};
let legacyService: LegacyService;
beforeEach(async () => {
Expand All @@ -343,7 +354,7 @@ describe('once LegacyService is set up without connection info', () => {
});

test('creates legacy kbnServer with `autoListen: false`.', () => {
expect(setupDeps.http.server.route).not.toHaveBeenCalled();
expect(setupDeps.core.http.server.route).not.toHaveBeenCalled();
expect(MockKbnServer).toHaveBeenCalledTimes(1);
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
Expand Down Expand Up @@ -390,18 +401,15 @@ describe('once LegacyService is set up in `devClusterMaster` mode', () => {
configService: configService as any,
});

await devClusterLegacyService.setup({
elasticsearch: setupDeps.elasticsearch,
plugins: { contracts: new Map(), uiPlugins: { public: new Map(), internal: new Map() } },
http: setupDeps.http,
});
await devClusterLegacyService.setup(setupDeps);
await devClusterLegacyService.start({
http: {
isListening: () => false,
},
plugins: {
contracts: new Map(),
core: {
http: {
isListening: () => false,
},
plugins: { contracts: new Map() },
},
plugins: {},
});

expect(MockClusterManager.create.mock.calls).toMatchSnapshot(
Expand All @@ -421,18 +429,15 @@ describe('once LegacyService is set up in `devClusterMaster` mode', () => {
configService: configService as any,
});

await devClusterLegacyService.setup({
elasticsearch: setupDeps.elasticsearch,
plugins: { contracts: new Map(), uiPlugins: { public: new Map(), internal: new Map() } },
http: setupDeps.http,
});
await devClusterLegacyService.setup(setupDeps);
await devClusterLegacyService.start({
http: {
isListening: () => false,
},
plugins: {
contracts: new Map(),
core: {
http: {
isListening: () => false,
},
plugins: { contracts: new Map() },
},
plugins: {},
});

expect(MockClusterManager.create).toBeCalledTimes(1);
Expand Down
28 changes: 17 additions & 11 deletions src/core/server/legacy/legacy_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,24 @@ function getLegacyRawConfig(config: Config) {
return rawConfig;
}

interface SetupDeps {
core: InternalCoreSetup;
plugins: Record<string, unknown>;
}

interface StartDeps {
core: InternalCoreStart;
plugins: Record<string, unknown>;
}

/** @internal */
export class LegacyService implements CoreService {
private readonly log: Logger;
private readonly devConfig$: Observable<DevConfig>;
private readonly httpConfig$: Observable<HttpConfig>;
private kbnServer?: LegacyKbnServer;
private configSubscription?: Subscription;
private setupDeps?: InternalCoreSetup;
private setupDeps?: SetupDeps;

constructor(private readonly coreContext: CoreContext) {
this.log = coreContext.logger.get('legacy-service');
Expand All @@ -66,10 +76,10 @@ export class LegacyService implements CoreService {
.atPath<HttpConfigType>('server')
.pipe(map(rawConfig => new HttpConfig(rawConfig, coreContext.env)));
}
public async setup(setupDeps: InternalCoreSetup) {
public async setup(setupDeps: SetupDeps) {
this.setupDeps = setupDeps;
}
public async start(startDeps: InternalCoreStart) {
public async start(startDeps: StartDeps) {
const { setupDeps } = this;
if (!setupDeps) {
throw new Error('Legacy service is not setup yet.');
Expand Down Expand Up @@ -135,11 +145,7 @@ export class LegacyService implements CoreService {
);
}

private async createKbnServer(
config: Config,
setupDeps: InternalCoreSetup,
startDeps: InternalCoreStart
) {
private async createKbnServer(config: Config, setupDeps: SetupDeps, startDeps: StartDeps) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const KbnServer = require('../../../legacy/server/kbn_server');
const kbnServer: LegacyKbnServer = new KbnServer(getLegacyRawConfig(config), {
Expand All @@ -148,10 +154,10 @@ export class LegacyService implements CoreService {
// bridge with the "legacy" Kibana. If server isn't run (e.g. if process is
// managed by ClusterManager or optimizer) then we won't have that info,
// so we can't start "legacy" server either.
serverOptions: startDeps.http.isListening()
serverOptions: startDeps.core.http.isListening()
? {
...setupDeps.http.options,
listener: this.setupProxyListener(setupDeps.http.server),
...setupDeps.core.http.options,
listener: this.setupProxyListener(setupDeps.core.http.server),
}
: { autoListen: false },
handledConfigPaths: await this.coreContext.configService.getUsedPaths(),
Expand Down
8 changes: 8 additions & 0 deletions src/core/server/plugins/plugins_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ const createServiceMock = () => {
start: jest.fn(),
stop: jest.fn(),
};
mocked.setup.mockResolvedValue({
contracts: new Map(),
uiPlugins: {
public: new Map(),
internal: new Map(),
},
});
mocked.start.mockResolvedValue({ contracts: new Map() });
return mocked;
};

Expand Down
Loading

0 comments on commit ab6f0a7

Please sign in to comment.