mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Introduce start
lifecycle event to client (#35269)
This commit is contained in:
parent
9947045f69
commit
c2f4123b8c
104 changed files with 1267 additions and 651 deletions
|
@ -16,30 +16,30 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { Capabilities, CapabilitiesService, CapabilitiesSetup } from './capabilities_service';
|
||||
import { Capabilities, CapabilitiesService, CapabilitiesStart } from './capabilities_service';
|
||||
|
||||
const createSetupContractMock = () => {
|
||||
const setupContract: jest.Mocked<CapabilitiesSetup> = {
|
||||
const createStartContractMock = () => {
|
||||
const startContract: jest.Mocked<CapabilitiesStart> = {
|
||||
getCapabilities: jest.fn(),
|
||||
};
|
||||
setupContract.getCapabilities.mockReturnValue({
|
||||
startContract.getCapabilities.mockReturnValue({
|
||||
catalogue: {},
|
||||
management: {},
|
||||
navLinks: {},
|
||||
} as Capabilities);
|
||||
return setupContract;
|
||||
return startContract;
|
||||
};
|
||||
|
||||
type CapabilitiesServiceContract = PublicMethodsOf<CapabilitiesService>;
|
||||
const createMock = () => {
|
||||
const mocked: jest.Mocked<CapabilitiesServiceContract> = {
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
};
|
||||
mocked.setup.mockReturnValue(createSetupContractMock());
|
||||
mocked.start.mockReturnValue(createStartContractMock());
|
||||
return mocked;
|
||||
};
|
||||
|
||||
export const capabilitiesServiceMock = {
|
||||
create: createMock,
|
||||
createSetupContract: createSetupContractMock,
|
||||
createStartContract: createStartContractMock,
|
||||
};
|
||||
|
|
|
@ -32,7 +32,7 @@ describe('#start', () => {
|
|||
} as any,
|
||||
});
|
||||
const service = new CapabilitiesService();
|
||||
const startContract = service.setup({ injectedMetadata: injectedMetadata.setup() });
|
||||
const startContract = service.start({ injectedMetadata: injectedMetadata.start() });
|
||||
expect(startContract.getCapabilities()).toEqual({
|
||||
foo: 'bar',
|
||||
bar: 'baz',
|
||||
|
@ -51,7 +51,7 @@ describe('#start', () => {
|
|||
} as any,
|
||||
});
|
||||
const service = new CapabilitiesService();
|
||||
const startContract = service.setup({ injectedMetadata: injectedMetadata.setup() });
|
||||
const startContract = service.start({ injectedMetadata: injectedMetadata.start() });
|
||||
const capabilities = startContract.getCapabilities();
|
||||
|
||||
// @ts-ignore TypeScript knows this shouldn't be possible
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { InjectedMetadataSetup } from '../injected_metadata';
|
||||
import { InjectedMetadataStart } from '../injected_metadata';
|
||||
import { deepFreeze } from '../utils/deep_freeze';
|
||||
|
||||
interface StartDeps {
|
||||
injectedMetadata: InjectedMetadataSetup;
|
||||
injectedMetadata: InjectedMetadataStart;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,7 +50,7 @@ export interface Capabilities {
|
|||
* Capabilities Setup.
|
||||
* @public
|
||||
*/
|
||||
export interface CapabilitiesSetup {
|
||||
export interface CapabilitiesStart {
|
||||
/**
|
||||
* Gets the read-only capabilities.
|
||||
*/
|
||||
|
@ -63,10 +63,10 @@ export interface CapabilitiesSetup {
|
|||
* Service that is responsible for UI Capabilities.
|
||||
*/
|
||||
export class CapabilitiesService {
|
||||
public setup({ injectedMetadata }: StartDeps): CapabilitiesSetup {
|
||||
public start({ injectedMetadata }: StartDeps): CapabilitiesStart {
|
||||
return {
|
||||
getCapabilities: () =>
|
||||
deepFreeze<Capabilities>(injectedMetadata.getInjectedVar('uiCapabilities') as Capabilities),
|
||||
deepFreeze(injectedMetadata.getInjectedVar('uiCapabilities') as Capabilities),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { Capabilities, CapabilitiesService, CapabilitiesSetup } from './capabilities_service';
|
||||
export { Capabilities, CapabilitiesService, CapabilitiesStart } from './capabilities_service';
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import { basePathServiceMock } from './base_path/base_path_service.mock';
|
||||
import { capabilitiesServiceMock } from './capabilities/capabilities_service.mock';
|
||||
import { chromeServiceMock } from './chrome/chrome_service.mock';
|
||||
import { fatalErrorsServiceMock } from './fatal_errors/fatal_errors_service.mock';
|
||||
import { httpServiceMock } from './http/http_service.mock';
|
||||
|
@ -104,3 +105,11 @@ export const PluginsServiceConstructor = jest.fn().mockImplementation(() => Mock
|
|||
jest.doMock('./plugins', () => ({
|
||||
PluginsService: PluginsServiceConstructor,
|
||||
}));
|
||||
|
||||
export const MockCapabilitiesService = capabilitiesServiceMock.create();
|
||||
export const CapabilitiesServiceConstructor = jest
|
||||
.fn()
|
||||
.mockImplementation(() => MockCapabilitiesService);
|
||||
jest.doMock('./capabilities', () => ({
|
||||
CapabilitiesService: CapabilitiesServiceConstructor,
|
||||
}));
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
BasePathServiceConstructor,
|
||||
ChromeServiceConstructor,
|
||||
|
@ -42,6 +39,7 @@ import {
|
|||
NotificationServiceConstructor,
|
||||
OverlayServiceConstructor,
|
||||
UiSettingsServiceConstructor,
|
||||
MockCapabilitiesService,
|
||||
} from './core_system.test.mocks';
|
||||
|
||||
import { CoreSystem } from './core_system';
|
||||
|
@ -110,21 +108,11 @@ describe('constructor', () => {
|
|||
|
||||
expect(LegacyPlatformServiceConstructor).toHaveBeenCalledTimes(1);
|
||||
expect(LegacyPlatformServiceConstructor).toHaveBeenCalledWith({
|
||||
targetDomElement: expect.any(HTMLElement),
|
||||
requireLegacyFiles,
|
||||
useLegacyTestHarness,
|
||||
});
|
||||
});
|
||||
|
||||
it('passes a dom element to NotificationsService', () => {
|
||||
createCoreSystem();
|
||||
|
||||
expect(NotificationServiceConstructor).toHaveBeenCalledTimes(1);
|
||||
expect(NotificationServiceConstructor).toHaveBeenCalledWith({
|
||||
targetDomElement$: expect.any(Observable),
|
||||
});
|
||||
});
|
||||
|
||||
it('passes browserSupportsCsp to ChromeService', () => {
|
||||
createCoreSystem();
|
||||
|
||||
|
@ -157,7 +145,121 @@ describe('constructor', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#stop', () => {
|
||||
describe('#setup()', () => {
|
||||
function setupCore(rootDomElement = defaultCoreSystemParams.rootDomElement) {
|
||||
const core = createCoreSystem({
|
||||
...defaultCoreSystemParams,
|
||||
rootDomElement,
|
||||
});
|
||||
|
||||
return core.setup();
|
||||
}
|
||||
|
||||
it('calls injectedMetadata#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockInjectedMetadataService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls http#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockHttpService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls basePath#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockBasePathService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls uiSettings#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockUiSettingsService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls i18n#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockI18nService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls fatalErrors#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockFatalErrorsService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls notifications#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockNotificationsService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls chrome#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockChromeService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls plugin#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockPluginsService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#start()', () => {
|
||||
async function startCore(rootDomElement = defaultCoreSystemParams.rootDomElement) {
|
||||
const core = createCoreSystem({
|
||||
...defaultCoreSystemParams,
|
||||
rootDomElement,
|
||||
});
|
||||
|
||||
await core.setup();
|
||||
await core.start();
|
||||
}
|
||||
|
||||
it('clears the children of the rootDomElement and appends container for legacyPlatform and notifications', async () => {
|
||||
const root = document.createElement('div');
|
||||
root.innerHTML = '<p>foo bar</p>';
|
||||
await startCore(root);
|
||||
expect(root.innerHTML).toBe('<div></div><div></div><div></div>');
|
||||
});
|
||||
|
||||
it('calls capabilities#start()', async () => {
|
||||
await startCore();
|
||||
expect(MockCapabilitiesService.start).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls i18n#start()', async () => {
|
||||
await startCore();
|
||||
expect(MockI18nService.start).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls injectedMetadata#start()', async () => {
|
||||
await startCore();
|
||||
expect(MockInjectedMetadataService.start).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls notifications#start() with a dom element', async () => {
|
||||
await startCore();
|
||||
expect(MockNotificationsService.start).toHaveBeenCalledTimes(1);
|
||||
expect(MockNotificationsService.start).toHaveBeenCalledWith({
|
||||
i18n: expect.any(Object),
|
||||
targetDomElement: expect.any(HTMLElement),
|
||||
});
|
||||
});
|
||||
|
||||
it('calls plugins#start()', async () => {
|
||||
await startCore();
|
||||
expect(MockPluginsService.start).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls legacyPlatform#start()', async () => {
|
||||
await startCore();
|
||||
expect(MockLegacyPlatformService.start).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls overlays#start()', async () => {
|
||||
await startCore();
|
||||
expect(MockOverlayService.start).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#stop()', () => {
|
||||
it('calls legacyPlatform.stop()', () => {
|
||||
const coreSystem = createCoreSystem();
|
||||
|
||||
|
@ -212,126 +314,50 @@ describe('#stop', () => {
|
|||
});
|
||||
|
||||
await coreSystem.setup();
|
||||
await coreSystem.start();
|
||||
expect(rootDomElement.innerHTML).not.toBe('');
|
||||
await coreSystem.stop();
|
||||
expect(rootDomElement.innerHTML).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setup()', () => {
|
||||
function setupCore(rootDomElement = defaultCoreSystemParams.rootDomElement) {
|
||||
const core = createCoreSystem({
|
||||
...defaultCoreSystemParams,
|
||||
rootDomElement,
|
||||
});
|
||||
|
||||
return core.setup();
|
||||
}
|
||||
|
||||
it('clears the children of the rootDomElement and appends container for legacyPlatform and notifications', async () => {
|
||||
const root = document.createElement('div');
|
||||
root.innerHTML = '<p>foo bar</p>';
|
||||
await setupCore(root);
|
||||
expect(root.innerHTML).toBe('<div></div><div></div><div></div>');
|
||||
});
|
||||
|
||||
it('calls injectedMetadata#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockInjectedMetadataService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls http#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockHttpService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls basePath#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockBasePathService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls uiSettings#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockUiSettingsService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls i18n#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockI18nService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls fatalErrors#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockFatalErrorsService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls notifications#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockNotificationsService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls chrome#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockChromeService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls overlays#setup()', () => {
|
||||
setupCore();
|
||||
expect(MockOverlayService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls plugin#setup()', async () => {
|
||||
await setupCore();
|
||||
expect(MockPluginsService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('LegacyPlatform targetDomElement', () => {
|
||||
it('only mounts the element when set up, before setting up the legacyPlatformService', async () => {
|
||||
it('only mounts the element when start, after setting up the legacyPlatformService', async () => {
|
||||
const rootDomElement = document.createElement('div');
|
||||
const core = createCoreSystem({
|
||||
rootDomElement,
|
||||
});
|
||||
|
||||
let targetDomElementParentInSetup: HTMLElement;
|
||||
MockLegacyPlatformService.setup.mockImplementation(() => {
|
||||
targetDomElementParentInSetup = targetDomElement.parentElement;
|
||||
let targetDomElementParentInStart: HTMLElement | null;
|
||||
MockLegacyPlatformService.start.mockImplementation(async ({ targetDomElement }) => {
|
||||
targetDomElementParentInStart = targetDomElement.parentElement;
|
||||
});
|
||||
|
||||
// targetDomElement should not have a parent element when the LegacyPlatformService is constructed
|
||||
const [[{ targetDomElement }]] = LegacyPlatformServiceConstructor.mock.calls;
|
||||
expect(targetDomElement).toHaveProperty('parentElement', null);
|
||||
|
||||
// setting up the core system should mount the targetDomElement as a child of the rootDomElement
|
||||
await core.setup();
|
||||
expect(targetDomElementParentInSetup!).toBe(rootDomElement);
|
||||
await core.start();
|
||||
expect(targetDomElementParentInStart!).toBe(rootDomElement);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Notifications targetDomElement', () => {
|
||||
it('only mounts the element when set up, before setting up the notificationsService', async () => {
|
||||
it('only mounts the element when started, after setting up the notificationsService', async () => {
|
||||
const rootDomElement = document.createElement('div');
|
||||
const core = createCoreSystem({
|
||||
rootDomElement,
|
||||
});
|
||||
|
||||
const [[{ targetDomElement$ }]] = NotificationServiceConstructor.mock.calls;
|
||||
|
||||
let targetDomElementParentInSetup: HTMLElement | null;
|
||||
MockNotificationsService.setup.mockImplementation(
|
||||
(): any => {
|
||||
(targetDomElement$ as Observable<HTMLElement>).pipe(take(1)).subscribe({
|
||||
next: targetDomElement => {
|
||||
// The targetDomElement should already be a child once it's received by the NotificationsService
|
||||
expect(targetDomElement.parentElement).not.toBeNull();
|
||||
targetDomElementParentInSetup = targetDomElement.parentElement;
|
||||
},
|
||||
});
|
||||
let targetDomElementParentInStart: HTMLElement | null;
|
||||
MockNotificationsService.start.mockImplementation(
|
||||
({ targetDomElement }): any => {
|
||||
expect(targetDomElement.parentElement).not.toBeNull();
|
||||
targetDomElementParentInStart = targetDomElement.parentElement;
|
||||
}
|
||||
);
|
||||
|
||||
// setting up the core system should mount the targetDomElement as a child of the rootDomElement
|
||||
// setting up and starting the core system should mount the targetDomElement as a child of the rootDomElement
|
||||
await core.setup();
|
||||
expect(targetDomElementParentInSetup!).toBe(rootDomElement);
|
||||
await core.start();
|
||||
expect(targetDomElementParentInStart!).toBe(rootDomElement);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,9 +19,7 @@
|
|||
|
||||
import './core.css';
|
||||
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { CoreSetup } from '.';
|
||||
import { CoreSetup, CoreStart } from '.';
|
||||
import { BasePathService } from './base_path';
|
||||
import { CapabilitiesService } from './capabilities';
|
||||
import { ChromeService } from './chrome';
|
||||
|
@ -70,8 +68,6 @@ export class CoreSystem {
|
|||
private readonly plugins: PluginsService;
|
||||
|
||||
private readonly rootDomElement: HTMLElement;
|
||||
private readonly notificationsTargetDomElement$: Subject<HTMLDivElement>;
|
||||
private readonly legacyPlatformTargetDomElement: HTMLDivElement;
|
||||
private readonly overlayTargetDomElement: HTMLDivElement;
|
||||
|
||||
constructor(params: Params) {
|
||||
|
@ -101,10 +97,7 @@ export class CoreSystem {
|
|||
},
|
||||
});
|
||||
|
||||
this.notificationsTargetDomElement$ = new Subject();
|
||||
this.notifications = new NotificationsService({
|
||||
targetDomElement$: this.notificationsTargetDomElement$.asObservable(),
|
||||
});
|
||||
this.notifications = new NotificationsService();
|
||||
this.http = new HttpService();
|
||||
this.basePath = new BasePathService();
|
||||
this.uiSettings = new UiSettingsService();
|
||||
|
@ -115,9 +108,7 @@ export class CoreSystem {
|
|||
const core: CoreContext = {};
|
||||
this.plugins = new PluginsService(core);
|
||||
|
||||
this.legacyPlatformTargetDomElement = document.createElement('div');
|
||||
this.legacyPlatform = new LegacyPlatformService({
|
||||
targetDomElement: this.legacyPlatformTargetDomElement,
|
||||
requireLegacyFiles,
|
||||
useLegacyTestHarness,
|
||||
});
|
||||
|
@ -129,15 +120,13 @@ export class CoreSystem {
|
|||
const injectedMetadata = this.injectedMetadata.setup();
|
||||
const fatalErrors = this.fatalErrors.setup({ i18n });
|
||||
const http = this.http.setup({ fatalErrors });
|
||||
const overlays = this.overlay.setup({ i18n });
|
||||
const basePath = this.basePath.setup({ injectedMetadata });
|
||||
const capabilities = this.capabilities.setup({ injectedMetadata });
|
||||
const uiSettings = this.uiSettings.setup({
|
||||
http,
|
||||
injectedMetadata,
|
||||
basePath,
|
||||
});
|
||||
const notifications = this.notifications.setup({ i18n, uiSettings });
|
||||
const notifications = this.notifications.setup({ uiSettings });
|
||||
const chrome = this.chrome.setup({
|
||||
injectedMetadata,
|
||||
notifications,
|
||||
|
@ -149,31 +138,52 @@ export class CoreSystem {
|
|||
fatalErrors,
|
||||
http,
|
||||
i18n,
|
||||
capabilities,
|
||||
injectedMetadata,
|
||||
notifications,
|
||||
uiSettings,
|
||||
overlays,
|
||||
};
|
||||
|
||||
// Services that do not expose contracts at setup
|
||||
await this.plugins.setup(core);
|
||||
await this.legacyPlatform.setup({ core });
|
||||
|
||||
return { fatalErrors };
|
||||
} catch (error) {
|
||||
this.fatalErrors.add(error);
|
||||
}
|
||||
}
|
||||
|
||||
public async start() {
|
||||
try {
|
||||
// ensure the rootDomElement is empty
|
||||
this.rootDomElement.textContent = '';
|
||||
this.rootDomElement.classList.add('coreSystemRootDomElement');
|
||||
|
||||
const notificationsTargetDomElement = document.createElement('div');
|
||||
const legacyPlatformTargetDomElement = document.createElement('div');
|
||||
this.rootDomElement.appendChild(notificationsTargetDomElement);
|
||||
this.rootDomElement.appendChild(this.legacyPlatformTargetDomElement);
|
||||
this.rootDomElement.appendChild(legacyPlatformTargetDomElement);
|
||||
this.rootDomElement.appendChild(this.overlayTargetDomElement);
|
||||
|
||||
// Only provide the DOM element to notifications once it's attached to the page.
|
||||
// This prevents notifications from timing out before being displayed.
|
||||
this.notificationsTargetDomElement$.next(notificationsTargetDomElement);
|
||||
const injectedMetadata = this.injectedMetadata.start();
|
||||
const i18n = this.i18n.start();
|
||||
const capabilities = this.capabilities.start({ injectedMetadata });
|
||||
const notifications = this.notifications.start({
|
||||
i18n,
|
||||
targetDomElement: notificationsTargetDomElement,
|
||||
});
|
||||
const overlays = this.overlay.start({ i18n });
|
||||
|
||||
this.legacyPlatform.setup(core);
|
||||
const core: CoreStart = {
|
||||
capabilities,
|
||||
i18n,
|
||||
injectedMetadata,
|
||||
notifications,
|
||||
overlays,
|
||||
};
|
||||
|
||||
return { fatalErrors };
|
||||
await this.plugins.start(core);
|
||||
await this.legacyPlatform.start({ core, targetDomElement: legacyPlatformTargetDomElement });
|
||||
} catch (error) {
|
||||
this.fatalErrors.add(error);
|
||||
}
|
||||
|
|
|
@ -58,3 +58,62 @@ exports[`#setup() returns \`Context\` component 1`] = `
|
|||
</MockEuiContext>
|
||||
</MockI18nProvider>
|
||||
`;
|
||||
|
||||
exports[`#start() returns \`Context\` component 1`] = `
|
||||
<MockI18nProvider>
|
||||
<MockEuiContext
|
||||
i18n={
|
||||
Object {
|
||||
"mapping": Object {
|
||||
"euiBasicTable.selectAllRows": "Select all rows",
|
||||
"euiBasicTable.selectThisRow": "Select this row",
|
||||
"euiBasicTable.tableDescription": [Function],
|
||||
"euiBottomBar.screenReaderAnnouncement": "There is a new menu opening with page level controls at the end of the document.",
|
||||
"euiCodeBlock.copyButton": "Copy",
|
||||
"euiCodeEditor.startEditing": "Press Enter to start editing.",
|
||||
"euiCodeEditor.startInteracting": "Press Enter to start interacting with the code.",
|
||||
"euiCodeEditor.stopEditing": "When you're done, press Escape to stop editing.",
|
||||
"euiCodeEditor.stopInteracting": "When you're done, press Escape to stop interacting with the code.",
|
||||
"euiCollapsedItemActions.allActions": "All actions",
|
||||
"euiColorPicker.colorSelectionLabel": [Function],
|
||||
"euiColorPicker.transparentColor": "transparent",
|
||||
"euiComboBoxOptionsList.allOptionsSelected": "You've selected all available options",
|
||||
"euiComboBoxOptionsList.alreadyAdded": [Function],
|
||||
"euiComboBoxOptionsList.createCustomOption": [Function],
|
||||
"euiComboBoxOptionsList.loadingOptions": "Loading options",
|
||||
"euiComboBoxOptionsList.noAvailableOptions": "There aren't any options available",
|
||||
"euiComboBoxOptionsList.noMatchingOptions": [Function],
|
||||
"euiComboBoxPill.removeSelection": [Function],
|
||||
"euiForm.addressFormErrors": "Please address the errors in your form.",
|
||||
"euiFormControlLayoutClearButton.label": "Clear input",
|
||||
"euiHeaderAlert.dismiss": "Dismiss",
|
||||
"euiHeaderLinks.appNavigation": "App navigation",
|
||||
"euiHeaderLinks.openNavigationMenu": "Open navigation menu",
|
||||
"euiModal.closeModal": "Closes this modal window",
|
||||
"euiPagination.jumpToLastPage": [Function],
|
||||
"euiPagination.nextPage": "Next page",
|
||||
"euiPagination.pageOfTotal": [Function],
|
||||
"euiPagination.previousPage": "Previous page",
|
||||
"euiPopover.screenReaderAnnouncement": "You are in a popup. To exit this popup, hit Escape.",
|
||||
"euiStep.completeStep": "Step",
|
||||
"euiStep.incompleteStep": "Incomplete Step",
|
||||
"euiStepHorizontal.buttonTitle": [Function],
|
||||
"euiStepHorizontal.step": "Step",
|
||||
"euiStepNumber.hasErrors": "has errors",
|
||||
"euiStepNumber.hasWarnings": "has warnings",
|
||||
"euiStepNumber.isComplete": "complete",
|
||||
"euiSuperSelect.screenReaderAnnouncement": [Function],
|
||||
"euiSuperSelectControl.selectAnOption": [Function],
|
||||
"euiTablePagination.rowsPerPage": "Rows per page",
|
||||
"euiTableSortMobile.sorting": "Sorting",
|
||||
"euiToast.dismissToast": "Dismiss toast",
|
||||
"euiToast.newNotification": "A new notification appears",
|
||||
"euiToast.notification": "Notification",
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
content
|
||||
</MockEuiContext>
|
||||
</MockI18nProvider>
|
||||
`;
|
||||
|
|
|
@ -30,17 +30,23 @@ const createSetupContractMock = () => {
|
|||
return setupContract;
|
||||
};
|
||||
|
||||
// Start contract is identical to setup
|
||||
const createStartContractMock = createSetupContractMock;
|
||||
|
||||
type I18nServiceContract = PublicMethodsOf<I18nService>;
|
||||
const createMock = () => {
|
||||
const mocked: jest.Mocked<I18nServiceContract> = {
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
};
|
||||
mocked.setup.mockReturnValue(createSetupContractMock());
|
||||
mocked.start.mockReturnValue(createStartContractMock());
|
||||
return mocked;
|
||||
};
|
||||
|
||||
export const i18nServiceMock = {
|
||||
create: createMock,
|
||||
createSetupContract: createSetupContractMock,
|
||||
createStartContract: createStartContractMock,
|
||||
};
|
||||
|
|
|
@ -53,3 +53,13 @@ describe('#setup()', () => {
|
|||
expect(shallow(<i18n.Context>content</i18n.Context>)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#start()', () => {
|
||||
it('returns `Context` component', () => {
|
||||
const i18nService = new I18nService();
|
||||
|
||||
const i18n = i18nService.start();
|
||||
|
||||
expect(shallow(<i18n.Context>content</i18n.Context>)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -267,6 +267,10 @@ export class I18nService {
|
|||
return setup;
|
||||
}
|
||||
|
||||
public start() {
|
||||
return this.setup();
|
||||
}
|
||||
|
||||
public stop() {
|
||||
// nothing to do here currently
|
||||
}
|
||||
|
@ -285,3 +289,5 @@ export interface I18nSetup {
|
|||
*/
|
||||
Context: ({ children }: { children: React.ReactNode }) => JSX.Element;
|
||||
}
|
||||
|
||||
export type I18nStart = I18nSetup;
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { I18nService, I18nSetup } from './i18n_service';
|
||||
export { I18nService, I18nSetup, I18nStart } from './i18n_service';
|
||||
|
|
|
@ -18,14 +18,24 @@
|
|||
*/
|
||||
|
||||
import { BasePathSetup } from './base_path';
|
||||
import { Capabilities, CapabilitiesSetup } from './capabilities';
|
||||
import { Capabilities, CapabilitiesStart } from './capabilities';
|
||||
import { ChromeBrand, ChromeBreadcrumb, ChromeHelpExtension, ChromeSetup } from './chrome';
|
||||
import { FatalErrorsSetup } from './fatal_errors';
|
||||
import { HttpSetup } from './http';
|
||||
import { I18nSetup } from './i18n';
|
||||
import { InjectedMetadataParams, InjectedMetadataSetup } from './injected_metadata';
|
||||
import { NotificationsSetup, Toast, ToastInput, ToastsSetup } from './notifications';
|
||||
import { FlyoutRef, OverlaySetup } from './overlays';
|
||||
import { I18nSetup, I18nStart } from './i18n';
|
||||
import {
|
||||
InjectedMetadataParams,
|
||||
InjectedMetadataSetup,
|
||||
InjectedMetadataStart,
|
||||
} from './injected_metadata';
|
||||
import {
|
||||
NotificationsSetup,
|
||||
Toast,
|
||||
ToastInput,
|
||||
ToastsApi,
|
||||
NotificationsStart,
|
||||
} from './notifications';
|
||||
import { FlyoutRef, OverlayStart } from './overlays';
|
||||
import { Plugin, PluginInitializer, PluginInitializerContext, PluginSetupContext } from './plugins';
|
||||
import { UiSettingsClient, UiSettingsSetup, UiSettingsState } from './ui_settings';
|
||||
|
||||
|
@ -53,39 +63,51 @@ export interface CoreSetup {
|
|||
http: HttpSetup;
|
||||
/** {@link BasePathSetup} */
|
||||
basePath: BasePathSetup;
|
||||
/** {@link CapabilitiesSetup} */
|
||||
capabilities: CapabilitiesSetup;
|
||||
/** {@link UiSettingsSetup} */
|
||||
uiSettings: UiSettingsSetup;
|
||||
/** {@link ChromeSetup} */
|
||||
chrome: ChromeSetup;
|
||||
/** {@link OverlaySetup} */
|
||||
overlays: OverlaySetup;
|
||||
}
|
||||
|
||||
export interface CoreStart {
|
||||
/** {@link CapabilitiesStart} */
|
||||
capabilities: CapabilitiesStart;
|
||||
/** {@link I18nStart} */
|
||||
i18n: I18nStart;
|
||||
/** {@link InjectedMetadataStart} */
|
||||
injectedMetadata: InjectedMetadataStart;
|
||||
/** {@link NotificationsStart} */
|
||||
notifications: NotificationsStart;
|
||||
/** {@link OverlayStart} */
|
||||
overlays: OverlayStart;
|
||||
}
|
||||
|
||||
export {
|
||||
BasePathSetup,
|
||||
HttpSetup,
|
||||
FatalErrorsSetup,
|
||||
CapabilitiesSetup,
|
||||
Capabilities,
|
||||
CapabilitiesStart,
|
||||
ChromeSetup,
|
||||
ChromeBreadcrumb,
|
||||
ChromeBrand,
|
||||
ChromeHelpExtension,
|
||||
I18nSetup,
|
||||
I18nStart,
|
||||
InjectedMetadataSetup,
|
||||
InjectedMetadataStart,
|
||||
InjectedMetadataParams,
|
||||
Plugin,
|
||||
PluginInitializer,
|
||||
PluginInitializerContext,
|
||||
PluginSetupContext,
|
||||
NotificationsSetup,
|
||||
OverlaySetup,
|
||||
NotificationsStart,
|
||||
OverlayStart,
|
||||
FlyoutRef,
|
||||
Toast,
|
||||
ToastInput,
|
||||
ToastsSetup,
|
||||
ToastsApi,
|
||||
UiSettingsClient,
|
||||
UiSettingsState,
|
||||
UiSettingsSetup,
|
||||
|
|
|
@ -21,4 +21,5 @@ export {
|
|||
InjectedMetadataService,
|
||||
InjectedMetadataParams,
|
||||
InjectedMetadataSetup,
|
||||
InjectedMetadataStart,
|
||||
} from './injected_metadata_service';
|
||||
|
|
|
@ -40,18 +40,18 @@ const createSetupContractMock = () => {
|
|||
return setupContract;
|
||||
};
|
||||
|
||||
const createStartContractMock = createSetupContractMock;
|
||||
|
||||
type InjectedMetadataServiceContract = PublicMethodsOf<InjectedMetadataService>;
|
||||
const createMock = () => {
|
||||
const mocked: jest.Mocked<InjectedMetadataServiceContract> = {
|
||||
setup: jest.fn(),
|
||||
getKibanaVersion: jest.fn(),
|
||||
getKibanaBuildNumber: jest.fn(),
|
||||
};
|
||||
mocked.setup.mockReturnValue(createSetupContractMock());
|
||||
return mocked;
|
||||
};
|
||||
const createMock = (): jest.Mocked<InjectedMetadataServiceContract> => ({
|
||||
setup: jest.fn().mockReturnValue(createSetupContractMock()),
|
||||
start: jest.fn().mockReturnValue(createStartContractMock()),
|
||||
getKibanaVersion: jest.fn(),
|
||||
getKibanaBuildNumber: jest.fn(),
|
||||
});
|
||||
|
||||
export const injectedMetadataServiceMock = {
|
||||
create: createMock,
|
||||
createSetupContract: createSetupContractMock,
|
||||
createStartContract: createStartContractMock,
|
||||
};
|
||||
|
|
|
@ -105,6 +105,10 @@ export class InjectedMetadataService {
|
|||
};
|
||||
}
|
||||
|
||||
public start(): InjectedMetadataStart {
|
||||
return this.setup();
|
||||
}
|
||||
|
||||
public getKibanaVersion() {
|
||||
return this.state.version;
|
||||
}
|
||||
|
@ -154,3 +158,6 @@ export interface InjectedMetadataSetup {
|
|||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export type InjectedMetadataStart = InjectedMetadataSetup;
|
||||
|
|
|
@ -6,7 +6,6 @@ Array [
|
|||
"ui/i18n",
|
||||
"ui/notify/fatal_error",
|
||||
"ui/notify/toasts",
|
||||
"ui/capabilities",
|
||||
"ui/chrome/api/loading_count",
|
||||
"ui/chrome/api/base_path",
|
||||
"ui/chrome/api/ui_settings",
|
||||
|
@ -27,7 +26,6 @@ Array [
|
|||
"ui/i18n",
|
||||
"ui/notify/fatal_error",
|
||||
"ui/notify/toasts",
|
||||
"ui/capabilities",
|
||||
"ui/chrome/api/loading_count",
|
||||
"ui/chrome/api/base_path",
|
||||
"ui/chrome/api/ui_settings",
|
||||
|
@ -42,6 +40,48 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`#start() load order useLegacyTestHarness = false loads ui/modules before ui/chrome, and both before legacy files 1`] = `
|
||||
Array [
|
||||
"ui/metadata",
|
||||
"ui/i18n",
|
||||
"ui/notify/fatal_error",
|
||||
"ui/notify/toasts",
|
||||
"ui/chrome/api/loading_count",
|
||||
"ui/chrome/api/base_path",
|
||||
"ui/chrome/api/ui_settings",
|
||||
"ui/chrome/api/injected_vars",
|
||||
"ui/chrome/api/controls",
|
||||
"ui/chrome/api/help_extension",
|
||||
"ui/chrome/api/theme",
|
||||
"ui/chrome/api/breadcrumbs",
|
||||
"ui/chrome/services/global_nav_state",
|
||||
"ui/chrome",
|
||||
"legacy files",
|
||||
"ui/capabilities",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`#start() load order useLegacyTestHarness = true loads ui/modules before ui/test_harness, and both before legacy files 1`] = `
|
||||
Array [
|
||||
"ui/metadata",
|
||||
"ui/i18n",
|
||||
"ui/notify/fatal_error",
|
||||
"ui/notify/toasts",
|
||||
"ui/chrome/api/loading_count",
|
||||
"ui/chrome/api/base_path",
|
||||
"ui/chrome/api/ui_settings",
|
||||
"ui/chrome/api/injected_vars",
|
||||
"ui/chrome/api/controls",
|
||||
"ui/chrome/api/help_extension",
|
||||
"ui/chrome/api/theme",
|
||||
"ui/chrome/api/breadcrumbs",
|
||||
"ui/chrome/services/global_nav_state",
|
||||
"ui/test_harness",
|
||||
"legacy files",
|
||||
"ui/capabilities",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`#stop() destroys the angular scope and empties the targetDomElement if angular is bootstrapped to targetDomElement 1`] = `
|
||||
<div
|
||||
class="ng-scope"
|
||||
|
|
|
@ -22,6 +22,7 @@ type LegacyPlatformServiceContract = PublicMethodsOf<LegacyPlatformService>;
|
|||
const createMock = () => {
|
||||
const mocked: jest.Mocked<LegacyPlatformServiceContract> = {
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
};
|
||||
return mocked;
|
||||
|
|
|
@ -25,7 +25,7 @@ const mockUiMetadataInit = jest.fn();
|
|||
jest.mock('ui/metadata', () => {
|
||||
mockLoadOrder.push('ui/metadata');
|
||||
return {
|
||||
__newPlatformInit__: mockUiMetadataInit,
|
||||
__newPlatformSetup__: mockUiMetadataInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -49,7 +49,7 @@ const mockI18nContextInit = jest.fn();
|
|||
jest.mock('ui/i18n', () => {
|
||||
mockLoadOrder.push('ui/i18n');
|
||||
return {
|
||||
__newPlatformInit__: mockI18nContextInit,
|
||||
__newPlatformSetup__: mockI18nContextInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -57,7 +57,7 @@ const mockUICapabilitiesInit = jest.fn();
|
|||
jest.mock('ui/capabilities', () => {
|
||||
mockLoadOrder.push('ui/capabilities');
|
||||
return {
|
||||
__newPlatformInit__: mockUICapabilitiesInit,
|
||||
__newPlatformStart__: mockUICapabilitiesInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -65,7 +65,7 @@ const mockFatalErrorInit = jest.fn();
|
|||
jest.mock('ui/notify/fatal_error', () => {
|
||||
mockLoadOrder.push('ui/notify/fatal_error');
|
||||
return {
|
||||
__newPlatformInit__: mockFatalErrorInit,
|
||||
__newPlatformSetup__: mockFatalErrorInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -73,7 +73,7 @@ const mockNotifyToastsInit = jest.fn();
|
|||
jest.mock('ui/notify/toasts', () => {
|
||||
mockLoadOrder.push('ui/notify/toasts');
|
||||
return {
|
||||
__newPlatformInit__: mockNotifyToastsInit,
|
||||
__newPlatformSetup__: mockNotifyToastsInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -81,7 +81,7 @@ const mockHttpInit = jest.fn();
|
|||
jest.mock('ui/chrome/api/loading_count', () => {
|
||||
mockLoadOrder.push('ui/chrome/api/loading_count');
|
||||
return {
|
||||
__newPlatformInit__: mockHttpInit,
|
||||
__newPlatformSetup__: mockHttpInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -89,7 +89,7 @@ const mockBasePathInit = jest.fn();
|
|||
jest.mock('ui/chrome/api/base_path', () => {
|
||||
mockLoadOrder.push('ui/chrome/api/base_path');
|
||||
return {
|
||||
__newPlatformInit__: mockBasePathInit,
|
||||
__newPlatformSetup__: mockBasePathInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -97,7 +97,7 @@ const mockUiSettingsInit = jest.fn();
|
|||
jest.mock('ui/chrome/api/ui_settings', () => {
|
||||
mockLoadOrder.push('ui/chrome/api/ui_settings');
|
||||
return {
|
||||
__newPlatformInit__: mockUiSettingsInit,
|
||||
__newPlatformSetup__: mockUiSettingsInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -105,7 +105,7 @@ const mockInjectedVarsInit = jest.fn();
|
|||
jest.mock('ui/chrome/api/injected_vars', () => {
|
||||
mockLoadOrder.push('ui/chrome/api/injected_vars');
|
||||
return {
|
||||
__newPlatformInit__: mockInjectedVarsInit,
|
||||
__newPlatformSetup__: mockInjectedVarsInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -113,7 +113,7 @@ const mockChromeControlsInit = jest.fn();
|
|||
jest.mock('ui/chrome/api/controls', () => {
|
||||
mockLoadOrder.push('ui/chrome/api/controls');
|
||||
return {
|
||||
__newPlatformInit__: mockChromeControlsInit,
|
||||
__newPlatformSetup__: mockChromeControlsInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -121,7 +121,7 @@ const mockChromeHelpExtensionInit = jest.fn();
|
|||
jest.mock('ui/chrome/api/help_extension', () => {
|
||||
mockLoadOrder.push('ui/chrome/api/help_extension');
|
||||
return {
|
||||
__newPlatformInit__: mockChromeHelpExtensionInit,
|
||||
__newPlatformSetup__: mockChromeHelpExtensionInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -129,7 +129,7 @@ const mockChromeThemeInit = jest.fn();
|
|||
jest.mock('ui/chrome/api/theme', () => {
|
||||
mockLoadOrder.push('ui/chrome/api/theme');
|
||||
return {
|
||||
__newPlatformInit__: mockChromeThemeInit,
|
||||
__newPlatformSetup__: mockChromeThemeInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -137,7 +137,7 @@ const mockChromeBreadcrumbsInit = jest.fn();
|
|||
jest.mock('ui/chrome/api/breadcrumbs', () => {
|
||||
mockLoadOrder.push('ui/chrome/api/breadcrumbs');
|
||||
return {
|
||||
__newPlatformInit__: mockChromeBreadcrumbsInit,
|
||||
__newPlatformSetup__: mockChromeBreadcrumbsInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -145,7 +145,7 @@ const mockGlobalNavStateInit = jest.fn();
|
|||
jest.mock('ui/chrome/services/global_nav_state', () => {
|
||||
mockLoadOrder.push('ui/chrome/services/global_nav_state');
|
||||
return {
|
||||
__newPlatformInit__: mockGlobalNavStateInit,
|
||||
__newPlatformSetup__: mockGlobalNavStateInit,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -168,28 +168,42 @@ const httpSetup = httpServiceMock.createSetupContract();
|
|||
const i18nSetup = i18nServiceMock.createSetupContract();
|
||||
const injectedMetadataSetup = injectedMetadataServiceMock.createSetupContract();
|
||||
const notificationsSetup = notificationServiceMock.createSetupContract();
|
||||
const capabilitiesSetup = capabilitiesServiceMock.createSetupContract();
|
||||
const uiSettingsSetup = uiSettingsServiceMock.createSetupContract();
|
||||
const overlaySetup = overlayServiceMock.createSetupContract();
|
||||
|
||||
const defaultParams = {
|
||||
targetDomElement: document.createElement('div'),
|
||||
requireLegacyFiles: jest.fn(() => {
|
||||
mockLoadOrder.push('legacy files');
|
||||
}),
|
||||
};
|
||||
|
||||
const defaultSetupDeps = {
|
||||
i18n: i18nSetup,
|
||||
fatalErrors: fatalErrorsSetup,
|
||||
injectedMetadata: injectedMetadataSetup,
|
||||
notifications: notificationsSetup,
|
||||
http: httpSetup,
|
||||
basePath: basePathSetup,
|
||||
capabilities: capabilitiesSetup,
|
||||
uiSettings: uiSettingsSetup,
|
||||
chrome: chromeSetup,
|
||||
overlays: overlaySetup,
|
||||
core: {
|
||||
i18n: i18nSetup,
|
||||
fatalErrors: fatalErrorsSetup,
|
||||
injectedMetadata: injectedMetadataSetup,
|
||||
notifications: notificationsSetup,
|
||||
http: httpSetup,
|
||||
basePath: basePathSetup,
|
||||
uiSettings: uiSettingsSetup,
|
||||
chrome: chromeSetup,
|
||||
},
|
||||
};
|
||||
|
||||
const capabilitiesStart = capabilitiesServiceMock.createStartContract();
|
||||
const i18nStart = i18nServiceMock.createStartContract();
|
||||
const injectedMetadataStart = injectedMetadataServiceMock.createStartContract();
|
||||
const notificationsStart = notificationServiceMock.createStartContract();
|
||||
const overlayStart = overlayServiceMock.createStartContract();
|
||||
|
||||
const defaultStartDeps = {
|
||||
core: {
|
||||
capabilities: capabilitiesStart,
|
||||
i18n: i18nStart,
|
||||
injectedMetadata: injectedMetadataStart,
|
||||
notifications: notificationsStart,
|
||||
overlays: overlayStart,
|
||||
},
|
||||
targetDomElement: document.createElement('div'),
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -226,17 +240,6 @@ describe('#setup()', () => {
|
|||
expect(mockI18nContextInit).toHaveBeenCalledWith(i18nSetup.Context);
|
||||
});
|
||||
|
||||
it('passes uiCapabilities to ui/capabilities', () => {
|
||||
const legacyPlatform = new LegacyPlatformService({
|
||||
...defaultParams,
|
||||
});
|
||||
|
||||
legacyPlatform.setup(defaultSetupDeps);
|
||||
|
||||
expect(mockUICapabilitiesInit).toHaveBeenCalledTimes(1);
|
||||
expect(mockUICapabilitiesInit).toHaveBeenCalledWith(capabilitiesSetup);
|
||||
});
|
||||
|
||||
it('passes fatalErrors service to ui/notify/fatal_errors', () => {
|
||||
const legacyPlatform = new LegacyPlatformService({
|
||||
...defaultParams,
|
||||
|
@ -357,35 +360,6 @@ describe('#setup()', () => {
|
|||
expect(mockGlobalNavStateInit).toHaveBeenCalledTimes(1);
|
||||
expect(mockGlobalNavStateInit).toHaveBeenCalledWith(chromeSetup);
|
||||
});
|
||||
|
||||
describe('useLegacyTestHarness = false', () => {
|
||||
it('passes the targetDomElement to ui/chrome', () => {
|
||||
const legacyPlatform = new LegacyPlatformService({
|
||||
...defaultParams,
|
||||
});
|
||||
|
||||
legacyPlatform.setup(defaultSetupDeps);
|
||||
|
||||
expect(mockUiTestHarnessBootstrap).not.toHaveBeenCalled();
|
||||
expect(mockUiChromeBootstrap).toHaveBeenCalledTimes(1);
|
||||
expect(mockUiChromeBootstrap).toHaveBeenCalledWith(defaultParams.targetDomElement);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useLegacyTestHarness = true', () => {
|
||||
it('passes the targetDomElement to ui/test_harness', () => {
|
||||
const legacyPlatform = new LegacyPlatformService({
|
||||
...defaultParams,
|
||||
useLegacyTestHarness: true,
|
||||
});
|
||||
|
||||
legacyPlatform.setup(defaultSetupDeps);
|
||||
|
||||
expect(mockUiChromeBootstrap).not.toHaveBeenCalled();
|
||||
expect(mockUiTestHarnessBootstrap).toHaveBeenCalledTimes(1);
|
||||
expect(mockUiTestHarnessBootstrap).toHaveBeenCalledWith(defaultParams.targetDomElement);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('load order', () => {
|
||||
|
@ -420,6 +394,84 @@ describe('#setup()', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#start()', () => {
|
||||
it('passes uiCapabilities to ui/capabilities', () => {
|
||||
const legacyPlatform = new LegacyPlatformService({
|
||||
...defaultParams,
|
||||
});
|
||||
|
||||
legacyPlatform.setup(defaultSetupDeps);
|
||||
legacyPlatform.start(defaultStartDeps);
|
||||
|
||||
expect(mockUICapabilitiesInit).toHaveBeenCalledTimes(1);
|
||||
expect(mockUICapabilitiesInit).toHaveBeenCalledWith(capabilitiesStart);
|
||||
});
|
||||
|
||||
describe('useLegacyTestHarness = false', () => {
|
||||
it('passes the targetDomElement to ui/chrome', () => {
|
||||
const legacyPlatform = new LegacyPlatformService({
|
||||
...defaultParams,
|
||||
});
|
||||
|
||||
legacyPlatform.setup(defaultSetupDeps);
|
||||
legacyPlatform.start(defaultStartDeps);
|
||||
|
||||
expect(mockUiTestHarnessBootstrap).not.toHaveBeenCalled();
|
||||
expect(mockUiChromeBootstrap).toHaveBeenCalledTimes(1);
|
||||
expect(mockUiChromeBootstrap).toHaveBeenCalledWith(defaultStartDeps.targetDomElement);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useLegacyTestHarness = true', () => {
|
||||
it('passes the targetDomElement to ui/test_harness', () => {
|
||||
const legacyPlatform = new LegacyPlatformService({
|
||||
...defaultParams,
|
||||
useLegacyTestHarness: true,
|
||||
});
|
||||
|
||||
legacyPlatform.setup(defaultSetupDeps);
|
||||
legacyPlatform.start(defaultStartDeps);
|
||||
|
||||
expect(mockUiChromeBootstrap).not.toHaveBeenCalled();
|
||||
expect(mockUiTestHarnessBootstrap).toHaveBeenCalledTimes(1);
|
||||
expect(mockUiTestHarnessBootstrap).toHaveBeenCalledWith(defaultStartDeps.targetDomElement);
|
||||
});
|
||||
});
|
||||
|
||||
describe('load order', () => {
|
||||
describe('useLegacyTestHarness = false', () => {
|
||||
it('loads ui/modules before ui/chrome, and both before legacy files', () => {
|
||||
const legacyPlatform = new LegacyPlatformService({
|
||||
...defaultParams,
|
||||
});
|
||||
|
||||
expect(mockLoadOrder).toEqual([]);
|
||||
|
||||
legacyPlatform.setup(defaultSetupDeps);
|
||||
legacyPlatform.start(defaultStartDeps);
|
||||
|
||||
expect(mockLoadOrder).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useLegacyTestHarness = true', () => {
|
||||
it('loads ui/modules before ui/test_harness, and both before legacy files', () => {
|
||||
const legacyPlatform = new LegacyPlatformService({
|
||||
...defaultParams,
|
||||
useLegacyTestHarness: true,
|
||||
});
|
||||
|
||||
expect(mockLoadOrder).toEqual([]);
|
||||
|
||||
legacyPlatform.setup(defaultSetupDeps);
|
||||
legacyPlatform.start(defaultStartDeps);
|
||||
|
||||
expect(mockLoadOrder).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#stop()', () => {
|
||||
it('does nothing if angular was not bootstrapped to targetDomElement', () => {
|
||||
const targetDomElement = document.createElement('div');
|
||||
|
@ -429,20 +481,18 @@ describe('#stop()', () => {
|
|||
|
||||
const legacyPlatform = new LegacyPlatformService({
|
||||
...defaultParams,
|
||||
targetDomElement,
|
||||
});
|
||||
|
||||
legacyPlatform.stop();
|
||||
expect(targetDomElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('destroys the angular scope and empties the targetDomElement if angular is bootstrapped to targetDomElement', () => {
|
||||
it('destroys the angular scope and empties the targetDomElement if angular is bootstrapped to targetDomElement', async () => {
|
||||
const targetDomElement = document.createElement('div');
|
||||
const scopeDestroySpy = jest.fn();
|
||||
|
||||
const legacyPlatform = new LegacyPlatformService({
|
||||
...defaultParams,
|
||||
targetDomElement,
|
||||
});
|
||||
|
||||
// simulate bootstrapping with a module "foo"
|
||||
|
@ -459,6 +509,8 @@ describe('#stop()', () => {
|
|||
|
||||
angular.bootstrap(targetDomElement, ['foo']);
|
||||
|
||||
await legacyPlatform.setup(defaultSetupDeps);
|
||||
legacyPlatform.start({ ...defaultStartDeps, targetDomElement });
|
||||
legacyPlatform.stop();
|
||||
|
||||
expect(targetDomElement).toMatchSnapshot();
|
||||
|
|
|
@ -18,14 +18,27 @@
|
|||
*/
|
||||
|
||||
import angular from 'angular';
|
||||
import { CoreSetup } from '../';
|
||||
import { CoreSetup, CoreStart } from '../';
|
||||
|
||||
/** @internal */
|
||||
export interface LegacyPlatformParams {
|
||||
targetDomElement: HTMLElement;
|
||||
requireLegacyFiles: () => void;
|
||||
useLegacyTestHarness?: boolean;
|
||||
}
|
||||
|
||||
interface SetupDeps {
|
||||
core: CoreSetup;
|
||||
}
|
||||
|
||||
interface StartDeps {
|
||||
core: CoreStart;
|
||||
targetDomElement: HTMLElement;
|
||||
}
|
||||
|
||||
interface BootstrapModule {
|
||||
bootstrap: (targetDomElement: HTMLElement) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The LegacyPlatformService is responsible for initializing
|
||||
* the legacy platform by injecting parts of the new platform
|
||||
|
@ -34,9 +47,12 @@ export interface LegacyPlatformParams {
|
|||
* setup either the app or browser tests.
|
||||
*/
|
||||
export class LegacyPlatformService {
|
||||
private bootstrapModule?: BootstrapModule;
|
||||
private targetDomElement?: HTMLElement;
|
||||
|
||||
constructor(private readonly params: LegacyPlatformParams) {}
|
||||
|
||||
public setup(core: CoreSetup) {
|
||||
public async setup({ core }: SetupDeps) {
|
||||
const {
|
||||
i18n,
|
||||
injectedMetadata,
|
||||
|
@ -44,40 +60,53 @@ export class LegacyPlatformService {
|
|||
notifications,
|
||||
http,
|
||||
basePath,
|
||||
capabilities,
|
||||
uiSettings,
|
||||
chrome,
|
||||
} = 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').__newPlatformInit__(core);
|
||||
require('ui/metadata').__newPlatformInit__(injectedMetadata.getLegacyMetadata());
|
||||
require('ui/i18n').__newPlatformInit__(i18n.Context);
|
||||
require('ui/notify/fatal_error').__newPlatformInit__(fatalErrors);
|
||||
require('ui/notify/toasts').__newPlatformInit__(notifications.toasts);
|
||||
require('ui/capabilities').__newPlatformInit__(capabilities);
|
||||
require('ui/chrome/api/loading_count').__newPlatformInit__(http);
|
||||
require('ui/chrome/api/base_path').__newPlatformInit__(basePath);
|
||||
require('ui/chrome/api/ui_settings').__newPlatformInit__(uiSettings);
|
||||
require('ui/chrome/api/injected_vars').__newPlatformInit__(injectedMetadata);
|
||||
require('ui/chrome/api/controls').__newPlatformInit__(chrome);
|
||||
require('ui/chrome/api/help_extension').__newPlatformInit__(chrome);
|
||||
require('ui/chrome/api/theme').__newPlatformInit__(chrome);
|
||||
require('ui/chrome/api/breadcrumbs').__newPlatformInit__(chrome);
|
||||
require('ui/chrome/services/global_nav_state').__newPlatformInit__(chrome);
|
||||
require('ui/new_platform').__newPlatformSetup__(core);
|
||||
require('ui/metadata').__newPlatformSetup__(injectedMetadata.getLegacyMetadata());
|
||||
require('ui/i18n').__newPlatformSetup__(i18n.Context);
|
||||
require('ui/notify/fatal_error').__newPlatformSetup__(fatalErrors);
|
||||
require('ui/notify/toasts').__newPlatformSetup__(notifications.toasts);
|
||||
require('ui/chrome/api/loading_count').__newPlatformSetup__(http);
|
||||
require('ui/chrome/api/base_path').__newPlatformSetup__(basePath);
|
||||
require('ui/chrome/api/ui_settings').__newPlatformSetup__(uiSettings);
|
||||
require('ui/chrome/api/injected_vars').__newPlatformSetup__(injectedMetadata);
|
||||
require('ui/chrome/api/controls').__newPlatformSetup__(chrome);
|
||||
require('ui/chrome/api/help_extension').__newPlatformSetup__(chrome);
|
||||
require('ui/chrome/api/theme').__newPlatformSetup__(chrome);
|
||||
require('ui/chrome/api/breadcrumbs').__newPlatformSetup__(chrome);
|
||||
require('ui/chrome/services/global_nav_state').__newPlatformSetup__(chrome);
|
||||
|
||||
// Load the bootstrap module before loading the legacy platform files so that
|
||||
// the bootstrap module can modify the environment a bit first
|
||||
const bootstrapModule = this.loadBootstrapModule();
|
||||
this.bootstrapModule = this.loadBootstrapModule();
|
||||
|
||||
// require the files that will tie into the legacy platform
|
||||
this.params.requireLegacyFiles();
|
||||
}
|
||||
|
||||
bootstrapModule.bootstrap(this.params.targetDomElement);
|
||||
public start({ core, targetDomElement }: 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/capabilities').__newPlatformStart__(core.capabilities);
|
||||
|
||||
this.bootstrapModule.bootstrap(this.targetDomElement);
|
||||
}
|
||||
|
||||
public stop() {
|
||||
const angularRoot = angular.element(this.params.targetDomElement);
|
||||
if (!this.targetDomElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const angularRoot = angular.element(this.targetDomElement);
|
||||
const injector$ = angularRoot.injector();
|
||||
|
||||
// if we haven't gotten to the point of bootstraping
|
||||
|
@ -90,12 +119,10 @@ export class LegacyPlatformService {
|
|||
injector$.get('$rootScope').$destroy();
|
||||
|
||||
// clear the inner html of the root angular element
|
||||
this.params.targetDomElement.textContent = '';
|
||||
this.targetDomElement.textContent = '';
|
||||
}
|
||||
|
||||
private loadBootstrapModule(): {
|
||||
bootstrap: (targetDomElement: HTMLElement) => void;
|
||||
} {
|
||||
private loadBootstrapModule(): BootstrapModule {
|
||||
if (this.params.useLegacyTestHarness) {
|
||||
// wrapped in NODE_ENV check so the `ui/test_harness` module
|
||||
// is not included in the distributable
|
||||
|
|
|
@ -17,5 +17,9 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { Toast, ToastInput, ToastsSetup } from './toasts';
|
||||
export { NotificationsService, NotificationsSetup } from './notifications_service';
|
||||
export { Toast, ToastInput, ToastsApi } from './toasts';
|
||||
export {
|
||||
NotificationsService,
|
||||
NotificationsSetup,
|
||||
NotificationsStart,
|
||||
} from './notifications_service';
|
||||
|
|
|
@ -16,22 +16,35 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { NotificationsService, NotificationsSetup } from './notifications_service';
|
||||
import {
|
||||
NotificationsService,
|
||||
NotificationsSetup,
|
||||
NotificationsStart,
|
||||
} from './notifications_service';
|
||||
import { toastsServiceMock } from './toasts/toasts_service.mock';
|
||||
import { ToastsSetup } from './toasts/toasts_start';
|
||||
import { ToastsApi } from './toasts/toasts_api';
|
||||
|
||||
const createSetupContractMock = () => {
|
||||
const setupContract: jest.Mocked<NotificationsSetup> = {
|
||||
// we have to suppress type errors until decide how to mock es6 class
|
||||
toasts: (toastsServiceMock.createSetupContract() as unknown) as ToastsSetup,
|
||||
toasts: (toastsServiceMock.createSetupContract() as unknown) as ToastsApi,
|
||||
};
|
||||
return setupContract;
|
||||
};
|
||||
|
||||
const createStartContractMock = () => {
|
||||
const startContract: jest.Mocked<NotificationsStart> = {
|
||||
// we have to suppress type errors until decide how to mock es6 class
|
||||
toasts: (toastsServiceMock.createStartContract() as unknown) as ToastsApi,
|
||||
};
|
||||
return startContract;
|
||||
};
|
||||
|
||||
type NotificationsServiceContract = PublicMethodsOf<NotificationsService>;
|
||||
const createMock = () => {
|
||||
const mocked: jest.Mocked<NotificationsServiceContract> = {
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
};
|
||||
mocked.setup.mockReturnValue(createSetupContractMock());
|
||||
|
@ -41,4 +54,5 @@ const createMock = () => {
|
|||
export const notificationServiceMock = {
|
||||
create: createMock,
|
||||
createSetupContract: createSetupContractMock,
|
||||
createStartContract: createStartContractMock,
|
||||
};
|
||||
|
|
|
@ -19,49 +19,33 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { Observable, Subject, Subscription } from 'rxjs';
|
||||
import { I18nSetup } from '../i18n';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { I18nStart } from '../i18n';
|
||||
import { ToastsService } from './toasts';
|
||||
import { ToastsApi } from './toasts/toasts_api';
|
||||
import { UiSettingsSetup } from '../ui_settings';
|
||||
|
||||
interface NotificationServiceParams {
|
||||
targetDomElement$: Observable<HTMLElement>;
|
||||
interface SetupDeps {
|
||||
uiSettings: UiSettingsSetup;
|
||||
}
|
||||
|
||||
interface NotificationsServiceDeps {
|
||||
i18n: I18nSetup;
|
||||
uiSettings: UiSettingsSetup;
|
||||
interface StartDeps {
|
||||
i18n: I18nStart;
|
||||
targetDomElement: HTMLElement;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export class NotificationsService {
|
||||
private readonly toasts: ToastsService;
|
||||
|
||||
private readonly toastsContainer$: Subject<HTMLElement>;
|
||||
private domElemSubscription?: Subscription;
|
||||
private uiSettingsErrorSubscription?: Subscription;
|
||||
private targetDomElement?: HTMLElement;
|
||||
|
||||
constructor(private readonly params: NotificationServiceParams) {
|
||||
this.toastsContainer$ = new Subject<HTMLElement>();
|
||||
this.toasts = new ToastsService({
|
||||
targetDomElement$: this.toastsContainer$.asObservable(),
|
||||
});
|
||||
constructor() {
|
||||
this.toasts = new ToastsService();
|
||||
}
|
||||
|
||||
public setup({ i18n: i18nDep, uiSettings }: NotificationsServiceDeps) {
|
||||
this.domElemSubscription = this.params.targetDomElement$.subscribe({
|
||||
next: targetDomElement => {
|
||||
this.cleanupTargetDomElement();
|
||||
this.targetDomElement = targetDomElement;
|
||||
|
||||
const toastsContainer = document.createElement('div');
|
||||
targetDomElement.appendChild(toastsContainer);
|
||||
this.toastsContainer$.next(toastsContainer);
|
||||
},
|
||||
});
|
||||
|
||||
const notificationSetup = { toasts: this.toasts.setup({ i18n: i18nDep }) };
|
||||
public setup({ uiSettings }: SetupDeps): NotificationsSetup {
|
||||
const notificationSetup = { toasts: this.toasts.setup() };
|
||||
|
||||
this.uiSettingsErrorSubscription = uiSettings.getUpdateErrors$().subscribe(error => {
|
||||
notificationSetup.toasts.addDanger({
|
||||
|
@ -75,25 +59,31 @@ export class NotificationsService {
|
|||
return notificationSetup;
|
||||
}
|
||||
|
||||
public start({ i18n: i18nDep, targetDomElement }: StartDeps): NotificationsStart {
|
||||
this.targetDomElement = targetDomElement;
|
||||
const toastsContainer = document.createElement('div');
|
||||
targetDomElement.appendChild(toastsContainer);
|
||||
|
||||
return { toasts: this.toasts.start({ i18n: i18nDep, targetDomElement: toastsContainer }) };
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.toasts.stop();
|
||||
this.cleanupTargetDomElement();
|
||||
|
||||
if (this.domElemSubscription) {
|
||||
this.domElemSubscription.unsubscribe();
|
||||
if (this.targetDomElement) {
|
||||
this.targetDomElement.textContent = '';
|
||||
}
|
||||
|
||||
if (this.uiSettingsErrorSubscription) {
|
||||
this.uiSettingsErrorSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
private cleanupTargetDomElement() {
|
||||
if (this.targetDomElement) {
|
||||
this.targetDomElement.textContent = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export type NotificationsSetup = ReturnType<NotificationsService['setup']>;
|
||||
export interface NotificationsSetup {
|
||||
toasts: ToastsApi;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export type NotificationsStart = NotificationsSetup;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`#setup() renders the GlobalToastList into the targetDomElement param 1`] = `
|
||||
exports[`#start() renders the GlobalToastList into the targetDomElement param 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
<I18nContext>
|
||||
|
|
|
@ -18,5 +18,5 @@
|
|||
*/
|
||||
|
||||
export { ToastsService } from './toasts_service';
|
||||
export { ToastsSetup, ToastInput } from './toasts_start';
|
||||
export { ToastsApi, ToastInput } from './toasts_api';
|
||||
export { Toast } from '@elastic/eui';
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
import { ToastsSetup } from './toasts_start';
|
||||
import { ToastsApi } from './toasts_api';
|
||||
|
||||
async function getCurrentToasts(toasts: ToastsSetup) {
|
||||
async function getCurrentToasts(toasts: ToastsApi) {
|
||||
return await toasts
|
||||
.get$()
|
||||
.pipe(take(1))
|
||||
|
@ -30,7 +30,7 @@ async function getCurrentToasts(toasts: ToastsSetup) {
|
|||
|
||||
describe('#get$()', () => {
|
||||
it('returns observable that emits NEW toast list when something added or removed', () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
const onToasts = jest.fn();
|
||||
|
||||
toasts.get$().subscribe(onToasts);
|
||||
|
@ -57,7 +57,7 @@ describe('#get$()', () => {
|
|||
});
|
||||
|
||||
it('does not emit a new toast list when unknown toast is passed to remove()', () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
const onToasts = jest.fn();
|
||||
|
||||
toasts.get$().subscribe(onToasts);
|
||||
|
@ -71,14 +71,14 @@ describe('#get$()', () => {
|
|||
|
||||
describe('#add()', () => {
|
||||
it('returns toast objects with auto assigned id', () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
const toast = toasts.add({ title: 'foo' });
|
||||
expect(toast).toHaveProperty('id');
|
||||
expect(toast).toHaveProperty('title', 'foo');
|
||||
});
|
||||
|
||||
it('adds the toast to toasts list', async () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
const toast = toasts.add({});
|
||||
|
||||
const currentToasts = await getCurrentToasts(toasts);
|
||||
|
@ -87,27 +87,27 @@ describe('#add()', () => {
|
|||
});
|
||||
|
||||
it('increments the toast ID for each additional toast', () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
expect(toasts.add({})).toHaveProperty('id', '0');
|
||||
expect(toasts.add({})).toHaveProperty('id', '1');
|
||||
expect(toasts.add({})).toHaveProperty('id', '2');
|
||||
});
|
||||
|
||||
it('accepts a string, uses it as the title', async () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
expect(toasts.add('foo')).toHaveProperty('title', 'foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#remove()', () => {
|
||||
it('removes a toast', async () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
toasts.remove(toasts.add('Test'));
|
||||
expect(await getCurrentToasts(toasts)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('ignores unknown toast', async () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
toasts.add('Test');
|
||||
toasts.remove({ id: 'foo' });
|
||||
|
||||
|
@ -118,12 +118,12 @@ describe('#remove()', () => {
|
|||
|
||||
describe('#addSuccess()', () => {
|
||||
it('adds a success toast', async () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
expect(toasts.addSuccess({})).toHaveProperty('color', 'success');
|
||||
});
|
||||
|
||||
it('returns the created toast', async () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
const toast = toasts.addSuccess({});
|
||||
const currentToasts = await getCurrentToasts(toasts);
|
||||
expect(currentToasts[0]).toBe(toast);
|
||||
|
@ -132,12 +132,12 @@ describe('#addSuccess()', () => {
|
|||
|
||||
describe('#addWarning()', () => {
|
||||
it('adds a warning toast', async () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
expect(toasts.addWarning({})).toHaveProperty('color', 'warning');
|
||||
});
|
||||
|
||||
it('returns the created toast', async () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
const toast = toasts.addWarning({});
|
||||
const currentToasts = await getCurrentToasts(toasts);
|
||||
expect(currentToasts[0]).toBe(toast);
|
||||
|
@ -146,12 +146,12 @@ describe('#addWarning()', () => {
|
|||
|
||||
describe('#addDanger()', () => {
|
||||
it('adds a danger toast', async () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
expect(toasts.addDanger({})).toHaveProperty('color', 'danger');
|
||||
});
|
||||
|
||||
it('returns the created toast', async () => {
|
||||
const toasts = new ToastsSetup();
|
||||
const toasts = new ToastsApi();
|
||||
const toast = toasts.addDanger({});
|
||||
const currentToasts = await getCurrentToasts(toasts);
|
||||
expect(currentToasts[0]).toBe(toast);
|
|
@ -34,7 +34,7 @@ const normalizeToast = (toastOrTitle: ToastInput) => {
|
|||
};
|
||||
|
||||
/** @public */
|
||||
export class ToastsSetup {
|
||||
export class ToastsApi {
|
||||
private toasts$ = new Rx.BehaviorSubject<Toast[]>([]);
|
||||
private idCounter = 0;
|
||||
|
|
@ -16,10 +16,10 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { ToastsSetup } from './toasts_start';
|
||||
import { ToastsApi } from './toasts_api';
|
||||
|
||||
const createSetupContractMock = () => {
|
||||
const setupContract: jest.Mocked<PublicMethodsOf<ToastsSetup>> = {
|
||||
const createToastsApiMock = () => {
|
||||
const api: jest.Mocked<PublicMethodsOf<ToastsApi>> = {
|
||||
get$: jest.fn(),
|
||||
add: jest.fn(),
|
||||
remove: jest.fn(),
|
||||
|
@ -27,9 +27,14 @@ const createSetupContractMock = () => {
|
|||
addWarning: jest.fn(),
|
||||
addDanger: jest.fn(),
|
||||
};
|
||||
return setupContract;
|
||||
return api;
|
||||
};
|
||||
|
||||
const createSetupContractMock = createToastsApiMock;
|
||||
|
||||
const createStartContractMock = createToastsApiMock;
|
||||
|
||||
export const toastsServiceMock = {
|
||||
createSetupContract: createSetupContractMock,
|
||||
createStartContract: createStartContractMock,
|
||||
};
|
||||
|
|
|
@ -19,9 +19,8 @@
|
|||
|
||||
import { mockReactDomRender, mockReactDomUnmount } from './toasts_service.test.mocks';
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { ToastsService } from './toasts_service';
|
||||
import { ToastsSetup } from './toasts_start';
|
||||
import { ToastsApi } from './toasts_api';
|
||||
|
||||
const mockI18n: any = {
|
||||
Context: function I18nContext() {
|
||||
|
@ -30,22 +29,31 @@ const mockI18n: any = {
|
|||
};
|
||||
|
||||
describe('#setup()', () => {
|
||||
it('returns a ToastsApi', () => {
|
||||
const toasts = new ToastsService();
|
||||
|
||||
expect(toasts.setup()).toBeInstanceOf(ToastsApi);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#start()', () => {
|
||||
it('renders the GlobalToastList into the targetDomElement param', async () => {
|
||||
const targetDomElement = document.createElement('div');
|
||||
targetDomElement.setAttribute('test', 'target-dom-element');
|
||||
const toasts = new ToastsService({ targetDomElement$: of(targetDomElement) });
|
||||
const toasts = new ToastsService();
|
||||
|
||||
expect(mockReactDomRender).not.toHaveBeenCalled();
|
||||
toasts.setup({ i18n: mockI18n });
|
||||
toasts.setup();
|
||||
toasts.start({ i18n: mockI18n, targetDomElement });
|
||||
expect(mockReactDomRender.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('returns a ToastsSetup', () => {
|
||||
const toasts = new ToastsService({
|
||||
targetDomElement$: of(document.createElement('div')),
|
||||
});
|
||||
it('returns a ToastsApi', () => {
|
||||
const targetDomElement = document.createElement('div');
|
||||
const toasts = new ToastsService();
|
||||
|
||||
expect(toasts.setup({ i18n: mockI18n })).toBeInstanceOf(ToastsSetup);
|
||||
toasts.setup();
|
||||
expect(toasts.start({ i18n: mockI18n, targetDomElement })).toBeInstanceOf(ToastsApi);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -53,9 +61,10 @@ describe('#stop()', () => {
|
|||
it('unmounts the GlobalToastList from the targetDomElement', () => {
|
||||
const targetDomElement = document.createElement('div');
|
||||
targetDomElement.setAttribute('test', 'target-dom-element');
|
||||
const toasts = new ToastsService({ targetDomElement$: of(targetDomElement) });
|
||||
const toasts = new ToastsService();
|
||||
|
||||
toasts.setup({ i18n: mockI18n });
|
||||
toasts.setup();
|
||||
toasts.start({ i18n: mockI18n, targetDomElement });
|
||||
|
||||
expect(mockReactDomUnmount).not.toHaveBeenCalled();
|
||||
toasts.stop();
|
||||
|
@ -63,9 +72,7 @@ describe('#stop()', () => {
|
|||
});
|
||||
|
||||
it('does not fail if setup() was never called', () => {
|
||||
const targetDomElement = document.createElement('div');
|
||||
targetDomElement.setAttribute('test', 'target-dom-element');
|
||||
const toasts = new ToastsService({ targetDomElement$: of(targetDomElement) });
|
||||
const toasts = new ToastsService();
|
||||
expect(() => {
|
||||
toasts.stop();
|
||||
}).not.toThrowError();
|
||||
|
@ -73,9 +80,10 @@ describe('#stop()', () => {
|
|||
|
||||
it('empties the content of the targetDomElement', () => {
|
||||
const targetDomElement = document.createElement('div');
|
||||
const toasts = new ToastsService({ targetDomElement$: of(targetDomElement) });
|
||||
const toasts = new ToastsService();
|
||||
|
||||
toasts.setup({ i18n: mockI18n });
|
||||
toasts.setup();
|
||||
toasts.start({ i18n: mockI18n, targetDomElement });
|
||||
toasts.stop();
|
||||
expect(targetDomElement.childNodes).toHaveLength(0);
|
||||
});
|
||||
|
|
|
@ -19,59 +19,43 @@
|
|||
|
||||
import React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
|
||||
import { Toast } from '@elastic/eui';
|
||||
import { I18nSetup } from '../../i18n';
|
||||
import { GlobalToastList } from './global_toast_list';
|
||||
import { ToastsSetup } from './toasts_start';
|
||||
import { ToastsApi } from './toasts_api';
|
||||
|
||||
interface Params {
|
||||
targetDomElement$: Observable<HTMLElement>;
|
||||
}
|
||||
|
||||
interface Deps {
|
||||
interface StartDeps {
|
||||
i18n: I18nSetup;
|
||||
targetDomElement: HTMLElement;
|
||||
}
|
||||
|
||||
export class ToastsService {
|
||||
private domElemSubscription?: Subscription;
|
||||
private api?: ToastsApi;
|
||||
private targetDomElement?: HTMLElement;
|
||||
|
||||
constructor(private readonly params: Params) {}
|
||||
public setup() {
|
||||
this.api = new ToastsApi();
|
||||
return this.api!;
|
||||
}
|
||||
|
||||
public setup({ i18n }: Deps) {
|
||||
const toasts = new ToastsSetup();
|
||||
public start({ i18n, targetDomElement }: StartDeps) {
|
||||
this.targetDomElement = targetDomElement;
|
||||
|
||||
this.domElemSubscription = this.params.targetDomElement$.subscribe({
|
||||
next: targetDomElement => {
|
||||
this.cleanupTargetDomElement();
|
||||
this.targetDomElement = targetDomElement;
|
||||
render(
|
||||
<i18n.Context>
|
||||
<GlobalToastList
|
||||
dismissToast={(toast: Toast) => this.api!.remove(toast)}
|
||||
toasts$={this.api!.get$()}
|
||||
/>
|
||||
</i18n.Context>,
|
||||
targetDomElement
|
||||
);
|
||||
|
||||
render(
|
||||
<i18n.Context>
|
||||
<GlobalToastList
|
||||
dismissToast={(toast: Toast) => toasts.remove(toast)}
|
||||
toasts$={toasts.get$()}
|
||||
/>
|
||||
</i18n.Context>,
|
||||
targetDomElement
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return toasts;
|
||||
return this.api!;
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.cleanupTargetDomElement();
|
||||
|
||||
if (this.domElemSubscription) {
|
||||
this.domElemSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
private cleanupTargetDomElement() {
|
||||
if (this.targetDomElement) {
|
||||
unmountComponentAtNode(this.targetDomElement);
|
||||
this.targetDomElement.textContent = '';
|
||||
|
|
|
@ -17,5 +17,5 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { OverlayService, OverlaySetup } from './overlay_service';
|
||||
export { OverlayService, OverlayStart } from './overlay_service';
|
||||
export { FlyoutRef } from './flyout';
|
||||
|
|
|
@ -16,24 +16,24 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { OverlayService, OverlaySetup } from './overlay_service';
|
||||
import { OverlayService, OverlayStart } from './overlay_service';
|
||||
|
||||
const createSetupContractMock = () => {
|
||||
const setupContract: jest.Mocked<PublicMethodsOf<OverlaySetup>> = {
|
||||
const createStartContractMock = () => {
|
||||
const startContract: jest.Mocked<PublicMethodsOf<OverlayStart>> = {
|
||||
openFlyout: jest.fn(),
|
||||
};
|
||||
return setupContract;
|
||||
return startContract;
|
||||
};
|
||||
|
||||
const createMock = () => {
|
||||
const mocked: jest.Mocked<PublicMethodsOf<OverlayService>> = {
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
};
|
||||
mocked.setup.mockReturnValue(createSetupContractMock());
|
||||
mocked.start.mockReturnValue(createStartContractMock());
|
||||
return mocked;
|
||||
};
|
||||
|
||||
export const overlayServiceMock = {
|
||||
create: createMock,
|
||||
createSetupContract: createSetupContractMock,
|
||||
createStartContract: createStartContractMock,
|
||||
};
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
import { FlyoutService } from './flyout';
|
||||
|
||||
import { FlyoutRef } from '..';
|
||||
import { I18nSetup } from '../i18n';
|
||||
import { I18nStart } from '../i18n';
|
||||
|
||||
interface Deps {
|
||||
i18n: I18nSetup;
|
||||
interface StartDeps {
|
||||
i18n: I18nStart;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
@ -34,7 +34,7 @@ export class OverlayService {
|
|||
this.flyoutService = new FlyoutService(targetDomElement);
|
||||
}
|
||||
|
||||
public setup({ i18n }: Deps): OverlaySetup {
|
||||
public start({ i18n }: StartDeps): OverlayStart {
|
||||
return {
|
||||
openFlyout: this.flyoutService.openFlyout.bind(this.flyoutService, i18n),
|
||||
};
|
||||
|
@ -42,7 +42,7 @@ export class OverlayService {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export interface OverlaySetup {
|
||||
export interface OverlayStart {
|
||||
openFlyout: (
|
||||
flyoutChildren: React.ReactNode,
|
||||
flyoutProps?: {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
export const mockPlugin = {
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
};
|
||||
export const mockInitializer = jest.fn(() => mockPlugin);
|
||||
|
|
|
@ -41,6 +41,7 @@ const addBasePath = (path: string) => path;
|
|||
beforeEach(() => {
|
||||
mockPluginLoader.mockClear();
|
||||
mockPlugin.setup.mockClear();
|
||||
mockPlugin.start.mockClear();
|
||||
mockPlugin.stop.mockClear();
|
||||
plugin = new PluginWrapper(createManifest('plugin-a'), initializerContext);
|
||||
});
|
||||
|
@ -58,13 +59,21 @@ describe('PluginWrapper', () => {
|
|||
});
|
||||
|
||||
test('`setup` fails if plugin.setup is not a function', async () => {
|
||||
mockInitializer.mockReturnValueOnce({ stop: jest.fn() } as any);
|
||||
mockInitializer.mockReturnValueOnce({ start: jest.fn() } as any);
|
||||
await plugin.load(addBasePath);
|
||||
await expect(plugin.setup({} as any, {} as any)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Instance of plugin \\"plugin-a\\" does not define \\"setup\\" function."`
|
||||
);
|
||||
});
|
||||
|
||||
test('`setup` fails if plugin.start is not a function', async () => {
|
||||
mockInitializer.mockReturnValueOnce({ setup: jest.fn() } as any);
|
||||
await plugin.load(addBasePath);
|
||||
await expect(plugin.setup({} as any, {} as any)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Instance of plugin \\"plugin-a\\" does not define \\"start\\" function."`
|
||||
);
|
||||
});
|
||||
|
||||
test('`setup` calls initializer with initializer context', async () => {
|
||||
await plugin.load(addBasePath);
|
||||
await plugin.setup({} as any, {} as any);
|
||||
|
@ -79,6 +88,22 @@ describe('PluginWrapper', () => {
|
|||
expect(mockPlugin.setup).toHaveBeenCalledWith(context, deps);
|
||||
});
|
||||
|
||||
test('`start` fails if setup is not called first', async () => {
|
||||
await plugin.load(addBasePath);
|
||||
await expect(plugin.start({} as any, {} as any)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Plugin \\"plugin-a\\" can't be started since it isn't set up."`
|
||||
);
|
||||
});
|
||||
|
||||
test('`start` calls plugin.start with context and dependencies', async () => {
|
||||
await plugin.load(addBasePath);
|
||||
await plugin.setup({} as any, {} as any);
|
||||
const context = { any: 'thing' } as any;
|
||||
const deps = { otherDep: 'value' };
|
||||
await plugin.start(context, deps);
|
||||
expect(mockPlugin.start).toHaveBeenCalledWith(context, deps);
|
||||
});
|
||||
|
||||
test('`stop` fails if plugin is not setup up', async () => {
|
||||
expect(() => plugin.stop()).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Plugin \\"plugin-a\\" can't be stopped since it isn't set up."`
|
||||
|
@ -93,7 +118,7 @@ describe('PluginWrapper', () => {
|
|||
});
|
||||
|
||||
test('`stop` does not fail if plugin.stop does not exist', async () => {
|
||||
mockInitializer.mockReturnValueOnce({ setup: jest.fn() } as any);
|
||||
mockInitializer.mockReturnValueOnce({ setup: jest.fn(), start: jest.fn() } as any);
|
||||
await plugin.load(addBasePath);
|
||||
await plugin.setup({} as any, {} as any);
|
||||
expect(() => plugin.stop()).not.toThrow();
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { DiscoveredPlugin, PluginName } from '../../server';
|
||||
import { PluginInitializerContext, PluginSetupContext } from './plugin_context';
|
||||
import { PluginInitializerContext, PluginSetupContext, PluginStartContext } from './plugin_context';
|
||||
import { loadPluginBundle } from './plugin_loader';
|
||||
|
||||
/**
|
||||
|
@ -26,8 +26,14 @@ import { loadPluginBundle } from './plugin_loader';
|
|||
*
|
||||
* @public
|
||||
*/
|
||||
export interface Plugin<TSetup, TPluginsSetup extends Record<string, unknown> = {}> {
|
||||
export interface Plugin<
|
||||
TSetup,
|
||||
TStart,
|
||||
TPluginsSetup extends Record<string, unknown> = {},
|
||||
TPluginsStart extends Record<string, unknown> = {}
|
||||
> {
|
||||
setup: (core: PluginSetupContext, plugins: TPluginsSetup) => TSetup | Promise<TSetup>;
|
||||
start: (core: PluginStartContext, plugins: TPluginsStart) => TStart | Promise<TStart>;
|
||||
stop?: () => void;
|
||||
}
|
||||
|
||||
|
@ -37,9 +43,12 @@ export interface Plugin<TSetup, TPluginsSetup extends Record<string, unknown> =
|
|||
*
|
||||
* @public
|
||||
*/
|
||||
export type PluginInitializer<TSetup, TPluginsSetup extends Record<string, unknown> = {}> = (
|
||||
core: PluginInitializerContext
|
||||
) => Plugin<TSetup, TPluginsSetup>;
|
||||
export type PluginInitializer<
|
||||
TSetup,
|
||||
TStart,
|
||||
TPluginsSetup extends Record<string, unknown> = {},
|
||||
TPluginsStart extends Record<string, unknown> = {}
|
||||
> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
|
||||
|
||||
/**
|
||||
* Lightweight wrapper around discovered plugin that is responsible for instantiating
|
||||
|
@ -49,14 +58,16 @@ export type PluginInitializer<TSetup, TPluginsSetup extends Record<string, unkno
|
|||
*/
|
||||
export class PluginWrapper<
|
||||
TSetup = unknown,
|
||||
TPluginsSetup extends Record<PluginName, unknown> = Record<PluginName, unknown>
|
||||
TStart = unknown,
|
||||
TPluginsSetup extends Record<PluginName, unknown> = Record<PluginName, unknown>,
|
||||
TPluginsStart extends Record<PluginName, unknown> = Record<PluginName, unknown>
|
||||
> {
|
||||
public readonly name: DiscoveredPlugin['id'];
|
||||
public readonly configPath: DiscoveredPlugin['configPath'];
|
||||
public readonly requiredPlugins: DiscoveredPlugin['requiredPlugins'];
|
||||
public readonly optionalPlugins: DiscoveredPlugin['optionalPlugins'];
|
||||
private initializer?: PluginInitializer<TSetup, TPluginsSetup>;
|
||||
private instance?: Plugin<TSetup, TPluginsSetup>;
|
||||
private initializer?: PluginInitializer<TSetup, TStart, TPluginsSetup, TPluginsStart>;
|
||||
private instance?: Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
|
||||
|
||||
constructor(
|
||||
readonly discoveredPlugin: DiscoveredPlugin,
|
||||
|
@ -74,7 +85,10 @@ export class PluginWrapper<
|
|||
* @param addBasePath Function that adds the base path to a string for plugin bundle path.
|
||||
*/
|
||||
public async load(addBasePath: (path: string) => string) {
|
||||
this.initializer = await loadPluginBundle<TSetup, TPluginsSetup>(addBasePath, this.name);
|
||||
this.initializer = await loadPluginBundle<TSetup, TStart, TPluginsSetup, TPluginsStart>(
|
||||
addBasePath,
|
||||
this.name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,6 +104,21 @@ export class PluginWrapper<
|
|||
return await this.instance.setup(setupContext, plugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls `setup` function exposed by the initialized plugin.
|
||||
* @param startContext Context that consists of various core services tailored specifically
|
||||
* for the `start` lifecycle event.
|
||||
* @param plugins The dictionary where the key is the dependency name and the value
|
||||
* is the contract returned by the dependency's `start` function.
|
||||
*/
|
||||
public async start(startContext: PluginStartContext, plugins: TPluginsStart) {
|
||||
if (this.instance === undefined) {
|
||||
throw new Error(`Plugin "${this.name}" can't be started since it isn't set up.`);
|
||||
}
|
||||
|
||||
return await this.instance.start(startContext, plugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls optional `stop` function exposed by the plugin initializer.
|
||||
*/
|
||||
|
@ -114,6 +143,8 @@ export class PluginWrapper<
|
|||
|
||||
if (typeof instance.setup !== 'function') {
|
||||
throw new Error(`Instance of plugin "${this.name}" does not define "setup" function.`);
|
||||
} else if (typeof instance.start !== 'function') {
|
||||
throw new Error(`Instance of plugin "${this.name}" does not define "start" function.`);
|
||||
}
|
||||
|
||||
return instance;
|
||||
|
|
|
@ -22,11 +22,13 @@ import { BasePathSetup } from '../base_path';
|
|||
import { ChromeSetup } from '../chrome';
|
||||
import { CoreContext } from '../core_system';
|
||||
import { FatalErrorsSetup } from '../fatal_errors';
|
||||
import { I18nSetup } from '../i18n';
|
||||
import { NotificationsSetup } from '../notifications';
|
||||
import { I18nSetup, I18nStart } from '../i18n';
|
||||
import { NotificationsSetup, NotificationsStart } from '../notifications';
|
||||
import { UiSettingsSetup } from '../ui_settings';
|
||||
import { PluginWrapper } from './plugin';
|
||||
import { PluginsServiceSetupDeps } from './plugins_service';
|
||||
import { PluginsServiceSetupDeps, PluginsServiceStartDeps } from './plugins_service';
|
||||
import { CapabilitiesStart } from '../capabilities';
|
||||
import { OverlayStart } from '../overlays';
|
||||
|
||||
/**
|
||||
* The available core services passed to a `PluginInitializer`
|
||||
|
@ -50,6 +52,18 @@ export interface PluginSetupContext {
|
|||
uiSettings: UiSettingsSetup;
|
||||
}
|
||||
|
||||
/**
|
||||
* The available core services passed to a plugin's `Plugin#start` method.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface PluginStartContext {
|
||||
capabilities: CapabilitiesStart;
|
||||
i18n: I18nStart;
|
||||
notifications: NotificationsStart;
|
||||
overlays: OverlayStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a plugin-specific context passed to the plugin's construtor. This is currently
|
||||
* empty but should provide static services in the future, such as config and logging.
|
||||
|
@ -75,10 +89,10 @@ export function createPluginInitializerContext(
|
|||
* @param plugin
|
||||
* @internal
|
||||
*/
|
||||
export function createPluginSetupContext<TPlugin, TPluginDependencies>(
|
||||
export function createPluginSetupContext<TSetup, TStart, TPluginsSetup, TPluginsStart>(
|
||||
coreContext: CoreContext,
|
||||
deps: PluginsServiceSetupDeps,
|
||||
plugin: PluginWrapper<TPlugin, TPluginDependencies>
|
||||
plugin: PluginWrapper<TSetup, TStart, TPluginsSetup, TPluginsStart>
|
||||
): PluginSetupContext {
|
||||
return {
|
||||
basePath: deps.basePath,
|
||||
|
@ -89,3 +103,26 @@ export function createPluginSetupContext<TPlugin, TPluginDependencies>(
|
|||
uiSettings: deps.uiSettings,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a plugin-specific context passed to the plugin's `start` lifecycle event. Currently
|
||||
* this returns a shallow copy the service start contracts, but in the future could provide
|
||||
* plugin-scoped versions of the service.
|
||||
*
|
||||
* @param coreContext
|
||||
* @param deps
|
||||
* @param plugin
|
||||
* @internal
|
||||
*/
|
||||
export function createPluginStartContext<TSetup, TStart, TPluginsSetup, TPluginsStart>(
|
||||
coreContext: CoreContext,
|
||||
deps: PluginsServiceStartDeps,
|
||||
plugin: PluginWrapper<TSetup, TStart, TPluginsSetup, TPluginsStart>
|
||||
): PluginStartContext {
|
||||
return {
|
||||
capabilities: deps.capabilities,
|
||||
i18n: deps.i18n,
|
||||
notifications: deps.notifications,
|
||||
overlays: deps.overlays,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -61,65 +61,74 @@ export const LOAD_TIMEOUT = 120 * 1000; // 2 minutes
|
|||
*/
|
||||
export const loadPluginBundle: LoadPluginBundle = <
|
||||
TSetup,
|
||||
TDependencies extends Record<string, unknown>
|
||||
TStart,
|
||||
TPluginsSetup extends Record<string, unknown>,
|
||||
TPluginsStart extends Record<string, unknown>
|
||||
>(
|
||||
addBasePath: (path: string) => string,
|
||||
pluginName: PluginName,
|
||||
{ timeoutMs = LOAD_TIMEOUT } = {}
|
||||
{ timeoutMs = LOAD_TIMEOUT }: { timeoutMs?: number } = {}
|
||||
) =>
|
||||
new Promise<PluginInitializer<TSetup, TDependencies>>((resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
const coreWindow = (window as unknown) as CoreWindow;
|
||||
new Promise<PluginInitializer<TSetup, TStart, TPluginsSetup, TPluginsStart>>(
|
||||
(resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
const coreWindow = (window as unknown) as CoreWindow;
|
||||
|
||||
// Assumes that all plugin bundles get put into the bundles/plugins subdirectory
|
||||
const bundlePath = addBasePath(`/bundles/plugin/${pluginName}.bundle.js`);
|
||||
script.setAttribute('src', bundlePath);
|
||||
script.setAttribute('id', `kbn-plugin-${pluginName}`);
|
||||
script.setAttribute('async', '');
|
||||
// Assumes that all plugin bundles get put into the bundles/plugins subdirectory
|
||||
const bundlePath = addBasePath(`/bundles/plugin/${pluginName}.bundle.js`);
|
||||
script.setAttribute('src', bundlePath);
|
||||
script.setAttribute('id', `kbn-plugin-${pluginName}`);
|
||||
script.setAttribute('async', '');
|
||||
|
||||
// Add kbnNonce for CSP
|
||||
script.setAttribute('nonce', coreWindow.__kbnNonce__);
|
||||
// Add kbnNonce for CSP
|
||||
script.setAttribute('nonce', coreWindow.__kbnNonce__);
|
||||
|
||||
const cleanupTag = () => {
|
||||
clearTimeout(timeout);
|
||||
// Set to null for IE memory leak issue. Webpack does the same thing.
|
||||
// @ts-ignore
|
||||
script.onload = script.onerror = null;
|
||||
};
|
||||
const cleanupTag = () => {
|
||||
clearTimeout(timeout);
|
||||
// Set to null for IE memory leak issue. Webpack does the same thing.
|
||||
// @ts-ignore
|
||||
script.onload = script.onerror = null;
|
||||
};
|
||||
|
||||
// Wire up resolve and reject
|
||||
script.onload = () => {
|
||||
cleanupTag();
|
||||
// Wire up resolve and reject
|
||||
script.onload = () => {
|
||||
cleanupTag();
|
||||
|
||||
const initializer = coreWindow.__kbnBundles__[`plugin/${pluginName}`];
|
||||
if (!initializer || typeof initializer !== 'function') {
|
||||
reject(
|
||||
new Error(`Definition of plugin "${pluginName}" should be a function (${bundlePath}).`)
|
||||
);
|
||||
} else {
|
||||
resolve(initializer as PluginInitializer<TSetup, TDependencies>);
|
||||
}
|
||||
};
|
||||
const initializer = coreWindow.__kbnBundles__[`plugin/${pluginName}`];
|
||||
if (!initializer || typeof initializer !== 'function') {
|
||||
reject(
|
||||
new Error(`Definition of plugin "${pluginName}" should be a function (${bundlePath}).`)
|
||||
);
|
||||
} else {
|
||||
resolve(initializer as PluginInitializer<TSetup, TStart, TPluginsSetup, TPluginsStart>);
|
||||
}
|
||||
};
|
||||
|
||||
script.onerror = () => {
|
||||
cleanupTag();
|
||||
reject(new Error(`Failed to load "${pluginName}" bundle (${bundlePath})`));
|
||||
};
|
||||
script.onerror = () => {
|
||||
cleanupTag();
|
||||
reject(new Error(`Failed to load "${pluginName}" bundle (${bundlePath})`));
|
||||
};
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
cleanupTag();
|
||||
reject(new Error(`Timeout reached when loading "${pluginName}" bundle (${bundlePath})`));
|
||||
}, timeoutMs);
|
||||
const timeout = setTimeout(() => {
|
||||
cleanupTag();
|
||||
reject(new Error(`Timeout reached when loading "${pluginName}" bundle (${bundlePath})`));
|
||||
}, timeoutMs);
|
||||
|
||||
// Add the script tag to the end of the body to start downloading
|
||||
document.body.appendChild(script);
|
||||
});
|
||||
// Add the script tag to the end of the body to start downloading
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type LoadPluginBundle = <TSetup, TDependencies extends Record<string, unknown>>(
|
||||
export type LoadPluginBundle = <
|
||||
TSetup,
|
||||
TStart,
|
||||
TPluginsSetup extends Record<string, unknown>,
|
||||
TPluginsStart extends Record<string, unknown>
|
||||
>(
|
||||
addBasePath: (path: string) => string,
|
||||
pluginName: PluginName,
|
||||
options?: { timeoutMs?: number }
|
||||
) => Promise<PluginInitializer<TSetup, TDependencies>>;
|
||||
) => Promise<PluginInitializer<TSetup, TStart, TPluginsSetup, TPluginsStart>>;
|
||||
|
|
|
@ -20,25 +20,36 @@
|
|||
import { PluginsService, PluginsServiceSetup } from './plugins_service';
|
||||
|
||||
const createSetupContractMock = () => {
|
||||
const setupContract: jest.Mocked<PublicMethodsOf<PluginsServiceSetup>> = {
|
||||
pluginSetups: new Map(),
|
||||
const setupContract: jest.Mocked<PluginsServiceSetup> = {
|
||||
contracts: new Map(),
|
||||
};
|
||||
// we have to suppress type errors until decide how to mock es6 class
|
||||
return (setupContract as unknown) as PluginsServiceSetup;
|
||||
return setupContract as PluginsServiceSetup;
|
||||
};
|
||||
|
||||
const createStartContractMock = () => {
|
||||
const startContract: jest.Mocked<PluginsServiceSetup> = {
|
||||
contracts: new Map(),
|
||||
};
|
||||
// we have to suppress type errors until decide how to mock es6 class
|
||||
return startContract as PluginsServiceSetup;
|
||||
};
|
||||
|
||||
type PluginsServiceContract = PublicMethodsOf<PluginsService>;
|
||||
const createMock = () => {
|
||||
const mocked: jest.Mocked<PluginsServiceContract> = {
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
};
|
||||
|
||||
mocked.setup.mockResolvedValue(createSetupContractMock());
|
||||
mocked.start.mockResolvedValue(createStartContractMock());
|
||||
return mocked;
|
||||
};
|
||||
|
||||
export const pluginsServiceMock = {
|
||||
create: createMock,
|
||||
createSetupContract: createSetupContractMock,
|
||||
createStartContract: createStartContractMock,
|
||||
};
|
||||
|
|
|
@ -27,7 +27,21 @@ import {
|
|||
|
||||
import { PluginName } from 'src/core/server';
|
||||
import { CoreContext } from '../core_system';
|
||||
import { PluginsService } from './plugins_service';
|
||||
import {
|
||||
PluginsService,
|
||||
PluginsServiceStartDeps,
|
||||
PluginsServiceSetupDeps,
|
||||
} from './plugins_service';
|
||||
import { notificationServiceMock } from '../notifications/notifications_service.mock';
|
||||
import { capabilitiesServiceMock } from '../capabilities/capabilities_service.mock';
|
||||
import { i18nServiceMock } from '../i18n/i18n_service.mock';
|
||||
import { overlayServiceMock } from '../overlays/overlay_service.mock';
|
||||
import { PluginStartContext, PluginSetupContext } from './plugin_context';
|
||||
import { chromeServiceMock } from '../chrome/chrome_service.mock';
|
||||
import { fatalErrorsServiceMock } from '../fatal_errors/fatal_errors_service.mock';
|
||||
import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock';
|
||||
import { basePathServiceMock } from '../base_path/base_path_service.mock';
|
||||
import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock';
|
||||
|
||||
export let mockPluginInitializers: Map<PluginName, MockedPluginInitializer>;
|
||||
|
||||
|
@ -35,41 +49,56 @@ mockPluginInitializerProvider.mockImplementation(
|
|||
pluginName => mockPluginInitializers.get(pluginName)!
|
||||
);
|
||||
|
||||
type DeeplyMocked<T> = { [P in keyof T]: jest.Mocked<T[P]> };
|
||||
|
||||
const mockCoreContext: CoreContext = {};
|
||||
let mockDeps: any;
|
||||
let mockInitContext: any;
|
||||
let mockSetupDeps: DeeplyMocked<PluginsServiceSetupDeps>;
|
||||
let mockSetupContext: DeeplyMocked<PluginSetupContext>;
|
||||
let mockStartDeps: DeeplyMocked<PluginsServiceStartDeps>;
|
||||
let mockStartContext: DeeplyMocked<PluginStartContext>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockDeps = {
|
||||
injectedMetadata: {
|
||||
getPlugins: jest.fn(() => [
|
||||
mockSetupDeps = {
|
||||
injectedMetadata: (function() {
|
||||
const metadata = injectedMetadataServiceMock.createSetupContract();
|
||||
metadata.getPlugins.mockReturnValue([
|
||||
{ id: 'pluginA', plugin: createManifest('pluginA') },
|
||||
{ id: 'pluginB', plugin: createManifest('pluginB', { required: ['pluginA'] }) },
|
||||
{
|
||||
id: 'pluginC',
|
||||
plugin: createManifest('pluginC', { required: ['pluginA'], optional: ['nonexist'] }),
|
||||
},
|
||||
]),
|
||||
},
|
||||
basePath: {
|
||||
addToPath(path: string) {
|
||||
return path;
|
||||
},
|
||||
},
|
||||
chrome: {},
|
||||
fatalErrors: {},
|
||||
i18n: {},
|
||||
notifications: {},
|
||||
uiSettings: {},
|
||||
]);
|
||||
return metadata;
|
||||
})(),
|
||||
basePath: (function() {
|
||||
const basePath = basePathServiceMock.createSetupContract();
|
||||
basePath.addToPath.mockImplementation(path => path);
|
||||
return basePath;
|
||||
})(),
|
||||
chrome: chromeServiceMock.createSetupContract(),
|
||||
fatalErrors: fatalErrorsServiceMock.createSetupContract(),
|
||||
i18n: i18nServiceMock.createSetupContract(),
|
||||
notifications: notificationServiceMock.createSetupContract(),
|
||||
uiSettings: uiSettingsServiceMock.createSetupContract(),
|
||||
} as any;
|
||||
mockInitContext = omit(mockDeps, 'injectedMetadata');
|
||||
mockSetupContext = omit(mockSetupDeps, 'injectedMetadata');
|
||||
mockStartDeps = {
|
||||
capabilities: capabilitiesServiceMock.createStartContract(),
|
||||
i18n: i18nServiceMock.createStartContract(),
|
||||
injectedMetadata: injectedMetadataServiceMock.createStartContract(),
|
||||
notifications: notificationServiceMock.createStartContract(),
|
||||
overlays: overlayServiceMock.createStartContract(),
|
||||
};
|
||||
mockStartContext = omit(mockStartDeps, 'injectedMetadata');
|
||||
|
||||
// Reset these for each test.
|
||||
mockPluginInitializers = new Map<PluginName, MockedPluginInitializer>(([
|
||||
[
|
||||
'pluginA',
|
||||
jest.fn(() => ({
|
||||
setup: jest.fn(() => ({ exportedValue: 1 })),
|
||||
setup: jest.fn(() => ({ setupValue: 1 })),
|
||||
start: jest.fn(() => ({ startValue: 2 })),
|
||||
stop: jest.fn(),
|
||||
})),
|
||||
],
|
||||
|
@ -77,7 +106,10 @@ beforeEach(() => {
|
|||
'pluginB',
|
||||
jest.fn(() => ({
|
||||
setup: jest.fn((core, deps: any) => ({
|
||||
pluginAPlusB: deps.pluginA.exportedValue + 1,
|
||||
pluginAPlusB: deps.pluginA.setupValue + 1,
|
||||
})),
|
||||
start: jest.fn((core, deps: any) => ({
|
||||
pluginAPlusB: deps.pluginA.startValue + 1,
|
||||
})),
|
||||
stop: jest.fn(),
|
||||
})),
|
||||
|
@ -86,6 +118,7 @@ beforeEach(() => {
|
|||
'pluginC',
|
||||
jest.fn(() => ({
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
})),
|
||||
],
|
||||
|
@ -113,7 +146,7 @@ test('`PluginsService.setup` fails if any bundle cannot be loaded', async () =>
|
|||
mockLoadPluginBundle.mockRejectedValueOnce(new Error('Could not load bundle'));
|
||||
|
||||
const pluginsService = new PluginsService(mockCoreContext);
|
||||
await expect(pluginsService.setup(mockDeps)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
await expect(pluginsService.setup(mockSetupDeps)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Could not load bundle"`
|
||||
);
|
||||
});
|
||||
|
@ -121,24 +154,24 @@ test('`PluginsService.setup` fails if any bundle cannot be loaded', async () =>
|
|||
test('`PluginsService.setup` fails if any plugin instance does not have a setup function', async () => {
|
||||
mockPluginInitializers.set('pluginA', (() => ({})) as any);
|
||||
const pluginsService = new PluginsService(mockCoreContext);
|
||||
await expect(pluginsService.setup(mockDeps)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
await expect(pluginsService.setup(mockSetupDeps)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Instance of plugin \\"pluginA\\" does not define \\"setup\\" function."`
|
||||
);
|
||||
});
|
||||
|
||||
test('`PluginsService.setup` calls loadPluginBundles with basePath and plugins', async () => {
|
||||
const pluginsService = new PluginsService(mockCoreContext);
|
||||
await pluginsService.setup(mockDeps);
|
||||
await pluginsService.setup(mockSetupDeps);
|
||||
|
||||
expect(mockLoadPluginBundle).toHaveBeenCalledTimes(3);
|
||||
expect(mockLoadPluginBundle).toHaveBeenCalledWith(mockDeps.basePath.addToPath, 'pluginA');
|
||||
expect(mockLoadPluginBundle).toHaveBeenCalledWith(mockDeps.basePath.addToPath, 'pluginB');
|
||||
expect(mockLoadPluginBundle).toHaveBeenCalledWith(mockDeps.basePath.addToPath, 'pluginC');
|
||||
expect(mockLoadPluginBundle).toHaveBeenCalledWith(mockSetupDeps.basePath.addToPath, 'pluginA');
|
||||
expect(mockLoadPluginBundle).toHaveBeenCalledWith(mockSetupDeps.basePath.addToPath, 'pluginB');
|
||||
expect(mockLoadPluginBundle).toHaveBeenCalledWith(mockSetupDeps.basePath.addToPath, 'pluginC');
|
||||
});
|
||||
|
||||
test('`PluginsService.setup` initalizes plugins with CoreContext', async () => {
|
||||
const pluginsService = new PluginsService(mockCoreContext);
|
||||
await pluginsService.setup(mockDeps);
|
||||
await pluginsService.setup(mockSetupDeps);
|
||||
|
||||
expect(mockPluginInitializers.get('pluginA')).toHaveBeenCalledWith(mockCoreContext);
|
||||
expect(mockPluginInitializers.get('pluginB')).toHaveBeenCalledWith(mockCoreContext);
|
||||
|
@ -147,50 +180,102 @@ test('`PluginsService.setup` initalizes plugins with CoreContext', async () => {
|
|||
|
||||
test('`PluginsService.setup` exposes dependent setup contracts to plugins', async () => {
|
||||
const pluginsService = new PluginsService(mockCoreContext);
|
||||
await pluginsService.setup(mockDeps);
|
||||
await pluginsService.setup(mockSetupDeps);
|
||||
|
||||
const pluginAInstance = mockPluginInitializers.get('pluginA')!.mock.results[0].value;
|
||||
const pluginBInstance = mockPluginInitializers.get('pluginB')!.mock.results[0].value;
|
||||
const pluginCInstance = mockPluginInitializers.get('pluginC')!.mock.results[0].value;
|
||||
|
||||
expect(pluginAInstance.setup).toHaveBeenCalledWith(mockInitContext, {});
|
||||
expect(pluginBInstance.setup).toHaveBeenCalledWith(mockInitContext, {
|
||||
pluginA: { exportedValue: 1 },
|
||||
expect(pluginAInstance.setup).toHaveBeenCalledWith(mockSetupContext, {});
|
||||
expect(pluginBInstance.setup).toHaveBeenCalledWith(mockSetupContext, {
|
||||
pluginA: { setupValue: 1 },
|
||||
});
|
||||
// Does not supply value for `nonexist` optional dep
|
||||
expect(pluginCInstance.setup).toHaveBeenCalledWith(mockInitContext, {
|
||||
pluginA: { exportedValue: 1 },
|
||||
expect(pluginCInstance.setup).toHaveBeenCalledWith(mockSetupContext, {
|
||||
pluginA: { setupValue: 1 },
|
||||
});
|
||||
});
|
||||
|
||||
test('`PluginsService.setup` does not set missing dependent setup contracts', async () => {
|
||||
mockDeps.injectedMetadata.getPlugins.mockReturnValue([
|
||||
mockSetupDeps.injectedMetadata.getPlugins.mockReturnValue([
|
||||
{ id: 'pluginD', plugin: createManifest('pluginD', { required: ['missing'] }) },
|
||||
]);
|
||||
mockPluginInitializers.set('pluginD', jest.fn(() => ({ setup: jest.fn() })) as any);
|
||||
mockPluginInitializers.set('pluginD', jest.fn(() => ({
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
})) as any);
|
||||
|
||||
const pluginsService = new PluginsService(mockCoreContext);
|
||||
await pluginsService.setup(mockDeps);
|
||||
await pluginsService.setup(mockSetupDeps);
|
||||
|
||||
// If a dependency is missing it should not be in the deps at all, not even as undefined.
|
||||
const pluginDInstance = mockPluginInitializers.get('pluginD')!.mock.results[0].value;
|
||||
expect(pluginDInstance.setup).toHaveBeenCalledWith(mockInitContext, {});
|
||||
expect(pluginDInstance.setup).toHaveBeenCalledWith(mockSetupContext, {});
|
||||
const pluginDDeps = pluginDInstance.setup.mock.calls[0][1];
|
||||
expect(pluginDDeps).not.toHaveProperty('missing');
|
||||
});
|
||||
|
||||
test('`PluginsService.setup` returns plugin setup contracts', async () => {
|
||||
const pluginsService = new PluginsService(mockCoreContext);
|
||||
const { contracts } = await pluginsService.setup(mockDeps);
|
||||
const { contracts } = await pluginsService.setup(mockSetupDeps);
|
||||
|
||||
// Verify that plugin contracts were available
|
||||
expect((contracts.get('pluginA')! as any).exportedValue).toEqual(1);
|
||||
expect((contracts.get('pluginA')! as any).setupValue).toEqual(1);
|
||||
expect((contracts.get('pluginB')! as any).pluginAPlusB).toEqual(2);
|
||||
});
|
||||
|
||||
test('`PluginsService.start` exposes dependent start contracts to plugins', async () => {
|
||||
const pluginsService = new PluginsService(mockCoreContext);
|
||||
await pluginsService.setup(mockSetupDeps);
|
||||
await pluginsService.start(mockStartDeps);
|
||||
|
||||
const pluginAInstance = mockPluginInitializers.get('pluginA')!.mock.results[0].value;
|
||||
const pluginBInstance = mockPluginInitializers.get('pluginB')!.mock.results[0].value;
|
||||
const pluginCInstance = mockPluginInitializers.get('pluginC')!.mock.results[0].value;
|
||||
|
||||
expect(pluginAInstance.start).toHaveBeenCalledWith(mockStartContext, {});
|
||||
expect(pluginBInstance.start).toHaveBeenCalledWith(mockStartContext, {
|
||||
pluginA: { startValue: 2 },
|
||||
});
|
||||
// Does not supply value for `nonexist` optional dep
|
||||
expect(pluginCInstance.start).toHaveBeenCalledWith(mockStartContext, {
|
||||
pluginA: { startValue: 2 },
|
||||
});
|
||||
});
|
||||
|
||||
test('`PluginsService.start` does not set missing dependent start contracts', async () => {
|
||||
mockSetupDeps.injectedMetadata.getPlugins.mockReturnValue([
|
||||
{ id: 'pluginD', plugin: createManifest('pluginD', { required: ['missing'] }) },
|
||||
]);
|
||||
mockPluginInitializers.set('pluginD', jest.fn(() => ({
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
})) as any);
|
||||
|
||||
const pluginsService = new PluginsService(mockCoreContext);
|
||||
await pluginsService.setup(mockSetupDeps);
|
||||
await pluginsService.start(mockStartDeps);
|
||||
|
||||
// If a dependency is missing it should not be in the deps at all, not even as undefined.
|
||||
const pluginDInstance = mockPluginInitializers.get('pluginD')!.mock.results[0].value;
|
||||
expect(pluginDInstance.start).toHaveBeenCalledWith(mockStartContext, {});
|
||||
const pluginDDeps = pluginDInstance.start.mock.calls[0][1];
|
||||
expect(pluginDDeps).not.toHaveProperty('missing');
|
||||
});
|
||||
|
||||
test('`PluginsService.start` returns plugin start contracts', async () => {
|
||||
const pluginsService = new PluginsService(mockCoreContext);
|
||||
await pluginsService.setup(mockSetupDeps);
|
||||
const { contracts } = await pluginsService.start(mockStartDeps);
|
||||
|
||||
// Verify that plugin contracts were available
|
||||
expect((contracts.get('pluginA')! as any).startValue).toEqual(2);
|
||||
expect((contracts.get('pluginB')! as any).pluginAPlusB).toEqual(3);
|
||||
});
|
||||
|
||||
test('`PluginService.stop` calls the stop function on each plugin', async () => {
|
||||
const pluginsService = new PluginsService(mockCoreContext);
|
||||
await pluginsService.setup(mockDeps);
|
||||
await pluginsService.setup(mockSetupDeps);
|
||||
|
||||
const pluginAInstance = mockPluginInitializers.get('pluginA')!.mock.results[0].value;
|
||||
const pluginBInstance = mockPluginInitializers.get('pluginB')!.mock.results[0].value;
|
||||
|
|
|
@ -17,15 +17,21 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { CoreSetup } from '..';
|
||||
import { CoreSetup, CoreStart } from '..';
|
||||
import { PluginName } from '../../server';
|
||||
import { CoreService } from '../../types';
|
||||
import { CoreContext } from '../core_system';
|
||||
import { PluginWrapper } from './plugin';
|
||||
import { createPluginInitializerContext, createPluginSetupContext } from './plugin_context';
|
||||
import {
|
||||
createPluginInitializerContext,
|
||||
createPluginSetupContext,
|
||||
createPluginStartContext,
|
||||
} from './plugin_context';
|
||||
|
||||
/** @internal */
|
||||
export type PluginsServiceSetupDeps = CoreSetup;
|
||||
/** @internal */
|
||||
export type PluginsServiceStartDeps = CoreStart;
|
||||
|
||||
/** @internal */
|
||||
export interface PluginsServiceSetup {
|
||||
|
@ -98,6 +104,41 @@ export class PluginsService implements CoreService<PluginsServiceSetup> {
|
|||
return { contracts };
|
||||
}
|
||||
|
||||
public async start(deps: PluginsServiceStartDeps) {
|
||||
// Setup each plugin with required and optional plugin contracts
|
||||
const contracts = new Map<string, unknown>();
|
||||
for (const [pluginName, plugin] of this.plugins.entries()) {
|
||||
const pluginDeps = new Set([
|
||||
...plugin.requiredPlugins,
|
||||
...plugin.optionalPlugins.filter(optPlugin => this.plugins.get(optPlugin)),
|
||||
]);
|
||||
|
||||
const pluginDepContracts = [...pluginDeps.keys()].reduce(
|
||||
(depContracts, dependencyName) => {
|
||||
// Only set if present. Could be absent if plugin does not have client-side code or is a
|
||||
// missing optional plugin.
|
||||
if (contracts.has(dependencyName)) {
|
||||
depContracts[dependencyName] = contracts.get(dependencyName);
|
||||
}
|
||||
|
||||
return depContracts;
|
||||
},
|
||||
{} as Record<PluginName, unknown>
|
||||
);
|
||||
|
||||
contracts.set(
|
||||
pluginName,
|
||||
await plugin.start(
|
||||
createPluginStartContext(this.coreContext, deps, plugin),
|
||||
pluginDepContracts
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Expose start contracts
|
||||
return { contracts };
|
||||
}
|
||||
|
||||
public async stop() {
|
||||
// Stop plugins in reverse topological order.
|
||||
for (const pluginName of this.satupPlugins.reverse()) {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import * as CSS from 'csstype';
|
||||
import { default } from 'react';
|
||||
import { Observable } from 'rxjs';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import * as Rx from 'rxjs';
|
||||
import { Toast } from '@elastic/eui';
|
||||
|
@ -29,7 +28,7 @@ export interface Capabilities {
|
|||
}
|
||||
|
||||
// @public
|
||||
export interface CapabilitiesSetup {
|
||||
export interface CapabilitiesStart {
|
||||
getCapabilities: () => Capabilities;
|
||||
}
|
||||
|
||||
|
@ -66,8 +65,6 @@ export interface CoreSetup {
|
|||
// (undocumented)
|
||||
basePath: BasePathSetup;
|
||||
// (undocumented)
|
||||
capabilities: CapabilitiesSetup;
|
||||
// (undocumented)
|
||||
chrome: ChromeSetup;
|
||||
// (undocumented)
|
||||
fatalErrors: FatalErrorsSetup;
|
||||
|
@ -80,11 +77,23 @@ export interface CoreSetup {
|
|||
// (undocumented)
|
||||
notifications: NotificationsSetup;
|
||||
// (undocumented)
|
||||
overlays: OverlaySetup;
|
||||
// (undocumented)
|
||||
uiSettings: UiSettingsSetup;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CoreStart {
|
||||
// (undocumented)
|
||||
capabilities: CapabilitiesStart;
|
||||
// (undocumented)
|
||||
i18n: I18nStart;
|
||||
// (undocumented)
|
||||
injectedMetadata: InjectedMetadataStart;
|
||||
// (undocumented)
|
||||
notifications: NotificationsStart;
|
||||
// (undocumented)
|
||||
overlays: OverlayStart;
|
||||
}
|
||||
|
||||
// @internal
|
||||
export class CoreSystem {
|
||||
constructor(params: Params);
|
||||
|
@ -93,6 +102,8 @@ export class CoreSystem {
|
|||
fatalErrors: import(".").FatalErrorsSetup;
|
||||
} | undefined>;
|
||||
// (undocumented)
|
||||
start(): Promise<void>;
|
||||
// (undocumented)
|
||||
stop(): void;
|
||||
}
|
||||
|
||||
|
@ -119,6 +130,9 @@ export interface I18nSetup {
|
|||
}) => JSX.Element;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type I18nStart = I18nSetup;
|
||||
|
||||
// @internal (undocumented)
|
||||
export interface InjectedMetadataParams {
|
||||
// (undocumented)
|
||||
|
@ -197,10 +211,19 @@ export interface InjectedMetadataSetup {
|
|||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type NotificationsSetup = ReturnType<NotificationsService['setup']>;
|
||||
export type InjectedMetadataStart = InjectedMetadataSetup;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface OverlaySetup {
|
||||
export interface NotificationsSetup {
|
||||
// (undocumented)
|
||||
toasts: ToastsApi;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type NotificationsStart = NotificationsSetup;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface OverlayStart {
|
||||
// (undocumented)
|
||||
openFlyout: (flyoutChildren: React.ReactNode, flyoutProps?: {
|
||||
closeButtonAriaLabel?: string;
|
||||
|
@ -209,15 +232,17 @@ export interface OverlaySetup {
|
|||
}
|
||||
|
||||
// @public
|
||||
export interface Plugin<TSetup, TPluginsSetup extends Record<string, unknown> = {}> {
|
||||
export interface Plugin<TSetup, TStart, TPluginsSetup extends Record<string, unknown> = {}, TPluginsStart extends Record<string, unknown> = {}> {
|
||||
// (undocumented)
|
||||
setup: (core: PluginSetupContext, plugins: TPluginsSetup) => TSetup | Promise<TSetup>;
|
||||
// (undocumented)
|
||||
start: (core: PluginStartContext, plugins: TPluginsStart) => TStart | Promise<TStart>;
|
||||
// (undocumented)
|
||||
stop?: () => void;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type PluginInitializer<TSetup, TPluginsSetup extends Record<string, unknown> = {}> = (core: PluginInitializerContext) => Plugin<TSetup, TPluginsSetup>;
|
||||
export type PluginInitializer<TSetup, TStart, TPluginsSetup extends Record<string, unknown> = {}, TPluginsStart extends Record<string, unknown> = {}> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
|
||||
|
||||
// @public
|
||||
export interface PluginInitializerContext {
|
||||
|
@ -245,7 +270,7 @@ export { Toast }
|
|||
export type ToastInput = string | Pick<Toast, Exclude<keyof Toast, 'id'>>;
|
||||
|
||||
// @public (undocumented)
|
||||
export class ToastsSetup {
|
||||
export class ToastsApi {
|
||||
// (undocumented)
|
||||
add(toastOrTitle: ToastInput): Toast;
|
||||
// (undocumented)
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
*/
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
|
||||
uiModules.get('kibana')
|
||||
.provider('dashboardConfig', () => {
|
||||
let hideWriteControls = !uiCapabilities.dashboard.showWriteControls;
|
||||
let hideWriteControls = !capabilities.get().dashboard.showWriteControls;
|
||||
|
||||
return {
|
||||
/**
|
||||
|
|
|
@ -21,7 +21,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder';
|
||||
|
||||
|
@ -88,7 +88,7 @@ export class DashboardAddPanel extends React.Component {
|
|||
)}
|
||||
/>
|
||||
</EuiFlyoutBody>
|
||||
{ uiCapabilities.visualize.save ? (
|
||||
{ capabilities.get().visualize.save ? (
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
|
|
|
@ -27,11 +27,13 @@ import {
|
|||
|
||||
jest.mock('ui/capabilities',
|
||||
() => ({
|
||||
uiCapabilities: {
|
||||
visualize: {
|
||||
show: true,
|
||||
save: true
|
||||
}
|
||||
capabilities: {
|
||||
get: () => ({
|
||||
visualize: {
|
||||
show: true,
|
||||
save: true
|
||||
}
|
||||
})
|
||||
}
|
||||
}), { virtual: true });
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import 'ui/directives/css_truncate';
|
|||
import 'ui/directives/field_name';
|
||||
import './string_progress_bar';
|
||||
import detailsHtml from './lib/detail_views/string.html';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import { uiModules } from 'ui/modules';
|
||||
const app = uiModules.get('apps/discover');
|
||||
|
||||
|
@ -77,7 +77,7 @@ app.directive('discoverField', function ($compile, i18n) {
|
|||
|
||||
};
|
||||
|
||||
$scope.canVisualize = uiCapabilities.visualize.show;
|
||||
$scope.canVisualize = capabilities.get().visualize.show;
|
||||
|
||||
$scope.toggleDisplay = function (field) {
|
||||
if (field.display) {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import '../doc_table';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EmbeddableFactory } from 'ui/embeddable';
|
||||
import {
|
||||
|
@ -63,7 +63,7 @@ export class SearchEmbeddableFactory extends EmbeddableFactory {
|
|||
onEmbeddableStateChanged: OnEmbeddableStateChanged
|
||||
) {
|
||||
const editUrl = this.getEditPath(id);
|
||||
const editable = uiCapabilities.discover.save as boolean;
|
||||
const editable = capabilities.get().discover.save as boolean;
|
||||
|
||||
// can't change this to be async / awayt, because an Anglular promise is expected to be returned.
|
||||
return this.searchLoader.get(id).then(savedObject => {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import { management } from 'ui/management';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import indexTemplate from './index.html';
|
||||
import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
|
||||
|
@ -45,7 +45,7 @@ function updateAdvancedSettings($scope, config, query) {
|
|||
<AdvancedSettings
|
||||
config={config}
|
||||
query={query}
|
||||
enableSaving={uiCapabilities.advancedSettings.save}
|
||||
enableSaving={capabilities.get().advancedSettings.save}
|
||||
/>
|
||||
</I18nContext>,
|
||||
node,
|
||||
|
|
|
@ -24,7 +24,7 @@ import 'ui/vis/editors/default/sidebar';
|
|||
import 'ui/visualize';
|
||||
import 'ui/collapsible_sidebar';
|
||||
import 'ui/query_bar';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import 'ui/search_bar';
|
||||
import 'ui/apply_filters';
|
||||
import 'ui/listen';
|
||||
|
@ -150,7 +150,7 @@ function VisEditor(
|
|||
dirty: !savedVis.id
|
||||
};
|
||||
|
||||
$scope.topNavMenu = [...(uiCapabilities.visualize.save ? [{
|
||||
$scope.topNavMenu = [...(capabilities.get().visualize.save ? [{
|
||||
key: i18n('kbn.topNavMenu.saveVisualizationButtonLabel', { defaultMessage: 'save' }),
|
||||
description: i18n('kbn.visualize.topNavMenu.saveVisualizationButtonAriaLabel', {
|
||||
defaultMessage: 'Save Visualization',
|
||||
|
@ -215,7 +215,7 @@ function VisEditor(
|
|||
showShareContextMenu({
|
||||
anchorElement,
|
||||
allowEmbed: true,
|
||||
allowShortUrl: uiCapabilities.visualize.createShortUrl,
|
||||
allowShortUrl: capabilities.get().visualize.createShortUrl,
|
||||
getUnhashableStates,
|
||||
objectId: savedVis.id,
|
||||
objectType: 'visualization',
|
||||
|
|
|
@ -23,7 +23,7 @@ import { EmbeddableFactory } from 'ui/embeddable';
|
|||
import { getVisualizeLoader } from 'ui/visualize/loader';
|
||||
|
||||
import { Legacy } from 'kibana';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import {
|
||||
EmbeddableInstanceConfiguration,
|
||||
OnEmbeddableStateChanged,
|
||||
|
@ -90,7 +90,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<VisualizationA
|
|||
) {
|
||||
const visId = panelMetadata.id;
|
||||
const editUrl = this.getEditPath(visId);
|
||||
const editable: boolean = uiCapabilities.visualize.save as boolean;
|
||||
const editable: boolean = capabilities.get().visualize.save as boolean;
|
||||
|
||||
const loader = await getVisualizeLoader();
|
||||
const savedObject = await this.savedVisualizations.get(visId);
|
||||
|
|
|
@ -21,7 +21,7 @@ import React, { Component, Fragment } from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import { TableListView } from './../../table_list_view';
|
||||
|
||||
import {
|
||||
|
@ -46,8 +46,8 @@ class VisualizeListingTableUi extends Component {
|
|||
// for data exploration purposes
|
||||
createItem={this.props.createItem}
|
||||
findItems={this.props.findItems}
|
||||
deleteItems={uiCapabilities.visualize.delete ? this.props.deleteItems : null}
|
||||
editItem={uiCapabilities.visualize.save ? this.props.editItem : null}
|
||||
deleteItems={capabilities.get().visualize.delete ? this.props.deleteItems : null}
|
||||
editItem={capabilities.get().visualize.save ? this.props.editItem : null}
|
||||
tableColumns={this.getTableColumns()}
|
||||
listingLimit={100}
|
||||
initialFilter={''}
|
||||
|
|
|
@ -43,7 +43,7 @@ import { CoreSystem } from '__kibanaCore__'
|
|||
const rootDomElement = document.createElement('div');
|
||||
document.body.appendChild(rootDomElement)
|
||||
|
||||
new CoreSystem({
|
||||
const coreSystem = new CoreSystem({
|
||||
injectedMetadata: {
|
||||
version: '1.2.3',
|
||||
buildNumber: 1234,
|
||||
|
@ -113,5 +113,11 @@ new CoreSystem({
|
|||
requireLegacyFiles: () => {
|
||||
${bundle.getRequires().join('\n ')}
|
||||
}
|
||||
}).setup()
|
||||
})
|
||||
|
||||
coreSystem
|
||||
.setup()
|
||||
.then(() => {
|
||||
return coreSystem.start();
|
||||
});
|
||||
`;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import { DocTitleProvider } from 'ui/doc_title';
|
||||
import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry';
|
||||
import { notify, fatalError, toastNotifications } from 'ui/notify';
|
||||
|
@ -269,7 +269,7 @@ app.controller('timelion', function (
|
|||
testId: 'timelionDocsButton',
|
||||
};
|
||||
|
||||
if (uiCapabilities.timelion.save) {
|
||||
if (capabilities.get().timelion.save) {
|
||||
return [newSheetAction, addSheetAction, saveSheetAction, deleteSheetAction, openSheetAction, optionsAction, helpAction];
|
||||
}
|
||||
return [newSheetAction, addSheetAction, openSheetAction, optionsAction, helpAction];
|
||||
|
|
|
@ -17,15 +17,28 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Capabilities as UICapabilities, CapabilitiesSetup } from '../../../../core/public';
|
||||
import { Capabilities as UICapabilities, CapabilitiesStart } from '../../../../core/public';
|
||||
|
||||
export { Capabilities as UICapabilities } from '../../../../core/public';
|
||||
export let uiCapabilities: UICapabilities = null!;
|
||||
export { UICapabilities };
|
||||
|
||||
export function __newPlatformInit__(capabililitiesService: CapabilitiesSetup) {
|
||||
let uiCapabilities: UICapabilities = null!;
|
||||
|
||||
export function __newPlatformStart__(capabililitiesService: CapabilitiesStart) {
|
||||
if (uiCapabilities) {
|
||||
throw new Error('ui/capabilities already initialized with new platform apis');
|
||||
}
|
||||
|
||||
uiCapabilities = capabililitiesService.getCapabilities();
|
||||
}
|
||||
|
||||
export const capabilities = {
|
||||
get() {
|
||||
if (!uiCapabilities) {
|
||||
throw new Error(
|
||||
`UI Capabilities are only available in the legacy platform once Angular has booted.`
|
||||
);
|
||||
}
|
||||
|
||||
return uiCapabilities;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
*/
|
||||
|
||||
jest.mock('ui/capabilities', () => ({
|
||||
uiCapabilities: {
|
||||
uiCapability1: true,
|
||||
uiCapability2: {
|
||||
nestedProp: 'nestedValue',
|
||||
},
|
||||
capabilities: {
|
||||
get: () => ({
|
||||
uiCapability1: true,
|
||||
uiCapability2: {
|
||||
nestedProp: 'nestedValue',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
*/
|
||||
|
||||
jest.mock('ui/capabilities', () => ({
|
||||
uiCapabilities: {
|
||||
uiCapability1: true,
|
||||
uiCapability2: {
|
||||
nestedProp: 'nestedValue',
|
||||
},
|
||||
capabilities: {
|
||||
get: () => ({
|
||||
uiCapability1: true,
|
||||
uiCapability2: {
|
||||
nestedProp: 'nestedValue',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { uiCapabilities, UICapabilities } from '../..';
|
||||
import { capabilities, UICapabilities } from '../..';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
|
@ -38,7 +38,7 @@ export class UICapabilitiesProvider extends React.Component<Props, {}> {
|
|||
|
||||
public getChildContext(): ProviderContext {
|
||||
return {
|
||||
uiCapabilities,
|
||||
uiCapabilities: capabilities.get(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
import { uiCapabilities } from '..';
|
||||
import { capabilities } from '..';
|
||||
import { UICapabilitiesContext } from './ui_capabilities_context';
|
||||
|
||||
interface Props {
|
||||
|
@ -30,7 +30,7 @@ export class UICapabilitiesProvider extends React.Component<Props, {}> {
|
|||
|
||||
public render() {
|
||||
return (
|
||||
<UICapabilitiesContext.Provider value={uiCapabilities}>
|
||||
<UICapabilitiesContext.Provider value={capabilities.get()}>
|
||||
{this.props.children}
|
||||
</UICapabilitiesContext.Provider>
|
||||
);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { basePathServiceMock } from '../../../../../core/public/mocks';
|
||||
import { __newPlatformInit__, initChromeBasePathApi } from './base_path';
|
||||
import { __newPlatformSetup__, initChromeBasePathApi } from './base_path';
|
||||
|
||||
function initChrome() {
|
||||
const chrome: any = {};
|
||||
|
@ -27,7 +27,7 @@ function initChrome() {
|
|||
}
|
||||
|
||||
const newPlatformBasePath = basePathServiceMock.createSetupContract();
|
||||
__newPlatformInit__(newPlatformBasePath);
|
||||
__newPlatformSetup__(newPlatformBasePath);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import { BasePathSetup } from '../../../../../core/public';
|
||||
let newPlatformBasePath: BasePathSetup;
|
||||
|
||||
export function __newPlatformInit__(instance: BasePathSetup) {
|
||||
export function __newPlatformSetup__(instance: BasePathSetup) {
|
||||
if (newPlatformBasePath) {
|
||||
throw new Error('ui/chrome/api/base_path is already initialized');
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ export type Breadcrumb = ChromeBreadcrumb;
|
|||
export type BreadcrumbsApi = ReturnType<typeof createBreadcrumbsApi>['breadcrumbs'];
|
||||
|
||||
let newPlatformChrome: ChromeSetup;
|
||||
export function __newPlatformInit__(instance: ChromeSetup) {
|
||||
export function __newPlatformSetup__(instance: ChromeSetup) {
|
||||
if (newPlatformChrome) {
|
||||
throw new Error('ui/chrome/api/breadcrumbs is already initialized');
|
||||
}
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
import * as Rx from 'rxjs';
|
||||
|
||||
import { chromeServiceMock } from '../../../../../core/public/mocks';
|
||||
import { __newPlatformInit__, initChromeControlsApi } from './controls';
|
||||
import { __newPlatformSetup__, initChromeControlsApi } from './controls';
|
||||
|
||||
const newPlatformChrome = chromeServiceMock.createSetupContract();
|
||||
|
||||
__newPlatformInit__(newPlatformChrome);
|
||||
__newPlatformSetup__(newPlatformChrome);
|
||||
|
||||
function setup() {
|
||||
const isVisible$ = new Rx.BehaviorSubject(true);
|
||||
|
|
|
@ -22,7 +22,7 @@ import { ChromeSetup } from '../../../../../core/public';
|
|||
|
||||
let newPlatformChrome: ChromeSetup;
|
||||
|
||||
export function __newPlatformInit__(instance: ChromeSetup) {
|
||||
export function __newPlatformSetup__(instance: ChromeSetup) {
|
||||
if (newPlatformChrome) {
|
||||
throw new Error('ui/chrome/api/controls is already initialized');
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import { ChromeHelpExtension, ChromeSetup } from '../../../../../core/public';
|
||||
|
||||
let newPlatformChrome: ChromeSetup;
|
||||
export function __newPlatformInit__(instance: ChromeSetup) {
|
||||
export function __newPlatformSetup__(instance: ChromeSetup) {
|
||||
if (newPlatformChrome) {
|
||||
throw new Error('ui/chrome/api/help_extension is already initialized');
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { __newPlatformInit__, initChromeInjectedVarsApi } from './injected_vars';
|
||||
import { __newPlatformSetup__, initChromeInjectedVarsApi } from './injected_vars';
|
||||
|
||||
function initChrome() {
|
||||
const chrome: any = {};
|
||||
|
@ -29,7 +29,7 @@ const newPlatformInjectedMetadata: any = {
|
|||
getInjectedVars: jest.fn(),
|
||||
getInjectedVar: jest.fn(),
|
||||
};
|
||||
__newPlatformInit__(newPlatformInjectedMetadata);
|
||||
__newPlatformSetup__(newPlatformInjectedMetadata);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
|
|
@ -22,7 +22,7 @@ import { InjectedMetadataSetup } from '../../../../../core/public';
|
|||
|
||||
let newPlatformInjectedVars: InjectedMetadataSetup;
|
||||
|
||||
export function __newPlatformInit__(instance: InjectedMetadataSetup) {
|
||||
export function __newPlatformSetup__(instance: InjectedMetadataSetup) {
|
||||
if (newPlatformInjectedVars) {
|
||||
throw new Error('ui/chrome/api/injected_vars is already initialized');
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import * as Rx from 'rxjs';
|
|||
|
||||
let newPlatformHttp;
|
||||
|
||||
export function __newPlatformInit__(instance) {
|
||||
export function __newPlatformSetup__(instance) {
|
||||
if (newPlatformHttp) {
|
||||
throw new Error('ui/chrome/api/loading_count already initialized with new platform apis');
|
||||
}
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
import * as Rx from 'rxjs';
|
||||
|
||||
import { chromeServiceMock } from '../../../../../core/public/mocks';
|
||||
import { __newPlatformInit__, initChromeThemeApi } from './theme';
|
||||
import { __newPlatformSetup__, initChromeThemeApi } from './theme';
|
||||
|
||||
const newPlatformChrome = chromeServiceMock.createSetupContract();
|
||||
|
||||
__newPlatformInit__(newPlatformChrome);
|
||||
__newPlatformSetup__(newPlatformChrome);
|
||||
|
||||
function setup() {
|
||||
const brand$ = new Rx.BehaviorSubject({ logo: 'foo', smallLogo: 'foo' });
|
||||
|
|
|
@ -23,7 +23,7 @@ import { ChromeBrand, ChromeSetup } from '../../../../../core/public';
|
|||
|
||||
let newPlatformChrome: ChromeSetup;
|
||||
|
||||
export function __newPlatformInit__(instance: ChromeSetup) {
|
||||
export function __newPlatformSetup__(instance: ChromeSetup) {
|
||||
if (newPlatformChrome) {
|
||||
throw new Error('ui/chrome/api/theme is already initialized');
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
let newPlatformUiSettingsClient;
|
||||
|
||||
export function __newPlatformInit__(instance) {
|
||||
export function __newPlatformSetup__(instance) {
|
||||
if (newPlatformUiSettingsClient) {
|
||||
throw new Error('ui/chrome/api/ui_settings already initialized');
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import { distinctUntilChanged } from 'rxjs/operators';
|
|||
import { uiModules } from '../../modules';
|
||||
|
||||
let newPlatformChrome;
|
||||
export function __newPlatformInit__(instance) {
|
||||
export function __newPlatformSetup__(instance) {
|
||||
if (newPlatformChrome) {
|
||||
throw new Error('ui/chrome/global_nav_state is already initialized');
|
||||
}
|
||||
|
|
|
@ -21,11 +21,11 @@ import { render } from 'enzyme';
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
import { __newPlatformInit__, wrapInI18nContext } from '.';
|
||||
import { __newPlatformSetup__, wrapInI18nContext } from '.';
|
||||
|
||||
describe('ui/i18n', () => {
|
||||
test('renders children and forwards properties', () => {
|
||||
__newPlatformInit__(({ children }) => <div>Context: {children}</div>);
|
||||
__newPlatformSetup__(({ children }) => <div>Context: {children}</div>);
|
||||
|
||||
const mockPropTypes = {
|
||||
stringProp: PropTypes.string.isRequired,
|
||||
|
|
|
@ -25,7 +25,7 @@ import { uiModules } from 'ui/modules';
|
|||
import { I18nSetup } from '../../../../core/public';
|
||||
|
||||
export let I18nContext: I18nSetup['Context'] = null!;
|
||||
export function __newPlatformInit__(context: typeof I18nContext) {
|
||||
export function __newPlatformSetup__(context: typeof I18nContext) {
|
||||
if (I18nContext) {
|
||||
throw new Error('ui/i18n already initialized with new platform apis');
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ function open(adapters: Adapters, options: InspectorOptions = {}): InspectorSess
|
|||
if an inspector can be shown.`);
|
||||
}
|
||||
|
||||
return getNewPlatform().setup.core.overlays.openFlyout(
|
||||
return getNewPlatform().start.core.overlays.openFlyout(
|
||||
<InspectorPanel views={views} adapters={adapters} title={options.title} />,
|
||||
{
|
||||
'data-test-subj': 'inspectorPanel',
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import { assign } from 'lodash';
|
||||
import { IndexedArray } from '../indexed_array';
|
||||
import { uiCapabilities } from '../capabilities';
|
||||
import { capabilities } from '../capabilities';
|
||||
|
||||
const listeners = [];
|
||||
|
||||
|
@ -56,7 +56,7 @@ export class ManagementSection {
|
|||
|
||||
get visibleItems() {
|
||||
return this.items.inOrder.filter(item => {
|
||||
const capabilityManagementSection = uiCapabilities.management[this.id];
|
||||
const capabilityManagementSection = capabilities.get().management[this.id];
|
||||
const itemCapability = capabilityManagementSection ? capabilityManagementSection[item.id] : null;
|
||||
|
||||
return item.visible && itemCapability !== false;
|
||||
|
|
|
@ -17,14 +17,16 @@
|
|||
* under the License.
|
||||
*/
|
||||
jest.mock('ui/capabilities', () => ({
|
||||
uiCapabilities: {
|
||||
navLinks: {},
|
||||
management: {
|
||||
kibana: {
|
||||
sampleFeature1: true,
|
||||
sampleFeature2: false,
|
||||
capabilities: {
|
||||
get: () => ({
|
||||
navLinks: {},
|
||||
management: {
|
||||
kibana: {
|
||||
sampleFeature1: true,
|
||||
sampleFeature2: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
export let metadata = null;
|
||||
|
||||
export function __newPlatformInit__(legacyMetadata) {
|
||||
export function __newPlatformSetup__(legacyMetadata) {
|
||||
if (metadata === null) {
|
||||
metadata = legacyMetadata;
|
||||
} else {
|
||||
|
|
|
@ -16,4 +16,4 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
export { __newPlatformInit__, getNewPlatform } from './new_platform';
|
||||
export { __newPlatformSetup__, __newPlatformStart__, getNewPlatform } from './new_platform';
|
||||
|
|
|
@ -16,26 +16,39 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { CoreSetup } from '../../../../core/public';
|
||||
import { CoreSetup, CoreStart } from '../../../../core/public';
|
||||
|
||||
const runtimeContext = {
|
||||
setup: {
|
||||
core: (null as unknown) as CoreSetup,
|
||||
plugins: {},
|
||||
},
|
||||
start: {
|
||||
core: (null as unknown) as CoreStart,
|
||||
plugins: {},
|
||||
},
|
||||
};
|
||||
|
||||
export function __newPlatformInit__(core: CoreSetup) {
|
||||
export function __newPlatformSetup__(core: CoreSetup) {
|
||||
if (runtimeContext.setup.core) {
|
||||
throw new Error('New platform core api was already initialized');
|
||||
throw new Error('New platform core api was already set up');
|
||||
}
|
||||
|
||||
runtimeContext.setup.core = core;
|
||||
}
|
||||
|
||||
export function __newPlatformStart__(core: CoreStart) {
|
||||
if (runtimeContext.start.core) {
|
||||
throw new Error('New platform core api was already started');
|
||||
}
|
||||
|
||||
runtimeContext.start.core = core;
|
||||
}
|
||||
|
||||
export function getNewPlatform() {
|
||||
if (runtimeContext.setup.core === null) {
|
||||
if (runtimeContext.setup.core === null || runtimeContext.start.core === null) {
|
||||
throw new Error('runtimeContext is not initialized yet');
|
||||
}
|
||||
|
||||
return runtimeContext;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import {
|
|||
|
||||
let newPlatformFatalErrors: FatalErrorsSetup;
|
||||
|
||||
export function __newPlatformInit__(instance: FatalErrorsSetup) {
|
||||
export function __newPlatformSetup__(instance: FatalErrorsSetup) {
|
||||
if (newPlatformFatalErrors) {
|
||||
throw new Error('ui/notify/fatal_error already initialized with new platform apis');
|
||||
}
|
||||
|
|
|
@ -17,5 +17,5 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { toastNotifications, __newPlatformInit__ } from './toasts';
|
||||
export { toastNotifications, __newPlatformSetup__ } from './toasts';
|
||||
export { Toast, ToastInput } from './toast_notifications';
|
||||
|
|
|
@ -18,14 +18,14 @@
|
|||
*/
|
||||
|
||||
import sinon from 'sinon';
|
||||
import { ToastsSetup } from '../../../../../core/public';
|
||||
import { ToastsApi } from '../../../../../core/public';
|
||||
|
||||
import { ToastNotifications } from './toast_notifications';
|
||||
|
||||
describe('ToastNotifications', () => {
|
||||
describe('interface', () => {
|
||||
function setup() {
|
||||
return { toastNotifications: new ToastNotifications(new ToastsSetup()) };
|
||||
return { toastNotifications: new ToastNotifications(new ToastsApi()) };
|
||||
}
|
||||
|
||||
describe('add method', () => {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Toast, ToastInput, ToastsSetup } from '../../../../../core/public';
|
||||
import { Toast, ToastInput, ToastsApi } from '../../../../../core/public';
|
||||
|
||||
export { Toast, ToastInput };
|
||||
|
||||
|
@ -26,7 +26,7 @@ export class ToastNotifications {
|
|||
|
||||
private onChangeCallback?: () => void;
|
||||
|
||||
constructor(private readonly toasts: ToastsSetup) {
|
||||
constructor(private readonly toasts: ToastsApi) {
|
||||
toasts.get$().subscribe(list => {
|
||||
this.list = list;
|
||||
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { ToastsSetup } from '../../../../../core/public';
|
||||
import { ToastsApi } from '../../../../../core/public';
|
||||
import { ToastNotifications } from './toast_notifications';
|
||||
|
||||
export let toastNotifications: ToastNotifications;
|
||||
|
||||
export function __newPlatformInit__(toasts: ToastsSetup) {
|
||||
export function __newPlatformSetup__(toasts: ToastsApi) {
|
||||
if (toastNotifications) {
|
||||
throw new Error('ui/notify/toasts already initialized with new platform apis');
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { uiRegistry } from './_registry';
|
||||
import { uiCapabilities } from '../capabilities';
|
||||
import { capabilities } from '../capabilities';
|
||||
|
||||
export const FeatureCatalogueRegistryProvider = uiRegistry({
|
||||
name: 'featureCatalogue',
|
||||
|
@ -26,7 +26,7 @@ export const FeatureCatalogueRegistryProvider = uiRegistry({
|
|||
group: ['category'],
|
||||
order: ['title'],
|
||||
filter: featureCatalogItem => {
|
||||
const isDisabledViaCapabilities = uiCapabilities.catalogue[featureCatalogItem.id] === false;
|
||||
const isDisabledViaCapabilities = capabilities.get().catalogue[featureCatalogItem.id] === false;
|
||||
return !isDisabledViaCapabilities && Object.keys(featureCatalogItem).length > 0;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -17,14 +17,16 @@
|
|||
* under the License.
|
||||
*/
|
||||
jest.mock('ui/capabilities', () => ({
|
||||
uiCapabilities: {
|
||||
navLinks: {},
|
||||
management: {},
|
||||
catalogue: {
|
||||
item1: true,
|
||||
item2: false,
|
||||
item3: true,
|
||||
},
|
||||
capabilities: {
|
||||
get: () => ({
|
||||
navLinks: {},
|
||||
management: {},
|
||||
catalogue: {
|
||||
item1: true,
|
||||
item2: false,
|
||||
item3: true,
|
||||
},
|
||||
}),
|
||||
}
|
||||
}));
|
||||
import { FeatureCatalogueCategory, FeatureCatalogueRegistryProvider } from './feature_catalogue';
|
||||
|
|
|
@ -62,6 +62,8 @@ i18n.load(injectedMetadata.i18n.translationsUrl)
|
|||
if (i18nError) {
|
||||
coreSetup.fatalErrors.add(i18nError);
|
||||
}
|
||||
|
||||
return coreSystem.start();
|
||||
});
|
||||
});
|
||||
`;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import { PluginInitializer } from 'kibana/public';
|
||||
import { TestbedPlugin, TestbedPluginSetup } from './plugin';
|
||||
import { TestbedPlugin, TestbedPluginSetup, TestbedPluginStart } from './plugin';
|
||||
|
||||
export const plugin: PluginInitializer<TestbedPluginSetup> = () => new TestbedPlugin();
|
||||
export const plugin: PluginInitializer<TestbedPluginSetup, TestbedPluginStart> = () =>
|
||||
new TestbedPlugin();
|
||||
|
|
|
@ -19,11 +19,17 @@
|
|||
|
||||
import { Plugin, PluginSetupContext } from 'kibana/public';
|
||||
|
||||
export class TestbedPlugin implements Plugin<TestbedPluginSetup> {
|
||||
export class TestbedPlugin implements Plugin<TestbedPluginSetup, TestbedPluginStart> {
|
||||
public setup(core: PluginSetupContext, deps: {}) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Testbed plugin loaded`);
|
||||
console.log(`Testbed plugin set up`);
|
||||
}
|
||||
|
||||
public start() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Testbed plugin started`);
|
||||
}
|
||||
}
|
||||
|
||||
export type TestbedPluginSetup = ReturnType<TestbedPlugin['setup']>;
|
||||
export type TestbedPluginStart = ReturnType<TestbedPlugin['start']>;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import { PluginInitializer } from 'kibana/public';
|
||||
import { CorePluginAPlugin, CorePluginAPluginSetup } from './plugin';
|
||||
import { CorePluginAPlugin, CorePluginAPluginSetup, CorePluginAPluginStart } from './plugin';
|
||||
|
||||
export const plugin: PluginInitializer<CorePluginAPluginSetup> = () => new CorePluginAPlugin();
|
||||
export const plugin: PluginInitializer<CorePluginAPluginSetup, CorePluginAPluginStart> = () =>
|
||||
new CorePluginAPlugin();
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import { Plugin, PluginSetupContext } from 'kibana/public';
|
||||
|
||||
export class CorePluginAPlugin implements Plugin<CorePluginAPluginSetup> {
|
||||
export class CorePluginAPlugin implements Plugin<CorePluginAPluginSetup, CorePluginAPluginStart> {
|
||||
public setup(core: PluginSetupContext, deps: {}) {
|
||||
return {
|
||||
getGreeting() {
|
||||
|
@ -27,6 +27,9 @@ export class CorePluginAPlugin implements Plugin<CorePluginAPluginSetup> {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
public start() {}
|
||||
}
|
||||
|
||||
export type CorePluginAPluginSetup = ReturnType<CorePluginAPlugin['setup']>;
|
||||
export type CorePluginAPluginStart = ReturnType<CorePluginAPlugin['start']>;
|
||||
|
|
|
@ -18,7 +18,15 @@
|
|||
*/
|
||||
|
||||
import { PluginInitializer } from 'kibana/public';
|
||||
import { CorePluginBDeps, CorePluginBPlugin, CorePluginBPluginSetup } from './plugin';
|
||||
import {
|
||||
CorePluginBDeps,
|
||||
CorePluginBPlugin,
|
||||
CorePluginBPluginSetup,
|
||||
CorePluginBPluginStart,
|
||||
} from './plugin';
|
||||
|
||||
export const plugin: PluginInitializer<CorePluginBPluginSetup, CorePluginBDeps> = () =>
|
||||
new CorePluginBPlugin();
|
||||
export const plugin: PluginInitializer<
|
||||
CorePluginBPluginSetup,
|
||||
CorePluginBPluginStart,
|
||||
CorePluginBDeps
|
||||
> = () => new CorePluginBPlugin();
|
||||
|
|
|
@ -30,10 +30,14 @@ export interface CorePluginBDeps {
|
|||
core_plugin_a: CorePluginAPluginSetup;
|
||||
}
|
||||
|
||||
export class CorePluginBPlugin implements Plugin<CorePluginBPluginSetup, CorePluginBDeps> {
|
||||
export class CorePluginBPlugin
|
||||
implements Plugin<CorePluginBPluginSetup, CorePluginBPluginStart, CorePluginBDeps> {
|
||||
public setup(core: PluginSetupContext, deps: CorePluginBDeps) {
|
||||
window.corePluginB = `Plugin A said: ${deps.core_plugin_a.getGreeting()}`;
|
||||
}
|
||||
|
||||
public start() {}
|
||||
}
|
||||
|
||||
export type CorePluginBPluginSetup = ReturnType<CorePluginBPlugin['setup']>;
|
||||
export type CorePluginBPluginStart = ReturnType<CorePluginBPlugin['start']>;
|
||||
|
|
|
@ -38,7 +38,7 @@ class SamplePanelAction extends ContextMenuAction {
|
|||
if (!embeddable) {
|
||||
return;
|
||||
}
|
||||
getNewPlatform().setup.core.overlays.openFlyout(
|
||||
getNewPlatform().start.core.overlays.openFlyout(
|
||||
<React.Fragment>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle size="s" data-test-subj="samplePanelActionTitle">
|
||||
|
|
|
@ -15,14 +15,17 @@ let internals: UICapabilities = {
|
|||
},
|
||||
};
|
||||
|
||||
export const uiCapabilities = new Proxy(
|
||||
{},
|
||||
{
|
||||
get: (target, property) => {
|
||||
return internals[String(property)] as any;
|
||||
},
|
||||
}
|
||||
);
|
||||
export const capabilities = {
|
||||
get: () =>
|
||||
new Proxy(
|
||||
{},
|
||||
{
|
||||
get: (target, property) => {
|
||||
return internals[String(property)] as any;
|
||||
},
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
export function setMockCapabilities(mockCapabilities: UICapabilities) {
|
||||
internals = mockCapabilities;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import { getDefaultWorkpad } from './defaults';
|
||||
|
||||
export const getInitialState = path => {
|
||||
|
@ -13,7 +13,7 @@ export const getInitialState = path => {
|
|||
app: {}, // Kibana stuff in here
|
||||
assets: {}, // assets end up here
|
||||
transient: {
|
||||
canUserWrite: uiCapabilities.canvas.save,
|
||||
canUserWrite: capabilities.get().canvas.save,
|
||||
elementStats: {
|
||||
total: 0,
|
||||
ready: 0,
|
||||
|
|
|
@ -47,7 +47,7 @@ import {
|
|||
import {
|
||||
getOutlinkEncoders,
|
||||
} from './services/outlink_encoders';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
|
||||
const app = uiModules.get('app/graph');
|
||||
|
||||
|
@ -804,7 +804,7 @@ app.controller('graphuiPlugin', function ($scope, $route, $http, kbnUrl, Private
|
|||
|
||||
// if saving is disabled using uiCapabilities, we don't want to render the save
|
||||
// button so it's consistent with all of the other applications
|
||||
if (uiCapabilities.graph.save) {
|
||||
if (capabilities.get().graph.save) {
|
||||
// allSavingDisabled is based on the xpack.graph.savePolicy, we'll maintain this functionality
|
||||
if (!$scope.allSavingDisabled) {
|
||||
$scope.topNavMenu.push({
|
||||
|
@ -855,7 +855,7 @@ app.controller('graphuiPlugin', function ($scope, $route, $http, kbnUrl, Private
|
|||
});
|
||||
// if deleting is disabled using uiCapabilities, we don't want to render the delete
|
||||
// button so it's consistent with all of the other applications
|
||||
if (uiCapabilities.graph.delete) {
|
||||
if (capabilities.get().graph.delete) {
|
||||
|
||||
// allSavingDisabled is based on the xpack.graph.savePolicy, we'll maintain this functionality
|
||||
if (!$scope.allSavingDisabled) {
|
||||
|
|
|
@ -10,7 +10,7 @@ import 'ui/listen';
|
|||
import React from 'react';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
@ -127,7 +127,7 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage
|
|||
// clear old UI state
|
||||
store.dispatch(setSelectedLayer(null));
|
||||
store.dispatch(updateFlyout(FLYOUT_STATE.NONE));
|
||||
store.dispatch(setReadOnly(!uiCapabilities.maps.save));
|
||||
store.dispatch(setReadOnly(!capabilities.get().maps.save));
|
||||
|
||||
handleStoreChanges(store);
|
||||
unsubscribe = store.subscribe(() => {
|
||||
|
@ -297,7 +297,7 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage
|
|||
const inspectorAdapters = getInspectorAdapters(store.getState());
|
||||
Inspector.open(inspectorAdapters, {});
|
||||
}
|
||||
}, ...(uiCapabilities.maps.save ? [{
|
||||
}, ...(capabilities.get().maps.save ? [{
|
||||
key: i18n.translate('xpack.maps.mapController.saveMapButtonLabel', {
|
||||
defaultMessage: `save`
|
||||
}),
|
||||
|
|
|
@ -14,7 +14,7 @@ import 'uiExports/search';
|
|||
import 'uiExports/embeddableFactories';
|
||||
import 'ui/agg_types';
|
||||
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import chrome from 'ui/chrome';
|
||||
import routes from 'ui/routes';
|
||||
import 'ui/kbn_top_nav';
|
||||
|
@ -53,7 +53,7 @@ routes
|
|||
$scope.delete = (ids) => {
|
||||
return gisMapSavedObjectLoader.delete(ids);
|
||||
};
|
||||
$scope.readOnly = !uiCapabilities.maps.save;
|
||||
$scope.readOnly = !capabilities.get().maps.save;
|
||||
},
|
||||
resolve: {
|
||||
hasMaps: function (kbnUrl) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
import routes from 'ui/routes';
|
||||
import { uiCapabilities } from 'ui/capabilities';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { fatalError } from 'ui/notify';
|
||||
import template from 'plugins/security/views/management/edit_role/edit_role.html';
|
||||
|
@ -146,7 +146,7 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, {
|
|||
allowFieldLevelSecurity={allowFieldLevelSecurity}
|
||||
spaces={spaces}
|
||||
spacesEnabled={enableSpaceAwarePrivileges}
|
||||
uiCapabilities={uiCapabilities}
|
||||
uiCapabilities={capabilities.get()}
|
||||
features={features}
|
||||
privileges={privileges}
|
||||
/>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue