[KibanaLegacy] Remove unused stuff and make things async if it is easy (#104638)

* remove unused stuff and make things async if it is easy

* fix problems

* load bootstrap in monitoring

* load angular bootstrap for saved searches and in  unit tests

* fix vis_type_table tests

* Update x-pack/plugins/monitoring/public/plugin.ts

Co-authored-by: Ester Martí Vilaseca <ester.m87@gmail.com>

* Update x-pack/plugins/monitoring/public/plugin.ts

Co-authored-by: Ester Martí Vilaseca <ester.m87@gmail.com>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Stratoula Kalafateli <stratoula1@gmail.com>
Co-authored-by: Ester Martí Vilaseca <ester.m87@gmail.com>
This commit is contained in:
Joe Reuter 2021-07-12 14:13:42 +02:00 committed by GitHub
parent 216bb5e1b8
commit 832d349930
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 54 additions and 286 deletions

View file

@ -19,6 +19,7 @@ import { setScopedHistory, setServices, setDocViewsRegistry } from '../../../../
import { coreMock } from '../../../../../../../core/public/mocks';
import { dataPluginMock } from '../../../../../../data/public/mocks';
import { navigationPluginMock } from '../../../../../../navigation/public/mocks';
import { initAngularBootstrap } from '../../../../../../kibana_legacy/public/angular_bootstrap';
import { getInnerAngularModule } from '../../get_inner_angular';
import { createBrowserHistory } from 'history';
@ -41,6 +42,9 @@ describe('Doc Table', () => {
// Stub out a minimal mapping of 4 fields
let mapping;
beforeAll(async () => {
await initAngularBootstrap();
});
beforeAll(() => setScopedHistory(createBrowserHistory()));
beforeEach(() => {
angular.element.prototype.slice = jest.fn(function (index) {

View file

@ -17,6 +17,7 @@ import hits from '../../../__fixtures__/real_hits';
import { coreMock } from '../../../../../../core/public/mocks';
import { dataPluginMock } from '../../../../../data/public/mocks';
import { navigationPluginMock } from '../../../../../navigation/public/mocks';
import { initAngularBootstrap } from '../../../../../kibana_legacy/public/angular_bootstrap';
import { setScopedHistory, setServices } from '../../../kibana_services';
import { getInnerAngularModule } from '../get_inner_angular';
@ -54,6 +55,9 @@ describe('docTable', () => {
const core = coreMock.createStart();
let $elem;
beforeAll(async () => {
await initAngularBootstrap();
});
beforeAll(() => setScopedHistory(createBrowserHistory()));
beforeEach(() => {
angular.element.prototype.slice = jest.fn(() => {

View file

@ -33,13 +33,12 @@ import { createDocViewerDirective } from './doc_viewer';
import { createDiscoverGridDirective } from './create_discover_grid_directive';
import { createRenderCompleteDirective } from './directives/render_complete';
import {
initAngularBootstrap,
configureAppAngularModule,
PrivateProvider,
PromiseServiceCreator,
registerListenEventListener,
watchMultiDecorator,
} from '../../../../kibana_legacy/public';
import { PromiseServiceCreator } from './helpers';
import { DiscoverStartPlugins } from '../../plugin';
import { getScopedHistory } from '../../kibana_services';
import { createDiscoverDirective } from './create_discover_directive';
@ -54,7 +53,6 @@ export function getInnerAngularModule(
deps: DiscoverStartPlugins,
context: PluginInitializerContext
) {
initAngularBootstrap();
const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data);
configureAppAngularModule(module, { core, env: context.env }, true, getScopedHistory);
return module;

View file

@ -8,3 +8,4 @@
export { formatRow, formatTopLevelObject } from './row_formatter';
export { handleSourceColumnState } from './state_helpers';
export { PromiseServiceCreator } from './promises';

View file

@ -403,6 +403,7 @@ export class DiscoverPlugin
}
// this is used by application mount and tests
const { getInnerAngularModule } = await import('./application/angular/get_inner_angular');
await plugins.kibanaLegacy.loadAngularBootstrap();
const module = getInnerAngularModule(
innerAngularName,
core,
@ -473,6 +474,7 @@ export class DiscoverPlugin
throw Error('Discover plugin getEmbeddableInjector: initializeServices is undefined');
}
const { core, plugins } = await this.initializeServices();
await getServices().kibanaLegacy.loadAngularBootstrap();
getServices().kibanaLegacy.loadFontAwesome();
const { getInnerAngularModuleEmbeddable } = await import(
'./application/angular/get_inner_angular'

View file

@ -13,6 +13,7 @@ import {
ILocationProvider,
IModule,
IRootScopeService,
IRequestConfig,
} from 'angular';
import $ from 'jquery';
import { set } from '@elastic/safer-lodash-set';
@ -22,7 +23,6 @@ import { ChromeBreadcrumb, EnvironmentMode, PackageInfo } from 'kibana/public';
import { History } from 'history';
import { CoreStart } from 'kibana/public';
import { isSystemApiRequest } from '../utils';
import { formatAngularHttpError, isAngularHttpError } from '../notify/lib';
export interface RouteConfiguration {
@ -38,6 +38,11 @@ export interface RouteConfiguration {
requireUICapability?: string;
}
function isSystemApiRequest(request: IRequestConfig) {
const { headers } = request;
return headers && !!headers['kbn-system-request'];
}
/**
* Detects whether a given angular route is a dummy route that doesn't
* require any action. There are two ways this can happen:

View file

@ -6,8 +6,6 @@
* Side Public License, v 1.
*/
// @ts-ignore
export { PromiseServiceCreator } from './promises';
// @ts-ignore
export { watchMultiDecorator } from './watch_multi';
export * from './angular_config';

View file

@ -14,7 +14,6 @@ export const plugin = (initializerContext: PluginInitializerContext) =>
export * from './plugin';
export { initAngularBootstrap } from './angular_bootstrap';
export { PaginateDirectiveProvider, PaginateControlsDirectiveProvider } from './paginate/paginate';
export * from './angular';
export * from './notify';

View file

@ -22,6 +22,7 @@ const createStartContract = (): Start => ({
getHideWriteControls: jest.fn(),
},
loadFontAwesome: jest.fn(),
loadAngularBootstrap: jest.fn(),
});
export const kibanaLegacyPluginMock = {

View file

@ -6,5 +6,4 @@
* Side Public License, v 1.
*/
export * from './toasts';
export * from './lib';

View file

@ -1,100 +0,0 @@
# Toast notifications
Use this service to surface toasts in the bottom-right corner of the screen. After a brief delay, they'll disappear. They're useful for notifying the user of state changes. See [the EUI docs](https://elastic.github.io/eui/) for more information on toasts and their role within the UI.
## Importing the module
```js
import { toastNotifications } from 'ui/notify';
```
## Interface
### Adding toasts
For convenience, there are several methods which predefine the appearance of different types of toasts. Use these methods so that the same types of toasts look similar to the user.
#### Default
Neutral toast. Tell the user a change in state has occurred, which is not necessarily good or bad.
```js
toastNotifications.add('Copied to clipboard');
```
#### Success
Let the user know that an action was successful, such as saving or deleting an object.
```js
toastNotifications.addSuccess('Your document was saved');
```
#### Warning
If something OK or good happened, but perhaps wasn't perfect, show a warning toast.
```js
toastNotifications.addWarning('Your document was saved, but not its edit history');
```
#### Danger
When the user initiated an action but the action failed, show them a danger toast.
```js
toastNotifications.addDanger('An error caused your document to be lost');
```
### Removing a toast
Toasts will automatically be dismissed after a brief delay, but if for some reason you want to dismiss a toast, you can use the returned toast from one of the `add` methods and then pass it to `remove`.
```js
const toast = toastNotifications.add('Your document was saved');
toastNotifications.remove(toast);
```
### Configuration options
If you want to configure the toast further you can provide an object instead of a string. The properties of this object correspond to the `propTypes` accepted by the `EuiToast` component. Refer to [the EUI docs](https://elastic.github.io/eui/) for info on these `propTypes`.
```js
toastNotifications.add({
title: 'Your document was saved',
text: 'Only you have access to this document',
color: 'success',
iconType: 'check',
'data-test-subj': 'saveDocumentSuccess',
});
```
Because the underlying components are React, you can use JSX to pass in React elements to the `text` prop. This gives you total flexibility over the content displayed within the toast.
```js
toastNotifications.add({
title: 'Your document was saved',
text: (
<div>
<p>
Only you have access to this document. <a href="/documents">Edit permissions.</a>
</p>
<button onClick={() => deleteDocument()}>
Delete document
</button>
</div>
),
});
```
## Use in functional tests
Functional tests are commonly used to verify that a user action yielded a successful outcome. If you surface a toast to notify the user of this successful outcome, you can place a `data-test-subj` attribute on the toast and use it to check if the toast exists inside of your functional test. This acts as a proxy for verifying the successful outcome.
```js
toastNotifications.addSuccess({
title: 'Your document was saved',
'data-test-subj': 'saveDocumentSuccess',
});
```

View file

@ -1,9 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export { ToastNotifications } from './toast_notifications';

View file

@ -1,76 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { notificationServiceMock } from '../../../../../core/public/mocks';
import { ToastNotifications } from './toast_notifications';
import { Toast } from 'kibana/public';
import { BehaviorSubject } from 'rxjs';
describe('ToastNotifications', () => {
describe('interface', () => {
function setup() {
const toastsMock = notificationServiceMock.createStartContract().toasts;
return { toastNotifications: new ToastNotifications(toastsMock), toastsMock };
}
describe('add method', () => {
test('adds a toast', () => {
const { toastNotifications, toastsMock } = setup();
toastNotifications.add({});
expect(toastsMock.add).toHaveBeenCalled();
});
});
describe('remove method', () => {
test('removes a toast', () => {
const { toastNotifications, toastsMock } = setup();
const fakeToast = {} as Toast;
toastNotifications.remove(fakeToast);
expect(toastsMock.remove).toHaveBeenCalledWith(fakeToast);
});
});
describe('onChange method', () => {
test('callback is called when observable changes', () => {
const toastsMock = notificationServiceMock.createStartContract().toasts;
const toasts$ = new BehaviorSubject<any>([]);
toastsMock.get$.mockReturnValue(toasts$);
const toastNotifications = new ToastNotifications(toastsMock);
const onChangeSpy = jest.fn();
toastNotifications.onChange(onChangeSpy);
toasts$.next([{ id: 'toast1' }]);
toasts$.next([]);
expect(onChangeSpy).toHaveBeenCalledTimes(2);
});
});
describe('addSuccess method', () => {
test('adds a success toast', () => {
const { toastNotifications, toastsMock } = setup();
toastNotifications.addSuccess({});
expect(toastsMock.addSuccess).toHaveBeenCalled();
});
});
describe('addWarning method', () => {
test('adds a warning toast', () => {
const { toastNotifications, toastsMock } = setup();
toastNotifications.addWarning({});
expect(toastsMock.addWarning).toHaveBeenCalled();
});
});
describe('addDanger method', () => {
test('adds a danger toast', () => {
const { toastNotifications, toastsMock } = setup();
toastNotifications.addWarning({});
expect(toastsMock.addWarning).toHaveBeenCalled();
});
});
});
});

View file

@ -1,37 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { NotificationsSetup, Toast, ToastInput, ErrorToastOptions } from 'kibana/public';
export class ToastNotifications {
public list: Toast[] = [];
private onChangeCallback?: () => void;
constructor(private readonly toasts: NotificationsSetup['toasts']) {
toasts.get$().subscribe((list) => {
this.list = list;
if (this.onChangeCallback) {
this.onChangeCallback();
}
});
}
public onChange = (callback: () => void) => {
this.onChangeCallback = callback;
};
public add = (toastOrTitle: ToastInput) => this.toasts.add(toastOrTitle);
public remove = (toast: Toast) => this.toasts.remove(toast);
public addSuccess = (toastOrTitle: ToastInput) => this.toasts.addSuccess(toastOrTitle);
public addWarning = (toastOrTitle: ToastInput) => this.toasts.addWarning(toastOrTitle);
public addDanger = (toastOrTitle: ToastInput) => this.toasts.addDanger(toastOrTitle);
public addError = (error: Error, options: ErrorToastOptions) =>
this.toasts.addError(error, options);
}

View file

@ -33,6 +33,14 @@ export class KibanaLegacyPlugin {
loadFontAwesome: async () => {
await import('./font_awesome');
},
/**
* Loads angular bootstrap modules. Should be removed once the last consumer has migrated to EUI
* @deprecated
*/
loadAngularBootstrap: async () => {
const { initAngularBootstrap } = await import('./angular_bootstrap');
initAngularBootstrap();
},
/**
* @deprecated
* Just exported for wiring up with dashboard mode, should not be used.

View file

@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
export * from './system_api';
// @ts-ignore
export { KbnAccessibleClickProvider } from './kbn_accessible_click';
// @ts-ignore

View file

@ -1,40 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { IRequestConfig } from 'angular';
const SYSTEM_REQUEST_HEADER_NAME = 'kbn-system-request';
const LEGACY_SYSTEM_API_HEADER_NAME = 'kbn-system-api';
/**
* Adds a custom header designating request as system API
* @param originalHeaders Object representing set of headers
* @return Object representing set of headers, with system API header added in
*/
export function addSystemApiHeader(originalHeaders: Record<string, string>) {
const systemApiHeaders = {
[SYSTEM_REQUEST_HEADER_NAME]: true,
};
return {
...originalHeaders,
...systemApiHeaders,
};
}
/**
* Returns true if request is a system API request; false otherwise
*
* @param request Object Request object created by $http service
* @return true if request is a system API request; false otherwise
*/
export function isSystemApiRequest(request: IRequestConfig) {
const { headers } = request;
return (
headers && (!!headers[SYSTEM_REQUEST_HEADER_NAME] || !!headers[LEGACY_SYSTEM_API_HEADER_NAME])
);
}

View file

@ -19,7 +19,7 @@ import {
AppNavLinkStatus,
} from '../../../core/public';
import { Panel } from './panels/panel';
import { initAngularBootstrap } from '../../kibana_legacy/public';
import { KibanaLegacyStart } from '../../kibana_legacy/public';
import { createKbnUrlTracker } from '../../kibana_utils/public';
import { DataPublicPluginStart, esFilters, DataPublicPluginSetup } from '../../data/public';
import { NavigationPublicPluginStart } from '../../navigation/public';
@ -41,6 +41,7 @@ export interface TimelionPluginStartDependencies {
visualizations: VisualizationsStart;
visTypeTimelion: VisTypeTimelionPluginStart;
savedObjects: SavedObjectsStart;
kibanaLegacy: KibanaLegacyStart;
}
/** @internal */
@ -91,7 +92,6 @@ export class TimelionPlugin
stopUrlTracker();
};
initAngularBootstrap();
core.application.register({
id: 'timelion',
title: 'Timelion',
@ -103,6 +103,7 @@ export class TimelionPlugin
visTypeTimelion.isUiEnabled === false ? AppNavLinkStatus.hidden : AppNavLinkStatus.default,
mount: async (params: AppMountParameters) => {
const [coreStart, pluginsStart] = await core.getStartServices();
await pluginsStart.kibanaLegacy.loadAngularBootstrap();
this.currentHistory = params.history;
appMounted();

View file

@ -15,7 +15,7 @@ import { round } from 'lodash';
import { getFieldFormatsRegistry } from '../../../../data/public/test_utils';
import { coreMock } from '../../../../../core/public/mocks';
import { initAngularBootstrap } from '../../../../kibana_legacy/public';
import { initAngularBootstrap } from '../../../../kibana_legacy/public/angular_bootstrap';
import { setUiSettings } from '../../../../data/public/services';
import { UI_SETTINGS } from '../../../../data/public/';
import { CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING } from '../../../../share/public';
@ -60,10 +60,12 @@ describe('Table Vis - AggTable Directive', function () {
initTableVisLegacyModule(tableVisModule);
};
beforeAll(async () => {
await initAngularBootstrap();
});
beforeEach(() => {
setUiSettings(core.uiSettings);
setFormatService(getFieldFormatsRegistry(core));
initAngularBootstrap();
initLocalAngular();
angular.mock.module('kibana/table_vis');
angular.mock.inject(($injector, config) => {

View file

@ -13,11 +13,11 @@ import expect from '@kbn/expect';
import { getFieldFormatsRegistry } from '../../../../data/public/test_utils';
import { coreMock } from '../../../../../core/public/mocks';
import { initAngularBootstrap } from '../../../../kibana_legacy/public';
import { setUiSettings } from '../../../../data/public/services';
import { setFormatService } from '../../services';
import { getInnerAngular } from '../get_inner_angular';
import { initTableVisLegacyModule } from '../table_vis_legacy_module';
import { initAngularBootstrap } from '../../../../kibana_legacy/public/angular_bootstrap';
import { tabifiedData } from './tabified_data';
const uiSettings = new Map();
@ -40,10 +40,12 @@ describe('Table Vis - AggTableGroup Directive', function () {
initTableVisLegacyModule(tableVisModule);
};
beforeAll(async () => {
await initAngularBootstrap();
});
beforeEach(() => {
setUiSettings(core.uiSettings);
setFormatService(getFieldFormatsRegistry(core));
initAngularBootstrap();
initLocalAngular();
angular.mock.module('kibana/table_vis');
angular.mock.inject(($injector) => {

View file

@ -16,7 +16,6 @@ import 'angular-recursion';
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
import { CoreStart, IUiSettingsClient, PluginInitializerContext } from 'kibana/public';
import {
initAngularBootstrap,
PaginateDirectiveProvider,
PaginateControlsDirectiveProvider,
PrivateProvider,
@ -24,8 +23,6 @@ import {
KbnAccessibleClickProvider,
} from '../../../kibana_legacy/public';
initAngularBootstrap();
const thirdPartyAngularDependencies = ['ngSanitize', 'ui.bootstrap', 'RecursionHelper'];
export function getAngularModule(name: string, core: CoreStart, context: PluginInitializerContext) {

View file

@ -12,6 +12,7 @@ import $ from 'jquery';
import 'angular-sanitize';
import 'angular-mocks';
import { initAngularBootstrap } from '../../../../kibana_legacy/public/angular_bootstrap';
import { getAngularModule } from '../get_inner_angular';
import { initTableVisLegacyModule } from '../table_vis_legacy_module';
import { coreMock } from '../../../../../core/public/mocks';
@ -56,6 +57,10 @@ describe('Table Vis - Paginated table', () => {
const defaultPerPage = 10;
let paginatedTable: any;
beforeAll(async () => {
await initAngularBootstrap();
});
const initLocalAngular = () => {
const tableVisModule = getAngularModule(
'kibana/table_vis',

View file

@ -13,6 +13,7 @@ import $ from 'jquery';
import { getAngularModule } from './get_inner_angular';
import { initTableVisLegacyModule } from './table_vis_legacy_module';
import { initAngularBootstrap } from '../../../kibana_legacy/public/angular_bootstrap';
import { tableVisLegacyTypeDefinition } from './table_vis_legacy_type';
import { Vis } from '../../../visualizations/public';
import { stubFields } from '../../../data/public/stubs';
@ -76,6 +77,9 @@ describe('Table Vis - Controller', () => {
initTableVisLegacyModule(tableVisModule);
};
beforeAll(async () => {
await initAngularBootstrap();
});
beforeEach(initLocalAngular);
beforeEach(angular.mock.module('kibana/table_vis'));

View file

@ -56,6 +56,7 @@ export function getTableVisualizationControllerClass(
async initLocalAngular() {
if (!this.tableVisModule) {
const [coreStart, { kibanaLegacy }] = await core.getStartServices();
await kibanaLegacy.loadAngularBootstrap();
this.tableVisModule = getAngularModule(innerAngularName, coreStart, context);
initTableVisLegacyModule(this.tableVisModule);
kibanaLegacy.loadFontAwesome();

View file

@ -19,10 +19,7 @@ import {
} from '../../../../src/core/public';
import { Storage } from '../../../../src/plugins/kibana_utils/public';
import {
initAngularBootstrap,
KibanaLegacyStart,
} from '../../../../src/plugins/kibana_legacy/public';
import { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public';
import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public';
import { DataPublicPluginStart } from '../../../../src/plugins/data/public';
@ -77,7 +74,6 @@ export class GraphPlugin
const config = this.initializerContext.config.get();
initAngularBootstrap();
core.application.register({
id: 'graph',
title: 'Graph',
@ -88,6 +84,7 @@ export class GraphPlugin
updater$: this.appUpdater$,
mount: async (params: AppMountParameters) => {
const [coreStart, pluginsStart] = await core.getStartServices();
await pluginsStart.kibanaLegacy.loadAngularBootstrap();
coreStart.chrome.docTitle.change(
i18n.translate('xpack.graph.pageTitle', { defaultMessage: 'Graph' })
);

View file

@ -93,7 +93,10 @@ export class MonitoringPlugin
category: DEFAULT_APP_CATEGORIES.management,
mount: async (params: AppMountParameters) => {
const [coreStart, pluginsStart] = await core.getStartServices();
const { AngularApp } = await import('./angular');
const [, { AngularApp }] = await Promise.all([
pluginsStart.kibanaLegacy.loadAngularBootstrap(),
import('./angular'),
]);
const deps: MonitoringStartPluginDependencies = {
navigation: pluginsStart.navigation,
kibanaLegacy: pluginsStart.kibanaLegacy,