[Watcher] More robust handling of license at setup (#55831)

* Only register watcher app if we have received an indication of license first
Enable app to react to license updates from backend

* Return setup to a synchronous function.

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Jean-Louis Leysens 2020-01-27 13:35:40 +01:00 committed by GitHub
parent 94297f37b8
commit 35edfb0e56
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 63 deletions

View file

@ -5,6 +5,7 @@
*/
import React from 'react';
import { of } from 'rxjs';
import { ComponentType } from 'enzyme';
import {
chromeServiceMock,
@ -15,6 +16,7 @@ import {
} from '../../../../../../src/core/public/mocks';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { AppContextProvider } from '../../../public/application/app_context';
import { LicenseStatus } from '../../../common/types/license_status';
class MockTimeBuckets {
setBounds(_domain: any) {
@ -27,9 +29,7 @@ class MockTimeBuckets {
}
}
export const mockContextValue = {
getLicenseStatus: () => ({
valid: true,
}),
licenseStatus$: of<LicenseStatus>({ valid: true }),
docLinks: docLinksServiceMock.createStartContract(),
chrome: chromeServiceMock.createStartContract(),
MANAGEMENT_BREADCRUMB: { text: 'test' },

View file

@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { useEffect, useState } from 'react';
import { Observable } from 'rxjs';
import {
ChromeStart,
DocLinksStart,
@ -47,12 +48,17 @@ export interface AppDeps {
uiSettings: IUiSettingsClient;
euiUtils: any;
createTimeBuckets: () => any;
getLicenseStatus: () => LicenseStatus;
licenseStatus$: Observable<LicenseStatus>;
MANAGEMENT_BREADCRUMB: any;
}
export const App = (deps: AppDeps) => {
const { valid, message } = deps.getLicenseStatus();
const [{ valid, message }, setLicenseStatus] = useState<LicenseStatus>({ valid: true });
useEffect(() => {
const s = deps.licenseStatus$.subscribe(setLicenseStatus);
return () => s.unsubscribe();
}, [deps.licenseStatus$]);
if (!valid) {
return (

View file

@ -5,80 +5,90 @@
*/
import { i18n } from '@kbn/i18n';
import { CoreSetup, Plugin, CoreStart } from 'kibana/public';
import { first, map, skip } from 'rxjs/operators';
import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public';
import { LicenseStatus } from '../common/types/license_status';
import { LICENSE_CHECK_STATE } from '../../licensing/public';
import { ILicense, LICENSE_CHECK_STATE } from '../../licensing/public';
import { TimeBuckets, MANAGEMENT_BREADCRUMB } from './legacy';
import { PLUGIN } from '../common/constants';
import { Dependencies } from './types';
export class WatcherUIPlugin implements Plugin<void, void, Dependencies, any> {
// Reference for when `mount` gets called, we don't want to render if
// we don't have a valid license. Under certain conditions the Watcher app link
// may still be present so this is a final guard.
private licenseStatus: LicenseStatus = { valid: false };
private hasRegisteredESManagementSection = false;
const licenseToLicenseStatus = (license: ILicense): LicenseStatus => {
const { state, message } = license.check(PLUGIN.ID, PLUGIN.MINIMUM_LICENSE_REQUIRED);
return {
valid: state === LICENSE_CHECK_STATE.Valid && license.getFeature(PLUGIN.ID).isAvailable,
message,
};
};
export class WatcherUIPlugin implements Plugin<void, void, Dependencies, any> {
setup(
{ application, notifications, http, uiSettings, getStartServices }: CoreSetup,
{ licensing, management, data, home }: Dependencies
) {
licensing.license$.subscribe(license => {
const { state, message } = license.check(PLUGIN.ID, PLUGIN.MINIMUM_LICENSE_REQUIRED);
this.licenseStatus = {
valid: state === LICENSE_CHECK_STATE.Valid && license.getFeature(PLUGIN.ID).isAvailable,
message,
};
if (this.licenseStatus.valid) {
const esSection = management.sections.getSection('elasticsearch');
if (esSection && !this.hasRegisteredESManagementSection) {
esSection.registerApp({
id: 'watcher',
title: i18n.translate(
'xpack.watcher.sections.watchList.managementSection.watcherDisplayName',
{ defaultMessage: 'Watcher' }
),
mount: async ({ element }) => {
const [core, plugins] = await getStartServices();
const { chrome, i18n: i18nDep, docLinks, savedObjects } = core;
const { eui_utils } = plugins as any;
const { boot } = await import('./application/boot');
const esSection = management.sections.getSection('elasticsearch');
return boot({
getLicenseStatus: () => this.licenseStatus,
element,
toasts: notifications.toasts,
http,
uiSettings,
docLinks,
chrome,
euiUtils: eui_utils,
savedObjects: savedObjects.client,
I18nContext: i18nDep.Context,
createTimeBuckets: () => new TimeBuckets(uiSettings, data),
MANAGEMENT_BREADCRUMB,
});
},
});
const watcherESApp = esSection!.registerApp({
id: 'watcher',
title: i18n.translate(
'xpack.watcher.sections.watchList.managementSection.watcherDisplayName',
{ defaultMessage: 'Watcher' }
),
mount: async ({ element }) => {
const [core, plugins] = await getStartServices();
const { chrome, i18n: i18nDep, docLinks, savedObjects } = core;
const { eui_utils } = plugins as any;
const { boot } = await import('./application/boot');
home.featureCatalogue.register({
id: 'watcher',
title: 'Watcher', // This is a product name so we don't translate it.
category: FeatureCatalogueCategory.ADMIN,
description: i18n.translate('xpack.watcher.watcherDescription', {
defaultMessage:
'Detect changes in your data by creating, managing, and monitoring alerts.',
}),
icon: 'watchesApp',
path: '/app/kibana#/management/elasticsearch/watcher/watches',
showOnHomePage: true,
});
return boot({
// Skip the first license status, because that's already been used to determine
// whether to include Watcher.
licenseStatus$: licensing.license$.pipe(skip(1), map(licenseToLicenseStatus)),
element,
toasts: notifications.toasts,
http,
uiSettings,
docLinks,
chrome,
euiUtils: eui_utils,
savedObjects: savedObjects.client,
I18nContext: i18nDep.Context,
createTimeBuckets: () => new TimeBuckets(uiSettings, data),
MANAGEMENT_BREADCRUMB,
});
},
});
this.hasRegisteredESManagementSection = true;
}
watcherESApp.disable();
// TODO: Fix the below dependency on `home` plugin inner workings
// Because the home feature catalogue does not have enable/disable functionality we pass
// the config in but keep a reference for enabling and disabling showing on home based on
// license updates.
const watcherHome = {
id: 'watcher',
title: 'Watcher', // This is a product name so we don't translate it.
category: FeatureCatalogueCategory.ADMIN,
description: i18n.translate('xpack.watcher.watcherDescription', {
defaultMessage: 'Detect changes in your data by creating, managing, and monitoring alerts.',
}),
icon: 'watchesApp',
path: '/app/kibana#/management/elasticsearch/watcher/watches',
showOnHomePage: true,
};
home.featureCatalogue.register(watcherHome);
licensing.license$.pipe(first(), map(licenseToLicenseStatus)).subscribe(({ valid }) => {
if (valid) {
watcherESApp.enable();
watcherHome.showOnHomePage = true;
} else {
watcherESApp.disable();
watcherHome.showOnHomePage = false;
}
});
}