diff --git a/src/legacy/core_plugins/kibana/public/home/index.ts b/src/legacy/core_plugins/kibana/public/home/index.ts index bd3a0b38ec3f0..b2d90f1444654 100644 --- a/src/legacy/core_plugins/kibana/public/home/index.ts +++ b/src/legacy/core_plugins/kibana/public/home/index.ts @@ -73,6 +73,6 @@ let copiedLegacyCatalogue = false; }, }); instance.start(npStart.core, { - data: npStart.plugins.data, + ...npStart.plugins, }); })(); diff --git a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts index 3ec095f4f26bf..0eb55a3902eda 100644 --- a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts @@ -29,7 +29,7 @@ import { UiSettingsState, } from 'kibana/public'; import { UiStatsMetricType } from '@kbn/analytics'; -import { FeatureCatalogueEntry } from '../../../../../plugins/home/public'; +import { Environment, FeatureCatalogueEntry } from '../../../../../plugins/home/public'; export interface HomeKibanaServices { indexPatternService: any; @@ -61,6 +61,7 @@ export interface HomeKibanaServices { shouldShowTelemetryOptIn: boolean; docLinks: DocLinksStart; addBasePath: (url: string) => string; + environment: Environment; } let services: HomeKibanaServices | null = null; diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home_app.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home_app.js index 6532737cc02e8..e49f00b949da5 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home_app.js +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home_app.js @@ -28,22 +28,19 @@ import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom' import { getTutorial } from '../load_tutorials'; import { replaceTemplateStrings } from './tutorial/replace_template_strings'; import { getServices } from '../../kibana_services'; -// TODO This is going to be refactored soon -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { npSetup } from 'ui/new_platform'; export function HomeApp({ directories }) { const { getInjected, savedObjectsClient, getBasePath, addBasePath, + environment, telemetryOptInProvider: { setOptInNoticeSeen, getOptIn }, } = getServices(); - const { cloud } = npSetup.plugins; - const isCloudEnabled = !!(cloud && cloud.isCloudEnabled); + const isCloudEnabled = environment.cloud; + const mlEnabled = environment.ml; + const apmUiEnabled = environment.apmUi; - const apmUiEnabled = getInjected('apmUiEnabled', true); - const mlEnabled = getInjected('mlEnabled', false); const defaultAppId = getInjected('kbnDefaultAppId', 'discover'); const renderTutorialDirectory = props => { diff --git a/src/legacy/core_plugins/kibana/public/home/plugin.ts b/src/legacy/core_plugins/kibana/public/home/plugin.ts index a998e4d07ab15..42ab049eb5b3a 100644 --- a/src/legacy/core_plugins/kibana/public/home/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/home/plugin.ts @@ -23,7 +23,11 @@ import { UiStatsMetricType } from '@kbn/analytics'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { setServices } from './kibana_services'; import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public'; -import { FeatureCatalogueEntry } from '../../../../../plugins/home/public'; +import { + Environment, + FeatureCatalogueEntry, + HomePublicPluginStart, +} from '../../../../../plugins/home/public'; export interface LegacyAngularInjectedDependencies { telemetryOptInProvider: any; @@ -32,6 +36,7 @@ export interface LegacyAngularInjectedDependencies { export interface HomePluginStartDependencies { data: DataPublicPluginStart; + home: HomePublicPluginStart; } export interface HomePluginSetupDependencies { @@ -60,6 +65,7 @@ export interface HomePluginSetupDependencies { export class HomePlugin implements Plugin { private dataStart: DataPublicPluginStart | null = null; private savedObjectsClient: any = null; + private environment: Environment | null = null; setup( core: CoreSetup, @@ -86,6 +92,7 @@ export class HomePlugin implements Plugin { addBasePath: core.http.basePath.prepend, getBasePath: core.http.basePath.get, indexPatternService: this.dataStart!.indexPatterns, + environment: this.environment!, ...angularDependencies, }); const { renderApp } = await import('./np_ready/application'); @@ -94,8 +101,8 @@ export class HomePlugin implements Plugin { }); } - start(core: CoreStart, { data }: HomePluginStartDependencies) { - // TODO is this really the right way? I though the app context would give us those + start(core: CoreStart, { data, home }: HomePluginStartDependencies) { + this.environment = home.environment.get(); this.dataStart = data; this.savedObjectsClient = core.savedObjects.client; } diff --git a/src/legacy/core_plugins/kibana/public/visualize_embeddable/visualize_embeddable_factory.tsx b/src/legacy/core_plugins/kibana/public/visualize_embeddable/visualize_embeddable_factory.tsx index dd6723fb578af..ebb9ce2cfdf6b 100644 --- a/src/legacy/core_plugins/kibana/public/visualize_embeddable/visualize_embeddable_factory.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize_embeddable/visualize_embeddable_factory.tsx @@ -23,7 +23,6 @@ import 'uiExports/docViews'; import 'uiExports/embeddableActions'; import 'uiExports/fieldFormatEditors'; import 'uiExports/fieldFormats'; -import 'uiExports/home'; import 'uiExports/indexManagement'; import 'uiExports/inspectorViews'; import 'uiExports/savedObjectTypes'; diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 06424ea48a40f..d3f74a540b960 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -131,6 +131,9 @@ export const npSetup = { featureCatalogue: { register: sinon.fake(), }, + environment: { + update: sinon.fake(), + }, }, }, }; diff --git a/src/plugins/home/public/index.ts b/src/plugins/home/public/index.ts index 25e94c20c347b..ca05c8b5f760e 100644 --- a/src/plugins/home/public/index.ts +++ b/src/plugins/home/public/index.ts @@ -23,7 +23,7 @@ export { HomePublicPluginSetup, HomePublicPluginStart, } from './plugin'; -export { FeatureCatalogueEntry, FeatureCatalogueCategory } from './services'; +export { FeatureCatalogueEntry, FeatureCatalogueCategory, Environment } from './services'; import { HomePublicPlugin } from './plugin'; export const plugin = () => new HomePublicPlugin(); diff --git a/src/plugins/home/public/plugin.test.mocks.ts b/src/plugins/home/public/plugin.test.mocks.ts index a48ea8f795136..461930ddfb80f 100644 --- a/src/plugins/home/public/plugin.test.mocks.ts +++ b/src/plugins/home/public/plugin.test.mocks.ts @@ -18,8 +18,11 @@ */ import { featureCatalogueRegistryMock } from './services/feature_catalogue/feature_catalogue_registry.mock'; +import { environmentServiceMock } from './services/environment/environment.mock'; export const registryMock = featureCatalogueRegistryMock.create(); +export const environmentMock = environmentServiceMock.create(); jest.doMock('./services', () => ({ FeatureCatalogueRegistry: jest.fn(() => registryMock), + EnvironmentService: jest.fn(() => environmentMock), })); diff --git a/src/plugins/home/public/plugin.test.ts b/src/plugins/home/public/plugin.test.ts index fad6e8cf47bfe..34502d7d2c6cd 100644 --- a/src/plugins/home/public/plugin.test.ts +++ b/src/plugins/home/public/plugin.test.ts @@ -17,13 +17,15 @@ * under the License. */ -import { registryMock } from './plugin.test.mocks'; +import { registryMock, environmentMock } from './plugin.test.mocks'; import { HomePublicPlugin } from './plugin'; describe('HomePublicPlugin', () => { beforeEach(() => { registryMock.setup.mockClear(); registryMock.start.mockClear(); + environmentMock.setup.mockClear(); + environmentMock.start.mockClear(); }); describe('setup', () => { @@ -32,6 +34,12 @@ describe('HomePublicPlugin', () => { expect(setup).toHaveProperty('featureCatalogue'); expect(setup.featureCatalogue).toHaveProperty('register'); }); + + test('wires up and returns environment service', async () => { + const setup = await new HomePublicPlugin().setup(); + expect(setup).toHaveProperty('environment'); + expect(setup.environment).toHaveProperty('update'); + }); }); describe('start', () => { @@ -45,5 +53,15 @@ describe('HomePublicPlugin', () => { }); expect(start.featureCatalogue.get).toBeDefined(); }); + + test('wires up and returns environment service', async () => { + const service = new HomePublicPlugin(); + await service.setup(); + const start = await service.start({ + application: { capabilities: { catalogue: {} } }, + } as any); + expect(environmentMock.start).toHaveBeenCalled(); + expect(start.environment.get).toBeDefined(); + }); }); }); diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index 40f2047ef0016..39a7f23826900 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -19,6 +19,9 @@ import { CoreStart, Plugin } from 'src/core/public'; import { + EnvironmentService, + EnvironmentServiceSetup, + EnvironmentServiceStart, FeatureCatalogueRegistry, FeatureCatalogueRegistrySetup, FeatureCatalogueRegistryStart, @@ -26,10 +29,12 @@ import { export class HomePublicPlugin implements Plugin { private readonly featuresCatalogueRegistry = new FeatureCatalogueRegistry(); + private readonly environmentService = new EnvironmentService(); public async setup() { return { featureCatalogue: { ...this.featuresCatalogueRegistry.setup() }, + environment: { ...this.environmentService.setup() }, }; } @@ -40,6 +45,7 @@ export class HomePublicPlugin implements Plugin => { + const setup = { + update: jest.fn(), + }; + return setup; +}; + +const createStartMock = (): jest.Mocked => { + const start = { + get: jest.fn(), + }; + return start; +}; + +const createMock = (): jest.Mocked> => { + const service = { + setup: jest.fn(), + start: jest.fn(), + }; + service.setup.mockImplementation(createSetupMock); + service.start.mockImplementation(createStartMock); + return service; +}; + +export const environmentServiceMock = { + createSetup: createSetupMock, + createStart: createStartMock, + create: createMock, +}; diff --git a/src/plugins/home/public/services/environment/environment.test.ts b/src/plugins/home/public/services/environment/environment.test.ts new file mode 100644 index 0000000000000..f42eba782a760 --- /dev/null +++ b/src/plugins/home/public/services/environment/environment.test.ts @@ -0,0 +1,47 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { EnvironmentService } from './environment'; + +describe('EnvironmentService', () => { + describe('setup', () => { + test('allows multiple update calls', () => { + const setup = new EnvironmentService().setup(); + expect(() => { + setup.update({ ml: true }); + setup.update({ apmUi: true }); + }).not.toThrow(); + }); + }); + + describe('start', () => { + test('returns default values', () => { + const service = new EnvironmentService(); + expect(service.start().get()).toEqual({ ml: false, cloud: false, apmUi: false }); + }); + + test('returns last state of update calls', () => { + const service = new EnvironmentService(); + const setup = service.setup(); + setup.update({ ml: true, cloud: true }); + setup.update({ ml: false, apmUi: true }); + expect(service.start().get()).toEqual({ ml: false, cloud: true, apmUi: true }); + }); + }); +}); diff --git a/src/plugins/home/public/services/environment/environment.ts b/src/plugins/home/public/services/environment/environment.ts new file mode 100644 index 0000000000000..36c1afbca5e73 --- /dev/null +++ b/src/plugins/home/public/services/environment/environment.ts @@ -0,0 +1,71 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** @public */ +export interface Environment { + /** + * Flag whether the home app should advertize cloud features + */ + readonly cloud: boolean; + /** + * Flag whether the home app should advertize apm features + */ + readonly apmUi: boolean; + /** + * Flag whether the home app should advertize ml features + */ + readonly ml: boolean; +} + +export class EnvironmentService { + private environment = { + cloud: false, + apmUi: false, + ml: false, + }; + + public setup() { + return { + /** + * Update the environment to influence how the home app is presenting available features. + * This API should not be extended for new features and will be removed in future versions + * in favor of display specific extension apis. + * @deprecated + * @param update + */ + update: (update: Partial) => { + this.environment = Object.assign({}, this.environment, update); + }, + }; + } + + public start() { + return { + /** + * Retrieve the current environment home is running in. This API is only intended for internal + * use and is only exposed during a transition period of migrating the home app to the new platform. + * @deprecated + */ + get: (): Environment => this.environment, + }; + } +} + +export type EnvironmentServiceSetup = ReturnType; +export type EnvironmentServiceStart = ReturnType; diff --git a/src/plugins/home/public/services/environment/index.ts b/src/plugins/home/public/services/environment/index.ts new file mode 100644 index 0000000000000..ed20f6adb96c6 --- /dev/null +++ b/src/plugins/home/public/services/environment/index.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { + EnvironmentService, + Environment, + EnvironmentServiceSetup, + EnvironmentServiceStart, +} from './environment'; diff --git a/src/plugins/home/public/services/index.ts b/src/plugins/home/public/services/index.ts index 3621b0912393a..a6542dd066a67 100644 --- a/src/plugins/home/public/services/index.ts +++ b/src/plugins/home/public/services/index.ts @@ -18,3 +18,4 @@ */ export * from './feature_catalogue'; +export * from './environment'; diff --git a/x-pack/legacy/plugins/apm/public/legacy_register_feature.js b/x-pack/legacy/plugins/apm/public/legacy_register_feature.ts similarity index 53% rename from x-pack/legacy/plugins/apm/public/legacy_register_feature.js rename to x-pack/legacy/plugins/apm/public/legacy_register_feature.ts index 6e98d0784bee1..f12865399054e 100644 --- a/x-pack/legacy/plugins/apm/public/legacy_register_feature.js +++ b/x-pack/legacy/plugins/apm/public/legacy_register_feature.ts @@ -4,13 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { npStart } from 'ui/new_platform'; -import { FeatureCatalogueRegistryProvider } from 'ui/registry/feature_catalogue'; +import { npSetup } from 'ui/new_platform'; import { featureCatalogueEntry } from './new-platform/featureCatalogueEntry'; -const { core } = npStart; -const apmUiEnabled = core.injectedMetadata.getInjectedVar('apmUiEnabled'); +const { + core, + plugins: { home } +} = npSetup; +const apmUiEnabled = core.injectedMetadata.getInjectedVar( + 'apmUiEnabled' +) as boolean; if (apmUiEnabled) { - FeatureCatalogueRegistryProvider.register(() => featureCatalogueEntry); + home.featureCatalogue.register(featureCatalogueEntry); } + +home.environment.update({ + apmUi: apmUiEnabled +}); diff --git a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx index 216af91fbb591..de6cbc7d7a335 100644 --- a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx +++ b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx @@ -17,6 +17,7 @@ import { Plugin, PluginInitializerContext } from '../../../../../../src/core/public'; +import { featureCatalogueEntry } from './featureCatalogueEntry'; import { DataPublicPluginSetup } from '../../../../../../src/plugins/data/public'; import { HomePublicPluginSetup } from '../../../../../../src/plugins/home/public'; import { LicensingPluginSetup } from '../../../../../plugins/licensing/public'; @@ -32,7 +33,6 @@ import { UrlParamsProvider } from '../context/UrlParamsContext'; import { createStaticIndexPattern } from '../services/rest/index_pattern'; import { px, unit, units } from '../style/variables'; import { history } from '../utils/history'; -import { featureCatalogueEntry } from './featureCatalogueEntry'; import { getConfigFromInjectedMetadata } from './getConfigFromInjectedMetadata'; import { setHelpExtension } from './setHelpExtension'; import { toggleAppLinkInNav } from './toggleAppLinkInNav'; diff --git a/x-pack/legacy/plugins/ml/public/register_feature.js b/x-pack/legacy/plugins/ml/public/register_feature.js deleted file mode 100644 index c8d2fd7f6f676..0000000000000 --- a/x-pack/legacy/plugins/ml/public/register_feature.js +++ /dev/null @@ -1,29 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory, -} from 'ui/registry/feature_catalogue'; - -import { i18n } from '@kbn/i18n'; - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'ml', - title: i18n.translate('xpack.ml.machineLearningTitle', { - defaultMessage: 'Machine Learning', - }), - description: i18n.translate('xpack.ml.machineLearningDescription', { - defaultMessage: - 'Automatically model the normal behavior of your time series data to detect anomalies.', - }), - icon: 'machineLearningApp', - path: '/app/ml', - showOnHomePage: true, - category: FeatureCatalogueCategory.DATA, - }; -}); diff --git a/x-pack/legacy/plugins/ml/public/register_feature.ts b/x-pack/legacy/plugins/ml/public/register_feature.ts new file mode 100644 index 0000000000000..c75e37becbc0f --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/register_feature.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { npSetup } from 'ui/new_platform'; +import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; + +npSetup.plugins.home.featureCatalogue.register({ + id: 'ml', + title: i18n.translate('xpack.ml.machineLearningTitle', { + defaultMessage: 'Machine Learning', + }), + description: i18n.translate('xpack.ml.machineLearningDescription', { + defaultMessage: + 'Automatically model the normal behavior of your time series data to detect anomalies.', + }), + icon: 'machineLearningApp', + path: '/app/ml', + showOnHomePage: true, + category: FeatureCatalogueCategory.DATA, +}); + +npSetup.plugins.home.environment.update({ + ml: npSetup.core.injectedMetadata.getInjectedVar('mlEnabled') as boolean, +}); diff --git a/x-pack/plugins/cloud/kibana.json b/x-pack/plugins/cloud/kibana.json index 4b8b7bbf3186c..27b35bcbdd88b 100644 --- a/x-pack/plugins/cloud/kibana.json +++ b/x-pack/plugins/cloud/kibana.json @@ -3,7 +3,7 @@ "version": "8.0.0", "kibanaVersion": "kibana", "configPath": ["xpack", "cloud"], - "optionalPlugins": ["usageCollection"], + "optionalPlugins": ["usageCollection", "home"], "server": true, "ui": true } diff --git a/x-pack/plugins/cloud/public/plugin.ts b/x-pack/plugins/cloud/public/plugin.ts index 63f75ccd2ec94..f6408afb31493 100644 --- a/x-pack/plugins/cloud/public/plugin.ts +++ b/x-pack/plugins/cloud/public/plugin.ts @@ -7,11 +7,16 @@ import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public'; import { getIsCloudEnabled } from '../common/is_cloud_enabled'; import { ELASTIC_SUPPORT_LINK } from '../common/constants'; +import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; interface CloudConfigType { id?: string; } +interface CloudSetupDependencies { + home?: HomePublicPluginSetup; +} + export interface CloudSetup { cloudId?: string; isCloudEnabled: boolean; @@ -20,10 +25,14 @@ export interface CloudSetup { export class CloudPlugin implements Plugin { constructor(private readonly initializerContext: PluginInitializerContext) {} - public async setup(core: CoreSetup) { + public async setup(core: CoreSetup, { home }: CloudSetupDependencies) { const { id } = this.initializerContext.config.get(); const isCloudEnabled = getIsCloudEnabled(id); + if (home) { + home.environment.update({ cloud: isCloudEnabled }); + } + return { cloudId: id, isCloudEnabled,