From badceb46c73fe01d19398b234c9ad8a1b9846d90 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 19 Feb 2020 12:51:26 +0100 Subject: [PATCH 1/2] Remove injected reference from home app (#57836) --- .../kibana/public/home/kibana_services.ts | 8 ++- .../home/np_ready/components/home.test.js | 2 +- .../tutorial/replace_template_strings.js | 6 +- .../core_plugins/kibana/public/home/plugin.ts | 5 +- src/plugins/home/public/index.ts | 11 +++- src/plugins/home/public/mocks/index.ts | 39 +++++++++++++ src/plugins/home/public/plugin.test.mocks.ts | 3 + src/plugins/home/public/plugin.test.ts | 19 ++++++- src/plugins/home/public/plugin.ts | 14 +++++ .../services/environment/environment.ts | 6 +- src/plugins/home/public/services/index.ts | 1 + .../home/public/services/tutorials/index.ts | 25 +++++++++ .../tutorials/tutorial_service.mock.ts | 50 +++++++++++++++++ .../tutorials/tutorial_service.test.ts | 55 +++++++++++++++++++ .../services/tutorials/tutorial_service.ts | 53 ++++++++++++++++++ x-pack/plugins/cloud/public/plugin.ts | 3 + 16 files changed, 286 insertions(+), 14 deletions(-) create mode 100644 src/plugins/home/public/mocks/index.ts create mode 100644 src/plugins/home/public/services/tutorials/index.ts create mode 100644 src/plugins/home/public/services/tutorials/tutorial_service.mock.ts create mode 100644 src/plugins/home/public/services/tutorials/tutorial_service.test.ts create mode 100644 src/plugins/home/public/services/tutorials/tutorial_service.ts 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 0eb55a3902eda..4881b243ef67a 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,11 @@ import { UiSettingsState, } from 'kibana/public'; import { UiStatsMetricType } from '@kbn/analytics'; -import { Environment, FeatureCatalogueEntry } from '../../../../../plugins/home/public'; +import { + Environment, + FeatureCatalogueEntry, + TutorialStart, +} from '../../../../../plugins/home/public'; export interface HomeKibanaServices { indexPatternService: any; @@ -47,7 +51,6 @@ export interface HomeKibanaServices { devMode: boolean; uiSettings: { defaults: UiSettingsState; user?: UiSettingsState | undefined }; }; - getInjected: (name: string, defaultValue?: any) => unknown; chrome: ChromeStart; telemetryOptInProvider: any; uiSettings: IUiSettingsClient; @@ -62,6 +65,7 @@ export interface HomeKibanaServices { docLinks: DocLinksStart; addBasePath: (url: string) => string; environment: Environment; + tutorialVariables: TutorialStart['get']; } let services: HomeKibanaServices | null = null; diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js index be2ceb66f69d0..3a291db2a4b72 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js @@ -29,7 +29,7 @@ import { FeatureCatalogueCategory } from '../../../../../../../plugins/home/publ jest.mock('../../kibana_services', () => ({ getServices: () => ({ getBasePath: () => 'path', - getInjected: () => '', + tutorialVariables: () => ({}), }), })); diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js index daf996444eb3c..e4f236bd9d524 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js @@ -33,7 +33,7 @@ mustacheWriter.escapedValue = function escapedValue(token, context) { }; export function replaceTemplateStrings(text, params = {}) { - const { getInjected, metadata, docLinks } = getServices(); + const { tutorialVariables, metadata, docLinks } = getServices(); const variables = { // '{' and '}' can not be used in template since they are used as template tags. @@ -41,9 +41,7 @@ export function replaceTemplateStrings(text, params = {}) { curlyOpen: '{', curlyClose: '}', config: { - cloud: { - id: getInjected('cloudId'), - }, + ...tutorialVariables(), docs: { base_url: docLinks.ELASTIC_WEBSITE_URL, beats: { diff --git a/src/legacy/core_plugins/kibana/public/home/plugin.ts b/src/legacy/core_plugins/kibana/public/home/plugin.ts index 42ab049eb5b3a..fa63b1d744620 100644 --- a/src/legacy/core_plugins/kibana/public/home/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/home/plugin.ts @@ -68,7 +68,7 @@ export class HomePlugin implements Plugin { private environment: Environment | null = null; setup( - core: CoreSetup, + core: CoreSetup, { kibana_legacy, __LEGACY: { getAngularDependencies, ...legacyServices }, @@ -79,12 +79,12 @@ export class HomePlugin implements Plugin { title: 'Home', mount: async ({ core: contextCore }, params) => { const angularDependencies = await getAngularDependencies(); + const [, { home: homeStart }] = await core.getStartServices(); setServices({ ...legacyServices, http: contextCore.http, toastNotifications: core.notifications.toasts, banners: contextCore.overlays.banners, - getInjected: core.injectedMetadata.getInjectedVar, docLinks: contextCore.docLinks, savedObjectsClient: this.savedObjectsClient!, chrome: contextCore.chrome, @@ -93,6 +93,7 @@ export class HomePlugin implements Plugin { getBasePath: core.http.basePath.get, indexPatternService: this.dataStart!.indexPatterns, environment: this.environment!, + tutorialVariables: homeStart.tutorials.get, ...angularDependencies, }); const { renderApp } = await import('./np_ready/application'); diff --git a/src/plugins/home/public/index.ts b/src/plugins/home/public/index.ts index ca05c8b5f760e..8754d718651f7 100644 --- a/src/plugins/home/public/index.ts +++ b/src/plugins/home/public/index.ts @@ -20,10 +20,19 @@ export { FeatureCatalogueSetup, FeatureCatalogueStart, + EnvironmentSetup, + EnvironmentStart, + TutorialSetup, + TutorialStart, HomePublicPluginSetup, HomePublicPluginStart, } from './plugin'; -export { FeatureCatalogueEntry, FeatureCatalogueCategory, Environment } from './services'; +export { + FeatureCatalogueEntry, + FeatureCatalogueCategory, + Environment, + TutorialVariables, +} from './services'; import { HomePublicPlugin } from './plugin'; export const plugin = () => new HomePublicPlugin(); diff --git a/src/plugins/home/public/mocks/index.ts b/src/plugins/home/public/mocks/index.ts new file mode 100644 index 0000000000000..b0877d47ca5b4 --- /dev/null +++ b/src/plugins/home/public/mocks/index.ts @@ -0,0 +1,39 @@ +/* + * 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 { featureCatalogueRegistryMock } from '../services/feature_catalogue/feature_catalogue_registry.mock'; +import { environmentServiceMock } from '../services/environment/environment.mock'; +import { tutorialServiceMock } from '../services/tutorials/tutorial_service.mock'; + +const createSetupContract = () => ({ + featureCatalogue: featureCatalogueRegistryMock.createSetup(), + environment: environmentServiceMock.createSetup(), + tutorials: tutorialServiceMock.createSetup(), +}); + +const createStartContract = () => ({ + featureCatalogue: featureCatalogueRegistryMock.createStart(), + environment: environmentServiceMock.createStart(), + tutorials: tutorialServiceMock.createStart(), +}); + +export const homePluginMock = { + createSetupContract, + createStartContract, +}; diff --git a/src/plugins/home/public/plugin.test.mocks.ts b/src/plugins/home/public/plugin.test.mocks.ts index 461930ddfb80f..d047311968361 100644 --- a/src/plugins/home/public/plugin.test.mocks.ts +++ b/src/plugins/home/public/plugin.test.mocks.ts @@ -19,10 +19,13 @@ import { featureCatalogueRegistryMock } from './services/feature_catalogue/feature_catalogue_registry.mock'; import { environmentServiceMock } from './services/environment/environment.mock'; +import { tutorialServiceMock } from './services/tutorials/tutorial_service.mock'; export const registryMock = featureCatalogueRegistryMock.create(); export const environmentMock = environmentServiceMock.create(); +export const tutorialMock = tutorialServiceMock.create(); jest.doMock('./services', () => ({ FeatureCatalogueRegistry: jest.fn(() => registryMock), EnvironmentService: jest.fn(() => environmentMock), + TutorialService: jest.fn(() => tutorialMock), })); diff --git a/src/plugins/home/public/plugin.test.ts b/src/plugins/home/public/plugin.test.ts index 34502d7d2c6cd..47ed637e80ac2 100644 --- a/src/plugins/home/public/plugin.test.ts +++ b/src/plugins/home/public/plugin.test.ts @@ -17,15 +17,17 @@ * under the License. */ -import { registryMock, environmentMock } from './plugin.test.mocks'; +import { registryMock, environmentMock, tutorialMock } from './plugin.test.mocks'; import { HomePublicPlugin } from './plugin'; describe('HomePublicPlugin', () => { beforeEach(() => { registryMock.setup.mockClear(); registryMock.start.mockClear(); + tutorialMock.setup.mockClear(); environmentMock.setup.mockClear(); environmentMock.start.mockClear(); + tutorialMock.start.mockClear(); }); describe('setup', () => { @@ -40,6 +42,12 @@ describe('HomePublicPlugin', () => { expect(setup).toHaveProperty('environment'); expect(setup.environment).toHaveProperty('update'); }); + + test('wires up and returns tutorial service', async () => { + const setup = await new HomePublicPlugin().setup(); + expect(setup).toHaveProperty('tutorials'); + expect(setup.tutorials).toHaveProperty('setVariable'); + }); }); describe('start', () => { @@ -63,5 +71,14 @@ describe('HomePublicPlugin', () => { expect(environmentMock.start).toHaveBeenCalled(); expect(start.environment.get).toBeDefined(); }); + + test('wires up and returns tutorial service', async () => { + const service = new HomePublicPlugin(); + await service.setup(); + const core = { application: { capabilities: { catalogue: {} } } } as any; + const start = await service.start(core); + expect(tutorialMock.start).toHaveBeenCalled(); + expect(start.tutorials.get).toBeDefined(); + }); }); }); diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index 39a7f23826900..83fa80463e785 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -25,16 +25,21 @@ import { FeatureCatalogueRegistry, FeatureCatalogueRegistrySetup, FeatureCatalogueRegistryStart, + TutorialService, + TutorialServiceSetup, + TutorialServiceStart, } from './services'; export class HomePublicPlugin implements Plugin { private readonly featuresCatalogueRegistry = new FeatureCatalogueRegistry(); private readonly environmentService = new EnvironmentService(); + private readonly tutorialService = new TutorialService(); public async setup() { return { featureCatalogue: { ...this.featuresCatalogueRegistry.setup() }, environment: { ...this.environmentService.setup() }, + tutorials: { ...this.tutorialService.setup() }, }; } @@ -45,6 +50,7 @@ export class HomePublicPlugin implements Plugin => { + const setup = { + setVariable: 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 tutorialServiceMock = { + createSetup: createSetupMock, + createStart: createStartMock, + create: createMock, +}; diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.test.ts b/src/plugins/home/public/services/tutorials/tutorial_service.test.ts new file mode 100644 index 0000000000000..04f6bce9b7caa --- /dev/null +++ b/src/plugins/home/public/services/tutorials/tutorial_service.test.ts @@ -0,0 +1,55 @@ +/* + * 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 { TutorialService } from './tutorial_service'; + +describe('TutorialService', () => { + describe('setup', () => { + test('allows multiple set calls', () => { + const setup = new TutorialService().setup(); + expect(() => { + setup.setVariable('abc', 123); + setup.setVariable('def', 456); + }).not.toThrow(); + }); + + test('throws when same variable is set twice', () => { + const setup = new TutorialService().setup(); + expect(() => { + setup.setVariable('abc', 123); + setup.setVariable('abc', 456); + }).toThrow(); + }); + }); + + describe('start', () => { + test('returns empty object', () => { + const service = new TutorialService(); + expect(service.start().get()).toEqual({}); + }); + + test('returns last state of update calls', () => { + const service = new TutorialService(); + const setup = service.setup(); + setup.setVariable('abc', 123); + setup.setVariable('def', { subKey: 456 }); + expect(service.start().get()).toEqual({ abc: 123, def: { subKey: 456 } }); + }); + }); +}); diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.ts b/src/plugins/home/public/services/tutorials/tutorial_service.ts new file mode 100644 index 0000000000000..824c3d46a76a3 --- /dev/null +++ b/src/plugins/home/public/services/tutorials/tutorial_service.ts @@ -0,0 +1,53 @@ +/* + * 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 type TutorialVariables = Partial>; + +export class TutorialService { + private tutorialVariables: TutorialVariables = {}; + + public setup() { + return { + /** + * Set a variable usable in tutorial templates. Access with `{config.}`. + */ + setVariable: (key: string, value: unknown) => { + if (this.tutorialVariables[key]) { + throw new Error('variable already set'); + } + this.tutorialVariables[key] = value; + }, + }; + } + + public start() { + return { + /** + * Retrieve the variables for substitution in tutorials. 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: (): TutorialVariables => this.tutorialVariables, + }; + } +} + +export type TutorialServiceSetup = ReturnType; +export type TutorialServiceStart = ReturnType; diff --git a/x-pack/plugins/cloud/public/plugin.ts b/x-pack/plugins/cloud/public/plugin.ts index f6408afb31493..2b8247066bfc3 100644 --- a/x-pack/plugins/cloud/public/plugin.ts +++ b/x-pack/plugins/cloud/public/plugin.ts @@ -31,6 +31,9 @@ export class CloudPlugin implements Plugin { if (home) { home.environment.update({ cloud: isCloudEnabled }); + if (isCloudEnabled) { + home.tutorials.setVariable('cloud', { id }); + } } return { From b0a7626115c1e27c94b7a398af0a314119c36d7f Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 20 Feb 2020 12:09:25 +0100 Subject: [PATCH 2/2] fix test failures --- src/legacy/core_plugins/kibana/public/home/kibana_services.ts | 1 + .../kibana/public/home/np_ready/components/home.test.js | 1 + src/legacy/core_plugins/kibana/public/home/plugin.ts | 1 + 3 files changed, 3 insertions(+) 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 4881b243ef67a..20da836c1ee94 100644 --- a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts @@ -66,6 +66,7 @@ export interface HomeKibanaServices { addBasePath: (url: string) => string; environment: Environment; tutorialVariables: TutorialStart['get']; + getInjected: (name: string, defaultValue?: any) => unknown; } let services: HomeKibanaServices | null = null; diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js index 3a291db2a4b72..ef5bd01698332 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js @@ -30,6 +30,7 @@ jest.mock('../../kibana_services', () => ({ getServices: () => ({ getBasePath: () => 'path', tutorialVariables: () => ({}), + getInjected: () => '', }), })); diff --git a/src/legacy/core_plugins/kibana/public/home/plugin.ts b/src/legacy/core_plugins/kibana/public/home/plugin.ts index fa63b1d744620..a6fa1a47328f6 100644 --- a/src/legacy/core_plugins/kibana/public/home/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/home/plugin.ts @@ -94,6 +94,7 @@ export class HomePlugin implements Plugin { indexPatternService: this.dataStart!.indexPatterns, environment: this.environment!, tutorialVariables: homeStart.tutorials.get, + getInjected: core.injectedMetadata.getInjectedVar, ...angularDependencies, }); const { renderApp } = await import('./np_ready/application');