From 4602235ba81e9c13be251ab687359e737a44d4f0 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 27 Jan 2020 13:24:41 +0200 Subject: [PATCH 01/41] NP telemetry plugin barebones --- src/plugins/telemetry/common/constants.ts | 0 src/plugins/telemetry/kibana.json | 7 ++++++ src/plugins/telemetry/public/index.ts | 25 +++++++++++++++++++ src/plugins/telemetry/public/plugin.ts | 0 .../telemetry/public/services/index.ts | 0 5 files changed, 32 insertions(+) create mode 100644 src/plugins/telemetry/common/constants.ts create mode 100644 src/plugins/telemetry/kibana.json create mode 100644 src/plugins/telemetry/public/index.ts create mode 100644 src/plugins/telemetry/public/plugin.ts create mode 100644 src/plugins/telemetry/public/services/index.ts diff --git a/src/plugins/telemetry/common/constants.ts b/src/plugins/telemetry/common/constants.ts new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/plugins/telemetry/kibana.json b/src/plugins/telemetry/kibana.json new file mode 100644 index 0000000000000..c09514e1990ce --- /dev/null +++ b/src/plugins/telemetry/kibana.json @@ -0,0 +1,7 @@ +{ + "id": "telemetry", + "configPath": ["telemetry"], + "version": "kibana", + "server": false, + "ui": true +} diff --git a/src/plugins/telemetry/public/index.ts b/src/plugins/telemetry/public/index.ts new file mode 100644 index 0000000000000..578789e407df5 --- /dev/null +++ b/src/plugins/telemetry/public/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. + */ + +import { PluginInitializerContext } from '../../../core/public'; +import { TelemetryPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new TelemetryPlugin(initializerContext); +} diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/plugins/telemetry/public/services/index.ts b/src/plugins/telemetry/public/services/index.ts new file mode 100644 index 0000000000000..e69de29bb2d1d From cc39460026a8034bc614c4afda75867bb5a41c98 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 29 Jan 2020 14:00:17 +0200 Subject: [PATCH 02/41] fully migrate telemetry sender --- .../telemetry/common/constants.ts | 5 - src/legacy/core_plugins/telemetry/index.ts | 2 +- .../telemetry/public/hacks/telemetry.js | 120 ------------ .../telemetry/public/hacks/telemetry_init.ts | 53 ----- src/plugins/telemetry/common/constants.ts | 29 +++ src/plugins/telemetry/kibana.json | 1 - src/plugins/telemetry/public/plugin.ts | 91 +++++++++ .../telemetry/public/services/index.ts | 21 ++ .../public/services/telemetry_sender.test.ts} | 0 .../public/services/telemetry_sender.ts | 97 +++++++++ .../public/services/telemetry_service.ts | 185 ++++++++++++++++++ 11 files changed, 424 insertions(+), 180 deletions(-) delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/telemetry.js delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/telemetry_init.ts rename src/{legacy/core_plugins/telemetry/public/hacks/telemetry.test.js => plugins/telemetry/public/services/telemetry_sender.test.ts} (100%) create mode 100644 src/plugins/telemetry/public/services/telemetry_sender.ts create mode 100644 src/plugins/telemetry/public/services/telemetry_service.ts diff --git a/src/legacy/core_plugins/telemetry/common/constants.ts b/src/legacy/core_plugins/telemetry/common/constants.ts index cf2c9c883871b..52981c04ad34a 100644 --- a/src/legacy/core_plugins/telemetry/common/constants.ts +++ b/src/legacy/core_plugins/telemetry/common/constants.ts @@ -43,11 +43,6 @@ export const getConfigTelemetryDesc = () => { */ export const REPORT_INTERVAL_MS = 86400000; -/* - * Key for the localStorage service - */ -export const LOCALSTORAGE_KEY = 'telemetry.data'; - /** * Link to the Elastic Telemetry privacy statement. */ diff --git a/src/legacy/core_plugins/telemetry/index.ts b/src/legacy/core_plugins/telemetry/index.ts index 2a81e3fa05c6c..0e870911478d9 100644 --- a/src/legacy/core_plugins/telemetry/index.ts +++ b/src/legacy/core_plugins/telemetry/index.ts @@ -110,7 +110,7 @@ const telemetry = (kibana: any) => { telemetryNotifyUserAboutOptInDefault: false, }; }, - hacks: ['plugins/telemetry/hacks/telemetry_init', 'plugins/telemetry/hacks/telemetry_opt_in'], + hacks: ['plugins/telemetry/hacks/telemetry_opt_in'], mappings, }, postInit(server: Server) { diff --git a/src/legacy/core_plugins/telemetry/public/hacks/telemetry.js b/src/legacy/core_plugins/telemetry/public/hacks/telemetry.js deleted file mode 100644 index 8fa777ead3e4b..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/telemetry.js +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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 { REPORT_INTERVAL_MS, LOCALSTORAGE_KEY } from '../../common/constants'; - -export class Telemetry { - /** - * @param {Object} $injector - AngularJS injector service - * @param {Function} fetchTelemetry Method used to fetch telemetry data (expects an array response) - */ - constructor($injector, fetchTelemetry) { - this._storage = $injector.get('localStorage'); - this._$http = $injector.get('$http'); - this._telemetryUrl = $injector.get('telemetryUrl'); - this._telemetryOptedIn = $injector.get('telemetryOptedIn'); - this._fetchTelemetry = fetchTelemetry; - this._sending = false; - - // try to load the local storage data - const attributes = this._storage.get(LOCALSTORAGE_KEY) || {}; - this._lastReport = attributes.lastReport; - } - - _saveToBrowser() { - // we are the only code that manipulates this key, so it's safe to blindly overwrite the whole object - this._storage.set(LOCALSTORAGE_KEY, { lastReport: this._lastReport }); - } - - /** - * Determine if we are due to send a new report. - * - * @returns {Boolean} true if a new report should be sent. false otherwise. - */ - _checkReportStatus() { - // check if opt-in for telemetry is enabled - if (this._telemetryOptedIn) { - // returns NaN for any malformed or unset (null/undefined) value - const lastReport = parseInt(this._lastReport, 10); - // If it's been a day since we last sent telemetry - if (isNaN(lastReport) || Date.now() - lastReport > REPORT_INTERVAL_MS) { - return true; - } - } - - return false; - } - - /** - * Check report permission and if passes, send the report - * - * @returns {Promise} Always. - */ - _sendIfDue() { - if (this._sending || !this._checkReportStatus()) { - return Promise.resolve(false); - } - - // mark that we are working so future requests are ignored until we're done - this._sending = true; - - return ( - this._fetchTelemetry() - .then(response => { - const clusters = [].concat(response.data); - return Promise.all( - clusters.map(cluster => { - const req = { - method: 'POST', - url: this._telemetryUrl, - data: cluster, - }; - // if passing data externally, then suppress kbnXsrfToken - if (this._telemetryUrl.match(/^https/)) { - req.kbnXsrfToken = false; - } - return this._$http(req); - }) - ); - }) - // the response object is ignored because we do not check it - .then(() => { - // we sent a report, so we need to record and store the current timestamp - this._lastReport = Date.now(); - this._saveToBrowser(); - }) - // no ajaxErrorHandlers for telemetry - .catch(() => null) - .then(() => { - this._sending = false; - return true; // sent, but not necessarilly successfully - }) - ); - } - - /** - * Public method - * - * @returns {Number} `window.setInterval` response to allow cancelling the interval. - */ - start() { - // continuously check if it's due time for a report - return window.setInterval(() => this._sendIfDue(), 60000); - } -} // end class diff --git a/src/legacy/core_plugins/telemetry/public/hacks/telemetry_init.ts b/src/legacy/core_plugins/telemetry/public/hacks/telemetry_init.ts deleted file mode 100644 index 1930d65d5c09b..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/telemetry_init.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 { npStart } from 'ui/new_platform'; -// @ts-ignore -import { uiModules } from 'ui/modules'; -import { isUnauthenticated } from '../services'; -// @ts-ignore -import { Telemetry } from './telemetry'; -// @ts-ignore -import { fetchTelemetry } from './fetch_telemetry'; -// @ts-ignore -import { isOptInHandleOldSettings } from './welcome_banner/handle_old_settings'; -import { TelemetryOptInProvider } from '../services'; - -function telemetryInit($injector: any) { - const $http = $injector.get('$http'); - const Private = $injector.get('Private'); - const config = $injector.get('config'); - const telemetryOptInProvider = Private(TelemetryOptInProvider); - - const telemetryEnabled = npStart.core.injectedMetadata.getInjectedVar('telemetryEnabled'); - const telemetryOptedIn = isOptInHandleOldSettings(config, telemetryOptInProvider); - const sendUsageFrom = npStart.core.injectedMetadata.getInjectedVar('telemetrySendUsageFrom'); - - if (telemetryEnabled && telemetryOptedIn && sendUsageFrom === 'browser') { - // no telemetry for non-logged in users - if (isUnauthenticated()) { - return; - } - - const sender = new Telemetry($injector, () => fetchTelemetry($http)); - sender.start(); - } -} - -uiModules.get('telemetry/hacks').run(telemetryInit); diff --git a/src/plugins/telemetry/common/constants.ts b/src/plugins/telemetry/common/constants.ts index e69de29bb2d1d..57aabc5175291 100644 --- a/src/plugins/telemetry/common/constants.ts +++ b/src/plugins/telemetry/common/constants.ts @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/** + * The amount of time, in milliseconds, to wait between reports when enabled. + * Currently 24 hours. + */ +export const REPORT_INTERVAL_MS = 86400000; + +/* + * Key for the localStorage service + */ +export const LOCALSTORAGE_KEY = 'telemetry.data'; diff --git a/src/plugins/telemetry/kibana.json b/src/plugins/telemetry/kibana.json index c09514e1990ce..3a28149276c3e 100644 --- a/src/plugins/telemetry/kibana.json +++ b/src/plugins/telemetry/kibana.json @@ -1,6 +1,5 @@ { "id": "telemetry", - "configPath": ["telemetry"], "version": "kibana", "server": false, "ui": true diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index e69de29bb2d1d..7d2652f96fb0e 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -0,0 +1,91 @@ +/* + * 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 { + // PluginInitializerContext, + Plugin, + CoreSetup, + CoreStart, + HttpSetup, +} from '../../../core/public'; +import { TelemetrySender, TelemetryService } from './services'; + +// interface PublicConfigType { +// uiMetric: { +// enabled: boolean; +// debug: boolean; +// }; +// } + +export interface TelemetryStart { + telemetryService: TelemetryService; +} + +export class TelemetryPlugin implements Plugin { + // private config: PublicConfigType; + private isUnauthenticated: boolean = false; + // constructor(initializerContext: PluginInitializerContext) { + // this.config = initializerContext.config.get(); + // } + + public setup({ http }: CoreSetup): TelemetrySetup { + this.isUnauthenticated = this.getIsUnauthenticated(http); + return {}; + } + + public start({ injectedMetadata, http }: CoreStart): TelemetryStart { + const isPluginEnabled = injectedMetadata.getInjectedVar('telemetryEnabled') as boolean; + const sendUsageFrom = injectedMetadata.getInjectedVar('telemetrySendUsageFrom') as + | 'browser' + | 'server'; + const telemetryService = new TelemetryService({ http, injectedMetadata }); + + this.maybeStartTelemetryPoller({ + telemetryService, + isPluginEnabled, + sendUsageFrom, + }); + + return { + telemetryService, + }; + } + + private getIsUnauthenticated(http: HttpSetup) { + const { anonymousPaths } = http; + return anonymousPaths.isAnonymous(window.location.pathname); + } + + private maybeStartTelemetryPoller({ + telemetryService, + isPluginEnabled, + sendUsageFrom, + }: { + telemetryService: TelemetryService; + isPluginEnabled: boolean; + sendUsageFrom: string; + }) { + if (isPluginEnabled && sendUsageFrom === 'browser') { + const sender = new TelemetrySender(telemetryService); + // no telemetry for non-logged in users + if (!this.isUnauthenticated) { + sender.startChecking(); + } + } + } +} diff --git a/src/plugins/telemetry/public/services/index.ts b/src/plugins/telemetry/public/services/index.ts index e69de29bb2d1d..d520fb6df2f9e 100644 --- a/src/plugins/telemetry/public/services/index.ts +++ b/src/plugins/telemetry/public/services/index.ts @@ -0,0 +1,21 @@ +/* + * 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 { TelemetrySender } from './telemetry_sender'; +export { TelemetryService } from './telemetry_service'; diff --git a/src/legacy/core_plugins/telemetry/public/hacks/telemetry.test.js b/src/plugins/telemetry/public/services/telemetry_sender.test.ts similarity index 100% rename from src/legacy/core_plugins/telemetry/public/hacks/telemetry.test.js rename to src/plugins/telemetry/public/services/telemetry_sender.test.ts diff --git a/src/plugins/telemetry/public/services/telemetry_sender.ts b/src/plugins/telemetry/public/services/telemetry_sender.ts new file mode 100644 index 0000000000000..d64ec539e8c81 --- /dev/null +++ b/src/plugins/telemetry/public/services/telemetry_sender.ts @@ -0,0 +1,97 @@ +/* + * 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 { REPORT_INTERVAL_MS, LOCALSTORAGE_KEY } from '../../common/constants'; +import { TelemetryService } from './telemetry_service'; +import { Storage } from '../../../kibana_utils/public'; + +export class TelemetrySender { + private readonly telemetryService: TelemetryService; + private isSending: boolean = false; + private lastReported?: string; + private readonly storage: Storage; + + constructor(telemetryService: TelemetryService) { + this.telemetryService = telemetryService; + this.storage = new Storage(window.localStorage); + + const attributes = this.storage.get(LOCALSTORAGE_KEY); + if (attributes) { + this.lastReported = attributes.lastReport; + } + } + + private saveToBrowser() { + // we are the only code that manipulates this key, so it's safe to blindly overwrite the whole object + this.storage.set(LOCALSTORAGE_KEY, { lastReport: this.lastReported }); + } + + private shouldSendReport(): boolean { + // check if opt-in for telemetry is enabled + if (this.telemetryService.getIsOptedIn()) { + if (!this.lastReported) { + return true; + } + // returns NaN for any malformed or unset (null/undefined) value + const lastReport = parseInt(this.lastReported, 10); + // If it's been a day since we last sent telemetry + if (isNaN(lastReport) || Date.now() - lastReport > REPORT_INTERVAL_MS) { + return true; + } + } + + return false; + } + + private async sendIfDue() { + if (this.isSending || !this.shouldSendReport()) { + return; + } + + // mark that we are working so future requests are ignored until we're done + this.isSending = true; + try { + const telemetryUrl = this.telemetryService.getTelemetryUrl(); + const telemetryData: any | any[] = await this.telemetryService.fetchTelemetry(); + const clusters: string[] = [].concat(telemetryData); + await Promise.all( + clusters.map( + async cluster => + await fetch(telemetryUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: cluster, + }) + ) + ); + this.lastReported = `${Date.now()}`; + this.saveToBrowser(); + } catch (err) { + // ignore err + } finally { + this.isSending = false; + } + } + + public startChecking() { + return setInterval(() => this.sendIfDue(), 60000); + } +} diff --git a/src/plugins/telemetry/public/services/telemetry_service.ts b/src/plugins/telemetry/public/services/telemetry_service.ts new file mode 100644 index 0000000000000..1a4cc9d637995 --- /dev/null +++ b/src/plugins/telemetry/public/services/telemetry_service.ts @@ -0,0 +1,185 @@ +/* + * 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 moment from 'moment'; +// @ts-ignore +// import { banners, toastNotifications } from 'ui/notify'; +import { i18n } from '@kbn/i18n'; +import { CoreStart } from 'kibana/public'; + +const bannerId: string | null = null; +const optInBannerNoticeId: string | null = null; +const currentOptInStatus = false; +const telemetryNotifyUserAboutOptInDefault = true; + +interface TelemetryServiceConstructor { + http: CoreStart['http']; + injectedMetadata: CoreStart['injectedMetadata']; + reportOptInStatusChange?: boolean; +} + +export class TelemetryService { + private readonly http: CoreStart['http']; + private readonly injectedMetadata: CoreStart['injectedMetadata']; + private readonly reportOptInStatusChange: boolean; + private isOptedIn: boolean; + + constructor({ + http, + injectedMetadata, + reportOptInStatusChange = true, + }: TelemetryServiceConstructor) { + const isOptedIn = injectedMetadata.getInjectedVar('telemetryOptedIn') as boolean; + + this.reportOptInStatusChange = reportOptInStatusChange; + this.injectedMetadata = injectedMetadata; + this.http = http; + + this.isOptedIn = isOptedIn; + } + + public getCanChangeOptInStatus() { + const allowChangingOptInStatus = this.injectedMetadata.getInjectedVar( + 'allowChangingOptInStatus' + ) as boolean; + return allowChangingOptInStatus; + } + + public getOptInStatusUrl() { + const telemetryOptInStatusUrl = this.injectedMetadata.getInjectedVar( + 'telemetryOptInStatusUrl' + ) as string; + return telemetryOptInStatusUrl; + } + + public getTelemetryUrl() { + const telemetryUrl = this.injectedMetadata.getInjectedVar('telemetryUrl') as string; + return telemetryUrl; + } + + public getIsOptedIn() { + return this.isOptedIn; + } + + public async fetchTelemetry({ unencrypted = false } = {}) { + const now = moment(); + return this.http.post(`/api/telemetry/v2/clusters/_stats`, { + body: JSON.stringify({ + unencrypted, + timeRange: { + min: now.subtract(20, 'minutes').toISOString(), + max: now.toISOString(), + }, + }), + }); + } + + public async setOptIn(optedIn: boolean): Promise { + const canChangeOptInStatus = this.getCanChangeOptInStatus(); + if (!canChangeOptInStatus) { + return false; + } + + try { + await this.http.post('/api/telemetry/v2/optIn', { + body: JSON.stringify({ enabled: optedIn }), + }); + if (this.reportOptInStatusChange) { + await this.reportOptInStatus(optedIn); + } + this.isOptedIn = optedIn; + } catch (err) { + toastNotifications.addError(err, { + title: i18n.translate('telemetry.optInErrorToastTitle', { + defaultMessage: 'Error', + }), + toastMessage: i18n.translate('telemetry.optInErrorToastText', { + defaultMessage: 'An error occurred while trying to set the usage statistics preference.', + }), + }); + + return false; + } + + return true; + } + + private async reportOptInStatus(OptInStatus: boolean) { + const telemetryOptInStatusUrl = this.getOptInStatusUrl(); + + try { + // const optInStatus = await this.http.post('/api/telemetry/v2/clusters/_opt_in_stats', { + // body: JSON.stringify({ enabled, unencrypted: false }) + // }); + + return await fetch(telemetryOptInStatusUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ enabled: OptInStatus }), + }); + } catch (err) { + // Sending the ping is best-effort. Telemetry tries to send the ping once and discards it immediately if sending fails. + // swallow any errors + } + } +} + +// getBannerId: () => bannerId, +// getOptInBannerNoticeId: () => optInBannerNoticeId, +// getOptIn: () => currentOptInStatus, +// canChangeOptInStatus: () => allowChangingOptInStatus, +// notifyUserAboutOptInDefault: () => telemetryNotifyUserAboutOptInDefault, +// setBannerId(id: string) { +// bannerId = id; +// }, +// setOptInBannerNoticeId(id: string) { +// optInBannerNoticeId = id; +// }, +// setOptInNoticeSeen: async () => { +// const $http = $injector.get('$http'); + +// // If they've seen the notice don't spam the API +// if (!telemetryNotifyUserAboutOptInDefault) { +// return telemetryNotifyUserAboutOptInDefault; +// } + +// if (optInBannerNoticeId) { +// banners.remove(optInBannerNoticeId); +// } + +// try { +// await $http.put(chrome.addBasePath('/api/telemetry/v2/userHasSeenNotice')); +// telemetryNotifyUserAboutOptInDefault = false; +// } catch (error) { +// toastNotifications.addError(error, { +// title: i18n.translate('telemetry.optInNoticeSeenErrorTitle', { +// defaultMessage: 'Error', +// }), +// toastMessage: i18n.translate('telemetry.optInNoticeSeenErrorToastText', { +// defaultMessage: 'An error occurred dismissing the notice', +// }), +// }); +// telemetryNotifyUserAboutOptInDefault = true; +// } + +// return telemetryNotifyUserAboutOptInDefault; +// }, +// }; From 4841c72391628ffcf979f139a683f8121953380a Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 29 Jan 2020 23:27:54 +0200 Subject: [PATCH 03/41] license plugin to use NP telemetry --- .../core_plugins/kibana/public/home/index.ts | 24 +-- .../kibana/public/home/kibana_services.ts | 4 +- .../public/home/np_ready/components/home.js | 8 +- .../home/np_ready/components/home_app.js | 5 +- .../home/np_ready/components/welcome.tsx | 11 +- .../core_plugins/kibana/public/home/plugin.ts | 21 +-- .../hacks/welcome_banner/inject_banner.js | 6 - src/plugins/telemetry/common/constants.ts | 10 ++ .../telemetry/public/components/index.ts} | 12 +- .../components/opt_in_example_flyout.test.tsx | 31 ++++ .../components/opt_in_example_flyout.tsx | 162 ++++++++++++++++++ .../opted_in_notice_banner.test.tsx | 0 .../components/opted_in_notice_banner.tsx | 23 +-- src/plugins/telemetry/public/index.ts | 1 + src/plugins/telemetry/public/plugin.ts | 50 +++++- .../telemetry/public/services/index.ts | 1 + .../services/telemetry_notifications/index.ts | 20 +++ .../render_opted_in_notice_banner.test.tsx} | 0 .../render_opted_in_notice_banner.tsx} | 31 ++-- .../telemetry_notifications.ts | 98 +++++++++++ .../public/services/telemetry_sender.test.ts | 2 +- .../public/services/telemetry_service.ts | 93 ++++------ .../telemetry_opt_in/{index.js => index.ts} | 0 ...lemetry_opt_in.js => telemetry_opt_in.tsx} | 51 +++--- .../np_ready/application/lib/telemetry.js | 36 ---- .../np_ready/application/lib/telemetry.ts | 9 + .../license_dashboard/license_dashboard.js | 4 +- .../start_trial/{index.js => index.ts} | 1 + .../{start_trial.js => start_trial.tsx} | 62 +++++-- .../upload_license.container.js | 3 + .../sections/upload_license/upload_license.js | 28 ++- .../public/register_route.ts | 20 --- 32 files changed, 552 insertions(+), 275 deletions(-) rename src/{legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_opt_in_banner.js => plugins/telemetry/public/components/index.ts} (63%) create mode 100644 src/plugins/telemetry/public/components/opt_in_example_flyout.test.tsx create mode 100644 src/plugins/telemetry/public/components/opt_in_example_flyout.tsx rename src/{legacy/core_plugins => plugins}/telemetry/public/components/opted_in_notice_banner.test.tsx (100%) rename src/{legacy/core_plugins => plugins}/telemetry/public/components/opted_in_notice_banner.tsx (81%) create mode 100644 src/plugins/telemetry/public/services/telemetry_notifications/index.ts rename src/{legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_notice_banner.test.js => plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.test.tsx} (100%) rename src/{legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_notice_banner.js => plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx} (58%) create mode 100644 src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts rename x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/{index.js => index.ts} (100%) rename x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/{telemetry_opt_in.js => telemetry_opt_in.tsx} (84%) delete mode 100644 x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.js create mode 100644 x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts rename x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/{index.js => index.ts} (95%) rename x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/{start_trial.js => start_trial.tsx} (87%) diff --git a/src/legacy/core_plugins/kibana/public/home/index.ts b/src/legacy/core_plugins/kibana/public/home/index.ts index 27d09a53ba20d..2798ee20c5e89 100644 --- a/src/legacy/core_plugins/kibana/public/home/index.ts +++ b/src/legacy/core_plugins/kibana/public/home/index.ts @@ -21,28 +21,7 @@ import { FeatureCatalogueRegistryProvider } from 'ui/registry/feature_catalogue' import { npSetup, npStart } from 'ui/new_platform'; import chrome from 'ui/chrome'; import { IPrivate } from 'ui/private'; -import { HomePlugin, LegacyAngularInjectedDependencies } from './plugin'; -import { TelemetryOptInProvider } from '../../../telemetry/public/services'; - -/** - * Get dependencies relying on the global angular context. - * They also have to get resolved together with the legacy imports above - */ -async function getAngularDependencies(): Promise { - const injector = await chrome.dangerouslyGetActiveInjector(); - - const Private = injector.get('Private'); - - const telemetryEnabled = npStart.core.injectedMetadata.getInjectedVar('telemetryEnabled'); - const telemetryBanner = npStart.core.injectedMetadata.getInjectedVar('telemetryBanner'); - const telemetryOptInProvider = Private(TelemetryOptInProvider); - - return { - telemetryOptInProvider, - shouldShowTelemetryOptIn: - telemetryEnabled && telemetryBanner && !telemetryOptInProvider.getOptIn(), - }; -} +import { HomePlugin } from './plugin'; let copiedLegacyCatalogue = false; @@ -64,7 +43,6 @@ let copiedLegacyCatalogue = false; } return npStart.plugins.home.featureCatalogue.get(); }, - getAngularDependencies, }, }); instance.start(npStart.core, { 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 4d9177735556d..fd27a197f844b 100644 --- a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts @@ -30,6 +30,7 @@ import { } from 'kibana/public'; import { UiStatsMetricType } from '@kbn/analytics'; import { Environment, FeatureCatalogueEntry } from '../../../../../plugins/home/public'; +import { TelemetryPluginStart } from '../../../../../plugins/telemetry/public'; export interface HomeKibanaServices { indexPatternService: any; @@ -49,7 +50,6 @@ export interface HomeKibanaServices { }; getInjected: (name: string, defaultValue?: any) => unknown; chrome: ChromeStart; - telemetryOptInProvider: any; uiSettings: IUiSettingsClient; http: HttpStart; savedObjectsClient: SavedObjectsClientContract; @@ -57,10 +57,10 @@ export interface HomeKibanaServices { banners: OverlayStart['banners']; trackUiMetric: (type: UiStatsMetricType, eventNames: string | string[], count?: number) => void; getBasePath: () => string; - shouldShowTelemetryOptIn: boolean; docLinks: DocLinksStart; addBasePath: (url: string) => string; environment: Environment; + telemetry?: TelemetryPluginStart; } let services: HomeKibanaServices | null = null; diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.js index 5c32a463da115..a7c915c28d0ed 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.js +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.js @@ -51,7 +51,6 @@ export class Home extends Component { getServices().getInjected('disableWelcomeScreen') || props.localStorage.getItem(KEY_ENABLE_WELCOME) === 'false' ); - const currentOptInStatus = this.props.getOptInStatus(); this.state = { // If welcome is enabled, we wait for loading to complete // before rendering. This prevents an annoying flickering @@ -60,7 +59,6 @@ export class Home extends Component { isLoading: isWelcomeEnabled, isNewKibanaInstance: false, isWelcomeEnabled, - currentOptInStatus, }; } @@ -224,8 +222,7 @@ export class Home extends Component { ); } @@ -264,6 +261,5 @@ Home.propTypes = { localStorage: PropTypes.object.isRequired, urlBasePath: PropTypes.string.isRequired, mlEnabled: PropTypes.bool.isRequired, - onOptInSeen: PropTypes.func.isRequired, - getOptInStatus: PropTypes.func.isRequired, + telemetry: PropTypes.shape({}), }; 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 e49f00b949da5..da4e896f53c21 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 @@ -35,7 +35,7 @@ export function HomeApp({ directories }) { getBasePath, addBasePath, environment, - telemetryOptInProvider: { setOptInNoticeSeen, getOptIn }, + telemetry, } = getServices(); const isCloudEnabled = environment.cloud; const mlEnabled = environment.ml; @@ -84,8 +84,7 @@ export function HomeApp({ directories }) { find={savedObjectsClient.find} localStorage={localStorage} urlBasePath={getBasePath()} - onOptInSeen={setOptInNoticeSeen} - getOptInStatus={getOptIn} + telemetry={telemetry} /> diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx index 1b7761d068d2f..552a653731cb1 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx @@ -38,13 +38,13 @@ import { import { METRIC_TYPE } from '@kbn/analytics'; import { FormattedMessage } from '@kbn/i18n/react'; import { getServices } from '../../kibana_services'; +import { TelemetryPluginStart } from '../../../../../../../plugins/telemetry/public'; import { SampleDataCard } from './sample_data'; interface Props { urlBasePath: string; onSkip: () => void; - onOptInSeen: () => any; - currentOptInStatus: boolean; + telemetry: TelemetryPluginStart; } /** @@ -75,8 +75,9 @@ export class Welcome extends React.Component { }; componentDidMount() { + const { telemetry } = this.props; this.services.trackUiMetric(METRIC_TYPE.LOADED, 'welcomeScreenMount'); - this.props.onOptInSeen(); + telemetry.telemetryNotifications.setOptedInNoticeSeen(); document.addEventListener('keydown', this.hideOnEsc); } @@ -85,7 +86,9 @@ export class Welcome extends React.Component { } private renderTelemetryEnabledOrDisabledText = () => { - if (this.props.currentOptInStatus) { + const { telemetry } = this.props; + const isOptedIn = telemetry.telemetryService.getIsOptedIn(); + if (isOptedIn) { return ( Promise; - getAngularDependencies: () => Promise; }; usageCollection: UsageCollectionSetup; kibana_legacy: KibanaLegacySetup; @@ -64,22 +60,18 @@ export interface HomePluginSetupDependencies { export class HomePlugin implements Plugin { private dataStart: DataPublicPluginStart | null = null; private savedObjectsClient: any = null; + private telemetry: any = null; private environment: Environment | null = null; setup( core: CoreSetup, - { - kibana_legacy, - usageCollection, - __LEGACY: { getAngularDependencies, ...legacyServices }, - }: HomePluginSetupDependencies + { kibana_legacy, usageCollection, __LEGACY: { ...legacyServices } }: HomePluginSetupDependencies ) { kibana_legacy.registerLegacyApp({ id: 'home', title: 'Home', mount: async ({ core: contextCore }, params) => { const trackUiMetric = usageCollection.reportUiStats.bind(usageCollection, 'Kibana_home'); - const angularDependencies = await getAngularDependencies(); setServices({ ...legacyServices, trackUiMetric, @@ -89,13 +81,13 @@ export class HomePlugin implements Plugin { getInjected: core.injectedMetadata.getInjectedVar, docLinks: contextCore.docLinks, savedObjectsClient: this.savedObjectsClient!, + telemetry: this.telemetry!, chrome: contextCore.chrome, uiSettings: core.uiSettings, 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'); return await renderApp(params.element); @@ -103,9 +95,10 @@ export class HomePlugin implements Plugin { }); } - start(core: CoreStart, { data, home }: HomePluginStartDependencies) { + start(core: CoreStart, { data, home, telemetry }: HomePluginStartDependencies) { this.environment = home.environment.get(); this.dataStart = data; + this.telemetry = telemetry; this.savedObjectsClient = core.savedObjects.client; } diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/inject_banner.js b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/inject_banner.js index c4c5c3e9e0aa2..cc3ef32c9b618 100644 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/inject_banner.js +++ b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/inject_banner.js @@ -21,9 +21,7 @@ import chrome from 'ui/chrome'; import { fetchTelemetry } from '../fetch_telemetry'; import { renderBanner } from './render_banner'; -import { renderOptedInBanner } from './render_notice_banner'; import { shouldShowBanner } from './should_show_banner'; -import { shouldShowOptInBanner } from './should_show_opt_in_banner'; import { TelemetryOptInProvider, isUnauthenticated } from '../../services'; import { npStart } from 'ui/new_platform'; @@ -56,10 +54,6 @@ async function asyncInjectBanner($injector) { if (await shouldShowBanner(telemetryOptInProvider, config)) { renderBanner(telemetryOptInProvider, () => fetchTelemetry($http, { unencrypted: true })); } - - if (await shouldShowOptInBanner(telemetryOptInProvider, config)) { - renderOptedInBanner(telemetryOptInProvider, () => fetchTelemetry($http, { unencrypted: true })); - } } /** diff --git a/src/plugins/telemetry/common/constants.ts b/src/plugins/telemetry/common/constants.ts index 57aabc5175291..7b7694ed9aed7 100644 --- a/src/plugins/telemetry/common/constants.ts +++ b/src/plugins/telemetry/common/constants.ts @@ -27,3 +27,13 @@ export const REPORT_INTERVAL_MS = 86400000; * Key for the localStorage service */ export const LOCALSTORAGE_KEY = 'telemetry.data'; + +/** + * Link to Advanced Settings. + */ +export const PATH_TO_ADVANCED_SETTINGS = 'kibana#/management/kibana/settings'; + +/** + * Link to the Elastic Telemetry privacy statement. + */ +export const PRIVACY_STATEMENT_URL = `https://www.elastic.co/legal/privacy-statement`; diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_opt_in_banner.js b/src/plugins/telemetry/public/components/index.ts similarity index 63% rename from src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_opt_in_banner.js rename to src/plugins/telemetry/public/components/index.ts index 45539c4eea46c..0adbeb493a222 100644 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_opt_in_banner.js +++ b/src/plugins/telemetry/public/components/index.ts @@ -17,14 +17,4 @@ * under the License. */ -/** - * Determine if the notice banner should be displayed. - * - * This method can have side-effects related to deprecated config settings. - * - * @param {Object} telemetryOptInProvider The Telemetry opt-in provider singleton. - * @return {Boolean} {@code true} if the banner should be displayed. {@code false} otherwise. - */ -export async function shouldShowOptInBanner(telemetryOptInProvider) { - return telemetryOptInProvider.notifyUserAboutOptInDefault(); -} +export { OptInExampleFlyout } from './opt_in_example_flyout'; diff --git a/src/plugins/telemetry/public/components/opt_in_example_flyout.test.tsx b/src/plugins/telemetry/public/components/opt_in_example_flyout.test.tsx new file mode 100644 index 0000000000000..fe5e73a244893 --- /dev/null +++ b/src/plugins/telemetry/public/components/opt_in_example_flyout.test.tsx @@ -0,0 +1,31 @@ +/* + * 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 React from 'react'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { OptInExampleFlyout } from './opt_in_example_flyout'; + +describe('OptInDetailsComponent', () => { + it('renders as expected', () => { + expect( + shallowWithIntl( + [])} onClose={jest.fn()} /> + ) + ).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/telemetry/public/components/opt_in_example_flyout.tsx b/src/plugins/telemetry/public/components/opt_in_example_flyout.tsx new file mode 100644 index 0000000000000..9ecbd4df20560 --- /dev/null +++ b/src/plugins/telemetry/public/components/opt_in_example_flyout.tsx @@ -0,0 +1,162 @@ +/* + * 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 * as React from 'react'; + +import { + EuiCallOut, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiLoadingSpinner, + EuiPortal, // EuiPortal is a temporary requirement to use EuiFlyout with "ownFocus" + EuiText, + EuiTextColor, + EuiTitle, +} from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n/react'; + +interface Props { + fetchExample: () => Promise; + onClose: () => void; +} + +interface State { + isLoading: boolean; + hasPrivilegeToRead: boolean; + data: any[] | null; +} + +/** + * React component for displaying the example data associated with the Telemetry opt-in banner. + */ +export class OptInExampleFlyout extends React.PureComponent { + public readonly state: State = { + data: null, + isLoading: true, + hasPrivilegeToRead: false, + }; + + async componentDidMount() { + try { + const { fetchExample } = this.props; + const clusters = await fetchExample(); + this.setState({ + data: Array.isArray(clusters) ? clusters : null, + isLoading: false, + hasPrivilegeToRead: true, + }); + } catch (err) { + this.setState({ + isLoading: false, + hasPrivilegeToRead: err.status !== 403, + }); + } + } + + renderBody({ data, isLoading, hasPrivilegeToRead }: State) { + if (isLoading) { + return ( + + + + + + ); + } + + if (!hasPrivilegeToRead) { + return ( + + } + color="danger" + iconType="cross" + > + + + ); + } + + if (data === null) { + return ( + + } + color="danger" + iconType="cross" + > + + + ); + } + + return {JSON.stringify(data, null, 2)}; + } + + render() { + return ( + + + + +

+ +

+
+ + + + + +
+ {this.renderBody(this.state)} +
+
+ ); + } +} diff --git a/src/legacy/core_plugins/telemetry/public/components/opted_in_notice_banner.test.tsx b/src/plugins/telemetry/public/components/opted_in_notice_banner.test.tsx similarity index 100% rename from src/legacy/core_plugins/telemetry/public/components/opted_in_notice_banner.test.tsx rename to src/plugins/telemetry/public/components/opted_in_notice_banner.test.tsx diff --git a/src/legacy/core_plugins/telemetry/public/components/opted_in_notice_banner.tsx b/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx similarity index 81% rename from src/legacy/core_plugins/telemetry/public/components/opted_in_notice_banner.tsx rename to src/plugins/telemetry/public/components/opted_in_notice_banner.tsx index e37fa73ebe7b8..5d6a12df419bb 100644 --- a/src/legacy/core_plugins/telemetry/public/components/opted_in_notice_banner.tsx +++ b/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx @@ -20,25 +20,19 @@ /* eslint @elastic/eui/href-or-on-click:0 */ import * as React from 'react'; -import chrome from 'ui/chrome'; import { EuiButton, EuiLink, EuiCallOut, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { PATH_TO_ADVANCED_SETTINGS } from '../../common/constants'; interface Props { onSeenBanner: () => any; + privacyStatementHref: string; + disableTelemetryHref: string; } -/** - * React component for displaying the Telemetry opt-in notice. - */ export class OptedInBanner extends React.PureComponent { - onLinkClick = () => { - this.props.onSeenBanner(); - return; - }; - render() { + const { privacyStatementHref, disableTelemetryHref, onSeenBanner } = this.props; + return ( { privacyStatementLink: ( @@ -59,10 +53,7 @@ export class OptedInBanner extends React.PureComponent { ), disableLink: ( - + { }} /> - + { +export class TelemetryPlugin implements Plugin { // private config: PublicConfigType; private isUnauthenticated: boolean = false; // constructor(initializerContext: PluginInitializerContext) { // this.config = initializerContext.config.get(); // } - public setup({ http }: CoreSetup): TelemetrySetup { + public setup({ http }: CoreSetup) { this.isUnauthenticated = this.getIsUnauthenticated(http); - return {}; } - public start({ injectedMetadata, http }: CoreStart): TelemetryStart { + public start({ injectedMetadata, http, notifications, overlays }: CoreStart): TelemetryStart { const isPluginEnabled = injectedMetadata.getInjectedVar('telemetryEnabled') as boolean; const sendUsageFrom = injectedMetadata.getInjectedVar('telemetrySendUsageFrom') as | 'browser' | 'server'; - const telemetryService = new TelemetryService({ http, injectedMetadata }); + + const telemetryService = new TelemetryService({ + http, + injectedMetadata, + notifications, + }); + + const telemetryNotifications = new TelemetryNotifications({ + http, + injectedMetadata, + notifications, + overlays, + }); this.maybeStartTelemetryPoller({ telemetryService, @@ -61,8 +73,13 @@ export class TelemetryPlugin implements Plugin { sendUsageFrom, }); + this.maybeShowOptedInNotificationBanner({ + telemetryNotifications, + }); + return { telemetryService, + telemetryNotifications, }; } @@ -88,4 +105,23 @@ export class TelemetryPlugin implements Plugin { } } } + + private maybeShowOptedInNotificationBanner({ + telemetryNotifications, + }: { + telemetryNotifications: TelemetryNotifications; + }) { + if (this.isUnauthenticated) { + return; + } + // and no banner on status page + // if (chrome.getApp().id === 'status_page') { + // return; + // } + + const shouldShowBanner = telemetryNotifications.shouldShowOptedInNoticeBanner(); + if (shouldShowBanner) { + telemetryNotifications.renderOptedInNoticeBanner(); + } + } } diff --git a/src/plugins/telemetry/public/services/index.ts b/src/plugins/telemetry/public/services/index.ts index d520fb6df2f9e..ff4404c626fe0 100644 --- a/src/plugins/telemetry/public/services/index.ts +++ b/src/plugins/telemetry/public/services/index.ts @@ -19,3 +19,4 @@ export { TelemetrySender } from './telemetry_sender'; export { TelemetryService } from './telemetry_service'; +export { TelemetryNotifications } from './telemetry_notifications'; diff --git a/src/plugins/telemetry/public/services/telemetry_notifications/index.ts b/src/plugins/telemetry/public/services/telemetry_notifications/index.ts new file mode 100644 index 0000000000000..c6ba2cce1edb0 --- /dev/null +++ b/src/plugins/telemetry/public/services/telemetry_notifications/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { TelemetryNotifications } from './telemetry_notifications'; diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_notice_banner.test.js b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.test.tsx similarity index 100% rename from src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_notice_banner.test.js rename to src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.test.tsx diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_notice_banner.js b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx similarity index 58% rename from src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_notice_banner.js rename to src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx index 2aa53db11c1d9..85838f3d12e7b 100644 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_notice_banner.js +++ b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx @@ -18,21 +18,24 @@ */ import React from 'react'; - -import { banners } from 'ui/notify'; +import { CoreStart } from 'kibana/public'; import { OptedInBanner } from '../../components/opted_in_notice_banner'; +import { PATH_TO_ADVANCED_SETTINGS, PRIVACY_STATEMENT_URL } from '../../../common/constants'; +import { toMountPoint } from '../../../../kibana_react/public'; -/** - * Render the Telemetry Opt-in notice banner. - * - * @param {Object} telemetryOptInProvider The telemetry opt-in provider. - * @param {Object} _banners Banners singleton, which can be overridden for tests. - */ -export function renderOptedInBanner(telemetryOptInProvider, { _banners = banners } = {}) { - const bannerId = _banners.add({ - component: , - priority: 10000, - }); +interface RenderBannerConfig { + overlays: CoreStart['overlays']; + onSeen: () => void; +} +export function renderOptedInBanner({ onSeen, overlays }: RenderBannerConfig) { + const mount = toMountPoint( + + ); + const bannerId = overlays.banners.add(mount, 10000); - telemetryOptInProvider.setOptInBannerNoticeId(bannerId); + return bannerId; } diff --git a/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts new file mode 100644 index 0000000000000..174ad520abe51 --- /dev/null +++ b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts @@ -0,0 +1,98 @@ +/* + * 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 { CoreStart } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; +import { renderOptedInBanner } from './render_opted_in_notice_banner'; + +interface TelemetryNotificationsConstructor { + notifications: CoreStart['notifications']; + injectedMetadata: CoreStart['injectedMetadata']; + overlays: CoreStart['overlays']; + http: CoreStart['http']; +} + +export class TelemetryNotifications { + private readonly overlays: CoreStart['overlays']; + private readonly notifications: CoreStart['notifications']; + private readonly http: CoreStart['http']; + + private optedInBannerNoticeId?: string; + // private optInBannerId?: string; + private showOptedInNoticeBanner: boolean; + + constructor({ + notifications, + http, + overlays, + injectedMetadata, + }: TelemetryNotificationsConstructor) { + this.notifications = notifications; + this.http = http; + this.overlays = overlays; + + this.showOptedInNoticeBanner = injectedMetadata.getInjectedVar( + 'telemetryNotifyUserAboutOptInDefault' + ) as boolean; + } + + public shouldShowOptedInNoticeBanner() { + return this.showOptedInNoticeBanner; + } + + public renderOptedInNoticeBanner = () => { + const bannerId = renderOptedInBanner({ + onSeen: this.setOptedInNoticeSeen, + overlays: this.overlays, + }); + + this.optedInBannerNoticeId = bannerId; + }; + + public setOptedInNoticeSeen = async (): Promise => { + // If they've seen the notice don't spam the API + if (!this.showOptedInNoticeBanner) { + return; + } + + const optInBannerNoticeId = this.optedInBannerNoticeId; + if (optInBannerNoticeId) { + this.overlays.banners.remove(optInBannerNoticeId); + } + + try { + await this.http.put('/api/telemetry/v2/userHasSeenNotice'); + this.showOptedInNoticeBanner = false; + } catch (error) { + this.notifications.toasts.addError(error, { + title: i18n.translate('telemetry.optInNoticeSeenErrorTitle', { + defaultMessage: 'Error', + }), + toastMessage: i18n.translate('telemetry.optInNoticeSeenErrorToastText', { + defaultMessage: 'An error occurred dismissing the notice', + }), + }); + this.showOptedInNoticeBanner = true; + } + }; +} + +// telemetryEnabled && +// !telemetryOptInService.getOptIn() && +// telemetryOptInService.canChangeOptInStatus() diff --git a/src/plugins/telemetry/public/services/telemetry_sender.test.ts b/src/plugins/telemetry/public/services/telemetry_sender.test.ts index 45a0653cd7a54..bd6fe55376f10 100644 --- a/src/plugins/telemetry/public/services/telemetry_sender.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_sender.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Telemetry } from './telemetry'; +import { TelemetrySender } from './telemetry_sender'; import { REPORT_INTERVAL_MS, LOCALSTORAGE_KEY } from '../../common/constants'; describe('telemetry class', () => { diff --git a/src/plugins/telemetry/public/services/telemetry_service.ts b/src/plugins/telemetry/public/services/telemetry_service.ts index 1a4cc9d637995..d212c01d38a31 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.ts @@ -23,14 +23,10 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { CoreStart } from 'kibana/public'; -const bannerId: string | null = null; -const optInBannerNoticeId: string | null = null; -const currentOptInStatus = false; -const telemetryNotifyUserAboutOptInDefault = true; - interface TelemetryServiceConstructor { http: CoreStart['http']; injectedMetadata: CoreStart['injectedMetadata']; + notifications: CoreStart['notifications']; reportOptInStatusChange?: boolean; } @@ -38,46 +34,53 @@ export class TelemetryService { private readonly http: CoreStart['http']; private readonly injectedMetadata: CoreStart['injectedMetadata']; private readonly reportOptInStatusChange: boolean; + private readonly notifications: CoreStart['notifications']; private isOptedIn: boolean; constructor({ http, injectedMetadata, + notifications, reportOptInStatusChange = true, }: TelemetryServiceConstructor) { const isOptedIn = injectedMetadata.getInjectedVar('telemetryOptedIn') as boolean; this.reportOptInStatusChange = reportOptInStatusChange; this.injectedMetadata = injectedMetadata; + this.notifications = notifications; this.http = http; this.isOptedIn = isOptedIn; } - public getCanChangeOptInStatus() { + public getCanChangeOptInStatus = () => { const allowChangingOptInStatus = this.injectedMetadata.getInjectedVar( 'allowChangingOptInStatus' ) as boolean; return allowChangingOptInStatus; - } + }; - public getOptInStatusUrl() { + public getOptInStatusUrl = () => { const telemetryOptInStatusUrl = this.injectedMetadata.getInjectedVar( 'telemetryOptInStatusUrl' ) as string; return telemetryOptInStatusUrl; - } + }; - public getTelemetryUrl() { + public getTelemetryUrl = () => { const telemetryUrl = this.injectedMetadata.getInjectedVar('telemetryUrl') as string; return telemetryUrl; - } + }; - public getIsOptedIn() { + public getIsOptedIn = () => { return this.isOptedIn; - } + }; - public async fetchTelemetry({ unencrypted = false } = {}) { + public fetchExample = async () => { + return await this.fetchTelemetry({ unencrypted: true }); + }; + + public fetchTelemetry = async ({ unencrypted = false } = {}) => { const now = moment(); return this.http.post(`/api/telemetry/v2/clusters/_stats`, { body: JSON.stringify({ @@ -88,9 +91,9 @@ export class TelemetryService { }, }), }); - } + }; - public async setOptIn(optedIn: boolean): Promise { + public setOptIn = async (optedIn: boolean): Promise => { const canChangeOptInStatus = this.getCanChangeOptInStatus(); if (!canChangeOptInStatus) { return false; @@ -105,7 +108,7 @@ export class TelemetryService { } this.isOptedIn = optedIn; } catch (err) { - toastNotifications.addError(err, { + this.notifications.toasts.addError(err, { title: i18n.translate('telemetry.optInErrorToastTitle', { defaultMessage: 'Error', }), @@ -118,9 +121,9 @@ export class TelemetryService { } return true; - } + }; - private async reportOptInStatus(OptInStatus: boolean) { + private reportOptInStatus = async (OptInStatus: boolean): Promise => { const telemetryOptInStatusUrl = this.getOptInStatusUrl(); try { @@ -128,7 +131,7 @@ export class TelemetryService { // body: JSON.stringify({ enabled, unencrypted: false }) // }); - return await fetch(telemetryOptInStatusUrl, { + await fetch(telemetryOptInStatusUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -139,47 +142,13 @@ export class TelemetryService { // Sending the ping is best-effort. Telemetry tries to send the ping once and discards it immediately if sending fails. // swallow any errors } - } + }; } -// getBannerId: () => bannerId, -// getOptInBannerNoticeId: () => optInBannerNoticeId, -// getOptIn: () => currentOptInStatus, -// canChangeOptInStatus: () => allowChangingOptInStatus, -// notifyUserAboutOptInDefault: () => telemetryNotifyUserAboutOptInDefault, -// setBannerId(id: string) { -// bannerId = id; -// }, -// setOptInBannerNoticeId(id: string) { -// optInBannerNoticeId = id; -// }, -// setOptInNoticeSeen: async () => { -// const $http = $injector.get('$http'); - -// // If they've seen the notice don't spam the API -// if (!telemetryNotifyUserAboutOptInDefault) { -// return telemetryNotifyUserAboutOptInDefault; -// } - -// if (optInBannerNoticeId) { -// banners.remove(optInBannerNoticeId); -// } - -// try { -// await $http.put(chrome.addBasePath('/api/telemetry/v2/userHasSeenNotice')); -// telemetryNotifyUserAboutOptInDefault = false; -// } catch (error) { -// toastNotifications.addError(error, { -// title: i18n.translate('telemetry.optInNoticeSeenErrorTitle', { -// defaultMessage: 'Error', -// }), -// toastMessage: i18n.translate('telemetry.optInNoticeSeenErrorToastText', { -// defaultMessage: 'An error occurred dismissing the notice', -// }), -// }); -// telemetryNotifyUserAboutOptInDefault = true; -// } - -// return telemetryNotifyUserAboutOptInDefault; -// }, -// }; +// export const shouldShowTelemetryOptIn = () => { +// return ( +// telemetryEnabled && +// !telemetryOptInService.getOptIn() && +// telemetryOptInService.canChangeOptInStatus() +// ); +// }; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/index.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/index.ts similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/index.js rename to x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/index.ts diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.tsx similarity index 84% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.js rename to x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.tsx index 5e570ae955dbf..cc0f2189c7f98 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.tsx @@ -6,26 +6,31 @@ import React, { Fragment } from 'react'; import { EuiLink, EuiCheckbox, EuiSpacer, EuiText, EuiTitle, EuiPopover } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { - shouldShowTelemetryOptIn, - getTelemetryFetcher, - PRIVACY_STATEMENT_URL, OptInExampleFlyout, + PRIVACY_STATEMENT_URL, + TelemetryPluginStart, } from '../../lib/telemetry'; -import { FormattedMessage } from '@kbn/i18n/react'; -export class TelemetryOptIn extends React.Component { - constructor() { - super(); - this.state = { - showMoreTelemetryInfo: false, - isOptingInToTelemetry: false, - showExample: false, - }; - } - isOptingInToTelemetry = () => { - return this.state.isOptingInToTelemetry; +interface State { + showMoreTelemetryInfo: boolean; + showExample: boolean; +} + +interface Props { + onOptInChange: (isOptingInToTelemetry: boolean) => void; + isOptingInToTelemetry: boolean; + isStartTrial: boolean; + telemetry: TelemetryPluginStart; +} + +export class TelemetryOptIn extends React.Component { + state: State = { + showMoreTelemetryInfo: false, + showExample: false, }; + closeReadMorePopover = () => { this.setState({ showMoreTelemetryInfo: false }); }; @@ -37,20 +42,22 @@ export class TelemetryOptIn extends React.Component { this.setState({ showExample: true }); this.closeReadMorePopover(); }; - onChangeOptIn = event => { + onChangeOptIn = (event: any) => { const isOptingInToTelemetry = event.target.checked; - this.setState({ isOptingInToTelemetry }); + const { onOptInChange } = this.props; + onOptInChange(isOptingInToTelemetry); }; + render() { - const { showMoreTelemetryInfo, isOptingInToTelemetry, showExample } = this.state; - const { isStartTrial } = this.props; + const { showMoreTelemetryInfo, showExample } = this.state; + const { isStartTrial, isOptingInToTelemetry, telemetry } = this.props; let example = null; if (showExample) { example = ( this.setState({ showExample: false })} - fetchTelemetry={getTelemetryFetcher} + fetchExample={telemetry.telemetryService.fetchExample} /> ); } @@ -123,7 +130,7 @@ export class TelemetryOptIn extends React.Component { ); - return shouldShowTelemetryOptIn() ? ( + return ( {example} {toCurrentCustomers} @@ -144,6 +151,6 @@ export class TelemetryOptIn extends React.Component { onChange={this.onChangeOptIn} /> - ) : null; + ); } } diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.js deleted file mode 100644 index 10da5d7705a8c..0000000000000 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.js +++ /dev/null @@ -1,36 +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 { fetchTelemetry } from '../../../../../../../../src/legacy/core_plugins/telemetry/public/hacks/fetch_telemetry'; -export { PRIVACY_STATEMENT_URL } from '../../../../../../../../src/legacy/core_plugins/telemetry/common/constants'; -export { TelemetryOptInProvider } from '../../../../../../../../src/legacy/core_plugins/telemetry/public/services'; -export { OptInExampleFlyout } from '../../../../../../../../src/legacy/core_plugins/telemetry/public/components'; - -let telemetryEnabled; -let httpClient; -let telemetryOptInService; -export const setTelemetryEnabled = isTelemetryEnabled => { - telemetryEnabled = isTelemetryEnabled; -}; -export const setHttpClient = anHttpClient => { - httpClient = anHttpClient; -}; -export const setTelemetryOptInService = aTelemetryOptInService => { - telemetryOptInService = aTelemetryOptInService; -}; -export const optInToTelemetry = async enableTelemetry => { - await telemetryOptInService.setOptIn(enableTelemetry); -}; -export const shouldShowTelemetryOptIn = () => { - return ( - telemetryEnabled && - !telemetryOptInService.getOptIn() && - telemetryOptInService.canChangeOptInStatus() - ); -}; -export const getTelemetryFetcher = () => { - return fetchTelemetry(httpClient, { unencrypted: true }); -}; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts b/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts new file mode 100644 index 0000000000000..b97f6bd237755 --- /dev/null +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export { TelemetryPluginStart } from '../../../../../../../../src/plugins/telemetry/public'; +export { OptInExampleFlyout } from '../../../../../../../../src/plugins/telemetry/public/components'; +export { PRIVACY_STATEMENT_URL } from '../../../../../../../../src/plugins/telemetry/common/constants'; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js index e14d392fe6706..6b8709c8d532a 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js @@ -11,6 +11,8 @@ import { StartTrial } from './start_trial'; import { AddLicense } from './add_license'; import { RequestTrialExtension } from './request_trial_extension'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { npStart } from 'ui/new_platform'; export const LicenseDashboard = ({ setBreadcrumb } = { setBreadcrumb: () => {} }) => { useEffect(() => { @@ -25,7 +27,7 @@ export const LicenseDashboard = ({ setBreadcrumb } = { setBreadcrumb: () => {} } - + diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/index.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/index.ts similarity index 95% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/index.js rename to x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/index.ts index b9b33e7e3f2cb..1b3c956edc3ab 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/index.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/index.ts @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ +// @ts-ignore export { StartTrial } from './start_trial.container'; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx similarity index 87% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.js rename to x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx index 532c1d5e1a32f..77a75d1dced6b 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { Component, Ref } from 'react'; import { EuiButtonEmpty, @@ -22,24 +22,46 @@ import { EuiModalHeaderTitle, } from '@elastic/eui'; -import { TelemetryOptIn } from '../../../components/telemetry_opt_in'; -import { optInToTelemetry } from '../../../lib/telemetry'; import { FormattedMessage } from '@kbn/i18n/react'; +import { TelemetryOptIn } from '../../../components/telemetry_opt_in'; import { EXTERNAL_LINKS } from '../../../../../../common/constants'; import { getDocLinks } from '../../../lib/docs_links'; +import { TelemetryPluginStart } from '../../../lib/telemetry'; + +interface Props { + loadTrialStatus: () => void; + startLicenseTrial: () => void; + telemetry: TelemetryPluginStart; + shouldShowStartTrial: boolean; +} + +interface State { + shouldShowTelemetryOptIn: boolean; + showConfirmation: boolean; + isOptingInToTelemetry: boolean; +} + +export class StartTrial extends Component { + cancelRef: any; + confirmRef: any; + + state: State = { + shouldShowTelemetryOptIn: true, + showConfirmation: false, + isOptingInToTelemetry: false, + }; -export class StartTrial extends React.PureComponent { - constructor(props) { - super(props); - this.state = { showConfirmation: false }; - } UNSAFE_componentWillMount() { this.props.loadTrialStatus(); } - startLicenseTrial = () => { - const { startLicenseTrial } = this.props; - if (this.telemetryOptIn.isOptingInToTelemetry()) { - optInToTelemetry(true); + + onOptInChange = (isOptingInToTelemetry: boolean) => { + this.setState({ isOptingInToTelemetry }); + }; + onStartLicenseTrial = () => { + const { telemetry, startLicenseTrial } = this.props; + if (this.state.isOptingInToTelemetry) { + telemetry.telemetryService.setOptIn(true); } startLicenseTrial(); }; @@ -158,12 +180,14 @@ export class StartTrial extends React.PureComponent { - { - this.telemetryOptIn = ref; - }} - /> + {this.state.shouldShowTelemetryOptIn && ( + + )} @@ -182,7 +206,7 @@ export class StartTrial extends React.PureComponent { { errorMessage: getUploadErrorMessage(state), applying: isApplying(state), currentLicenseType: getLicenseType(state) || '', + telemetry: npStart.plugins.telemetry, }; }; const mapDispatchToProps = { diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js index d814d3e42d49f..8522219124dd9 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js @@ -22,20 +22,30 @@ import { EuiPageContentBody, } from '@elastic/eui'; import { TelemetryOptIn } from '../../components/telemetry_opt_in'; -import { optInToTelemetry } from '../../lib/telemetry'; import { FormattedMessage } from '@kbn/i18n/react'; export class UploadLicense extends React.PureComponent { + constructor(props) { + super(props); + this.state = { + isOptingInToTelemetry: false, + shouldShowTelemetryOptIn: true, + }; + } componentDidMount() { this.props.setBreadcrumb('upload'); this.props.addUploadErrorMessage(''); } + onOptInChange = isOptingInToTelemetry => { + this.setState({ isOptingInToTelemetry }); + }; send = acknowledge => { const file = this.file; const fr = new FileReader(); + fr.onload = ({ target: { result } }) => { - if (this.telemetryOptIn.isOptingInToTelemetry()) { - optInToTelemetry(true); + if (this.state.isOptingInToTelemetry) { + this.props.telemetry.telemetryService.setOptIn(true); } this.props.uploadLicense(result, this.props.currentLicenseType, acknowledge); }; @@ -170,11 +180,13 @@ export class UploadLicense extends React.PureComponent { - { - this.telemetryOptIn = ref; - }} - /> + {this.state.shouldShowTelemetryOptIn && ( + + )} diff --git a/x-pack/legacy/plugins/license_management/public/register_route.ts b/x-pack/legacy/plugins/license_management/public/register_route.ts index fc1678a866ad3..fe34514303424 100644 --- a/x-pack/legacy/plugins/license_management/public/register_route.ts +++ b/x-pack/legacy/plugins/license_management/public/register_route.ts @@ -15,15 +15,6 @@ import routes from 'ui/routes'; import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; import { plugin } from './np_ready'; - -import { - setTelemetryOptInService, - setTelemetryEnabled, - setHttpClient, - TelemetryOptInProvider, - // @ts-ignore -} from './np_ready/application/lib/telemetry'; - import { BASE_PATH } from '../common/constants'; const licenseManagementUiEnabled = chrome.getInjected('licenseManagementUiEnabled'); @@ -51,15 +42,6 @@ if (licenseManagementUiEnabled) { }); }; - const initializeTelemetry = ($injector: any) => { - const telemetryEnabled = npStart.core.injectedMetadata.getInjectedVar('telemetryEnabled'); - const Private = $injector.get('Private'); - const telemetryOptInProvider = Private(TelemetryOptInProvider); - setTelemetryOptInService(telemetryOptInProvider); - setTelemetryEnabled(telemetryEnabled); - setHttpClient($injector.get('$http')); - }; - const template = `
`; @@ -69,8 +51,6 @@ if (licenseManagementUiEnabled) { controllerAs: 'licenseManagement', controller: class LicenseManagementController { constructor($injector: any, $rootScope: any, $scope: any, $route: any) { - initializeTelemetry($injector); - $scope.$$postDigest(() => { const element = document.getElementById('licenseReactRoot')!; From 980fae1657297acffafe97f6e166dde35cbc05c4 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Thu, 30 Jan 2020 22:11:38 +0200 Subject: [PATCH 04/41] fully migrated public to NP --- .../public/home/np_ready/components/home.js | 5 +- .../home/np_ready/components/welcome.tsx | 52 +++-- src/legacy/core_plugins/telemetry/index.ts | 28 +-- .../opt_in_details_component.test.tsx.snap | 50 ----- .../opt_in_message.test.tsx.snap | 24 -- .../opted_in_notice_banner.test.tsx.snap | 51 ----- .../__snapshots__/telemetry_form.test.js.snap | 80 ------- .../telemetry/public/components/index.ts | 24 -- .../components/opt_in_details_component.tsx | 163 -------------- .../public/components/telemetry_form.test.js | 83 ------- .../public/hacks/__tests__/telemetry.js | 29 --- .../telemetry/public/hacks/fetch_telemetry.js | 44 ---- .../public/hacks/telemetry_opt_in.js | 24 -- .../hacks/welcome_banner/click_banner.js | 77 ------- .../hacks/welcome_banner/click_banner.test.js | 128 ----------- .../handle_old_settings.test.js | 208 ------------------ .../hacks/welcome_banner/inject_banner.js | 70 ------ .../hacks/welcome_banner/render_banner.js | 46 ---- .../welcome_banner/should_show_banner.js | 40 ---- .../welcome_banner/should_show_banner.test.js | 91 -------- .../telemetry/public/services/index.ts | 21 -- .../telemetry/public/services/path.ts | 25 --- .../public/services/telemetry_opt_in.test.js | 148 ------------- .../services/telemetry_opt_in.test.mocks.js | 60 ----- .../public/services/telemetry_opt_in.ts | 154 ------------- .../views/management/{index.js => index.ts} | 0 .../{management.js => management.tsx} | 29 +-- .../handle_old_settings.ts} | 59 ++--- .../handle_old_settings/index.ts} | 2 +- .../core_plugins/telemetry/server/index.ts | 1 + .../telemetry/public/components/index.ts | 1 + .../public/components/opt_in_banner.test.ts} | 0 .../public/components/opt_in_banner.tsx} | 13 +- .../public/components/opt_in_message.test.tsx | 0 .../public/components/opt_in_message.tsx | 7 +- .../components/opted_in_notice_banner.tsx | 2 +- .../telemetry_management_section.tsx} | 76 +++---- src/plugins/telemetry/public/index.ts | 5 +- src/plugins/telemetry/public/plugin.ts | 68 +++--- .../render_opt_in_banner.tsx} | 29 +-- .../render_opted_in_notice_banner.tsx | 2 +- .../telemetry_notifications.test.ts | 54 +++++ .../telemetry_notifications.ts | 94 ++++---- .../services/telemetry_service.test.ts} | 31 +++ .../public/services/telemetry_service.ts | 45 ++-- .../telemetry/public/telemetry.mock.ts | 81 +++++++ .../np_ready/application/lib/telemetry.ts | 17 +- .../start_trial/start_trial.tsx | 19 +- .../sections/upload_license/upload_license.js | 20 +- 49 files changed, 450 insertions(+), 1930 deletions(-) delete mode 100644 src/legacy/core_plugins/telemetry/public/components/__snapshots__/opt_in_details_component.test.tsx.snap delete mode 100644 src/legacy/core_plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap delete mode 100644 src/legacy/core_plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap delete mode 100644 src/legacy/core_plugins/telemetry/public/components/__snapshots__/telemetry_form.test.js.snap delete mode 100644 src/legacy/core_plugins/telemetry/public/components/index.ts delete mode 100644 src/legacy/core_plugins/telemetry/public/components/opt_in_details_component.tsx delete mode 100644 src/legacy/core_plugins/telemetry/public/components/telemetry_form.test.js delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/__tests__/telemetry.js delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/fetch_telemetry.js delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/telemetry_opt_in.js delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.js delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.test.js delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/handle_old_settings.test.js delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/inject_banner.js delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_banner.js delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_banner.js delete mode 100644 src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_banner.test.js delete mode 100644 src/legacy/core_plugins/telemetry/public/services/index.ts delete mode 100644 src/legacy/core_plugins/telemetry/public/services/path.ts delete mode 100644 src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.js delete mode 100644 src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.mocks.js delete mode 100644 src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts rename src/legacy/core_plugins/telemetry/public/views/management/{index.js => index.ts} (100%) rename src/legacy/core_plugins/telemetry/public/views/management/{management.js => management.tsx} (60%) rename src/legacy/core_plugins/telemetry/{public/hacks/welcome_banner/handle_old_settings.js => server/handle_old_settings/handle_old_settings.ts} (51%) rename src/legacy/core_plugins/telemetry/{public/hacks/welcome_banner/index.js => server/handle_old_settings/index.ts} (93%) rename src/{legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_banner.test.js => plugins/telemetry/public/components/opt_in_banner.test.ts} (100%) rename src/{legacy/core_plugins/telemetry/public/components/opt_in_banner_component.tsx => plugins/telemetry/public/components/opt_in_banner.tsx} (84%) rename src/{legacy/core_plugins => plugins}/telemetry/public/components/opt_in_message.test.tsx (100%) rename src/{legacy/core_plugins => plugins}/telemetry/public/components/opt_in_message.tsx (94%) rename src/{legacy/core_plugins/telemetry/public/components/telemetry_form.js => plugins/telemetry/public/components/telemetry_management_section.tsx} (78%) rename src/{legacy/core_plugins/telemetry/public/components/opt_in_details_component.test.tsx => plugins/telemetry/public/services/telemetry_notifications/render_opt_in_banner.tsx} (61%) create mode 100644 src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.test.ts rename src/{legacy/core_plugins/telemetry/public/hacks/__tests__/fetch_telemetry.js => plugins/telemetry/public/services/telemetry_service.test.ts} (57%) create mode 100644 src/plugins/telemetry/public/telemetry.mock.ts diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.js index a7c915c28d0ed..fff9e2fa8e11e 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.js +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.js @@ -261,5 +261,8 @@ Home.propTypes = { localStorage: PropTypes.object.isRequired, urlBasePath: PropTypes.string.isRequired, mlEnabled: PropTypes.bool.isRequired, - telemetry: PropTypes.shape({}), + telemetry: PropTypes.shape({ + telemetryService: PropTypes.any, + telemetryNotifications: PropTypes.any, + }), }; diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx index 13410b233a39e..b146005f83b62 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx @@ -44,7 +44,7 @@ import { SampleDataCard } from './sample_data'; interface Props { urlBasePath: string; onSkip: () => void; - telemetry: TelemetryPluginStart; + telemetry?: TelemetryPluginStart; } /** @@ -77,7 +77,9 @@ export class Welcome extends React.Component { componentDidMount() { const { telemetry } = this.props; this.services.trackUiMetric(METRIC_TYPE.LOADED, 'welcomeScreenMount'); - telemetry.telemetryNotifications.setOptedInNoticeSeen(); + if (telemetry) { + telemetry.telemetryNotifications.setOptedInNoticeSeen(); + } document.addEventListener('keydown', this.hideOnEsc); } @@ -87,6 +89,10 @@ export class Welcome extends React.Component { private renderTelemetryEnabledOrDisabledText = () => { const { telemetry } = this.props; + if (!telemetry) { + return null; + } + const isOptedIn = telemetry.telemetryService.getIsOptedIn(); if (isOptedIn) { return ( @@ -122,7 +128,7 @@ export class Welcome extends React.Component { }; render() { - const { urlBasePath } = this.props; + const { urlBasePath, telemetry } = this.props; return (
@@ -157,24 +163,28 @@ export class Welcome extends React.Component { onDecline={this.onSampleDataDecline} /> - - - - - - {this.renderTelemetryEnabledOrDisabledText()} - - + {!!telemetry && ( + + + + + + + {this.renderTelemetryEnabledOrDisabledText()} + + + + )}
diff --git a/src/legacy/core_plugins/telemetry/index.ts b/src/legacy/core_plugins/telemetry/index.ts index 0e870911478d9..fba2fad103eff 100644 --- a/src/legacy/core_plugins/telemetry/index.ts +++ b/src/legacy/core_plugins/telemetry/index.ts @@ -29,7 +29,13 @@ import { getConfigPath } from '../../../core/server/path'; import mappings from './mappings.json'; import { CONFIG_TELEMETRY, getConfigTelemetryDesc } from './common/constants'; import { getXpackConfigWithDeprecated } from './common/get_xpack_config_with_deprecated'; -import { telemetryPlugin, replaceTelemetryInjectedVars, FetcherTask, PluginsSetup } from './server'; +import { + telemetryPlugin, + replaceTelemetryInjectedVars, + FetcherTask, + PluginsSetup, + handleOldSettings, +} from './server'; const ENDPOINT_VERSION = 'v2'; @@ -76,16 +82,6 @@ const telemetry = (kibana: any) => { }, uiExports: { managementSections: ['plugins/telemetry/views/management'], - uiSettingDefaults: { - [CONFIG_TELEMETRY]: { - name: i18n.translate('telemetry.telemetryConfigTitle', { - defaultMessage: 'Telemetry opt-in', - }), - description: getConfigTelemetryDesc(), - value: false, - readonly: true, - }, - }, savedObjectSchemas: { telemetry: { isNamespaceAgnostic: true, @@ -108,16 +104,16 @@ const telemetry = (kibana: any) => { allowChangingOptInStatus: config.get('telemetry.allowChangingOptInStatus'), telemetrySendUsageFrom: config.get('telemetry.sendUsageFrom'), telemetryNotifyUserAboutOptInDefault: false, + telemetryBanner1: 'lol', }; }, - hacks: ['plugins/telemetry/hacks/telemetry_opt_in'], mappings, }, postInit(server: Server) { const fetcherTask = new FetcherTask(server); fetcherTask.start(); }, - init(server: Server) { + async init(server: Server) { const { usageCollection } = server.newPlatform.setup.plugins; const initializerContext = { env: { @@ -145,6 +141,12 @@ const telemetry = (kibana: any) => { log: server.log, } as any) as CoreSetup; + try { + await handleOldSettings(server); + } catch (err) { + server.log(['warning', 'telemetry'], 'Unable to update legacy telemetry configs.'); + } + const pluginsSetup: PluginsSetup = { usageCollection, }; diff --git a/src/legacy/core_plugins/telemetry/public/components/__snapshots__/opt_in_details_component.test.tsx.snap b/src/legacy/core_plugins/telemetry/public/components/__snapshots__/opt_in_details_component.test.tsx.snap deleted file mode 100644 index 2601f691cd184..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/components/__snapshots__/opt_in_details_component.test.tsx.snap +++ /dev/null @@ -1,50 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`OptInDetailsComponent renders as expected 1`] = ` - - - - -

- -

-
- - - - - -
- - - - - - - -
-
-`; diff --git a/src/legacy/core_plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap b/src/legacy/core_plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap deleted file mode 100644 index c80485332fa8a..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`OptInMessage renders as expected 1`] = ` - - - - , - } - } - /> - -`; diff --git a/src/legacy/core_plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap b/src/legacy/core_plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap deleted file mode 100644 index 193205cd394e2..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap +++ /dev/null @@ -1,51 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`OptInDetailsComponent renders as expected 1`] = ` - - - - , - "privacyStatementLink": - - , - } - } - /> - - - - - -`; diff --git a/src/legacy/core_plugins/telemetry/public/components/__snapshots__/telemetry_form.test.js.snap b/src/legacy/core_plugins/telemetry/public/components/__snapshots__/telemetry_form.test.js.snap deleted file mode 100644 index 079a43e77616d..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/components/__snapshots__/telemetry_form.test.js.snap +++ /dev/null @@ -1,80 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TelemetryForm doesn't render form when not allowed to change optIn status 1`] = `""`; - -exports[`TelemetryForm renders as expected when allows to change optIn status 1`] = ` - - - - - - -

- -

-
-
-
- - -

- - - , - } - } - /> -

-

- - - -

- , - "type": "boolean", - "value": false, - } - } - /> -
-
-
-`; diff --git a/src/legacy/core_plugins/telemetry/public/components/index.ts b/src/legacy/core_plugins/telemetry/public/components/index.ts deleted file mode 100644 index 1fc55eadd1e10..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/components/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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. - */ - -// @ts-ignore -export { TelemetryForm } from './telemetry_form'; -export { OptInExampleFlyout } from './opt_in_details_component'; -export { OptInBanner } from './opt_in_banner_component'; -export { OptInMessage } from './opt_in_message'; diff --git a/src/legacy/core_plugins/telemetry/public/components/opt_in_details_component.tsx b/src/legacy/core_plugins/telemetry/public/components/opt_in_details_component.tsx deleted file mode 100644 index 12ab780e75990..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/components/opt_in_details_component.tsx +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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 * as React from 'react'; - -import { - EuiCallOut, - EuiCodeBlock, - EuiFlexGroup, - EuiFlexItem, - EuiFlyout, - EuiFlyoutHeader, - EuiFlyoutBody, - EuiLoadingSpinner, - EuiPortal, // EuiPortal is a temporary requirement to use EuiFlyout with "ownFocus" - EuiText, - EuiTextColor, - EuiTitle, -} from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n/react'; - -interface Props { - fetchTelemetry: () => Promise; - onClose: () => void; -} - -interface State { - isLoading: boolean; - hasPrivilegeToRead: boolean; - data: any[] | null; -} - -/** - * React component for displaying the example data associated with the Telemetry opt-in banner. - */ -export class OptInExampleFlyout extends React.PureComponent { - public readonly state: State = { - data: null, - isLoading: true, - hasPrivilegeToRead: false, - }; - - componentDidMount() { - this.props - .fetchTelemetry() - .then(response => - this.setState({ - data: Array.isArray(response.data) ? response.data : null, - isLoading: false, - hasPrivilegeToRead: true, - }) - ) - .catch(err => { - this.setState({ - isLoading: false, - hasPrivilegeToRead: err.status !== 403, - }); - }); - } - - renderBody({ data, isLoading, hasPrivilegeToRead }: State) { - if (isLoading) { - return ( - - - - - - ); - } - - if (!hasPrivilegeToRead) { - return ( - - } - color="danger" - iconType="cross" - > - - - ); - } - - if (data === null) { - return ( - - } - color="danger" - iconType="cross" - > - - - ); - } - - return {JSON.stringify(data, null, 2)}; - } - - render() { - return ( - - - - -

- -

-
- - - - - -
- {this.renderBody(this.state)} -
-
- ); - } -} diff --git a/src/legacy/core_plugins/telemetry/public/components/telemetry_form.test.js b/src/legacy/core_plugins/telemetry/public/components/telemetry_form.test.js deleted file mode 100644 index fe0c2c3449af1..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/components/telemetry_form.test.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 { mockInjectedMetadata } from '../services/telemetry_opt_in.test.mocks'; -import React from 'react'; -import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { TelemetryForm } from './telemetry_form'; -import { TelemetryOptInProvider } from '../services'; - -const buildTelemetryOptInProvider = () => { - const mockHttp = { - post: jest.fn(), - }; - - const mockInjector = { - get: key => { - switch (key) { - case '$http': - return mockHttp; - case 'allowChangingOptInStatus': - return true; - default: - return null; - } - }, - }; - - const chrome = { - addBasePath: url => url, - }; - - return new TelemetryOptInProvider(mockInjector, chrome); -}; - -describe('TelemetryForm', () => { - it('renders as expected when allows to change optIn status', () => { - mockInjectedMetadata({ telemetryOptedIn: null, allowChangingOptInStatus: true }); - - expect( - shallowWithIntl( - - ) - ).toMatchSnapshot(); - }); - - it(`doesn't render form when not allowed to change optIn status`, () => { - mockInjectedMetadata({ telemetryOptedIn: null, allowChangingOptInStatus: false }); - - expect( - shallowWithIntl( - - ) - ).toMatchSnapshot(); - }); -}); diff --git a/src/legacy/core_plugins/telemetry/public/hacks/__tests__/telemetry.js b/src/legacy/core_plugins/telemetry/public/hacks/__tests__/telemetry.js deleted file mode 100644 index 74f1de4934a78..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/__tests__/telemetry.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 { uiModules } from 'ui/modules'; - -// This overrides settings for other UI tests -uiModules - .get('kibana') - // disable stat reporting while running tests, - // MockInjector used in these tests is not impacted - .constant('telemetryEnabled', false) - .constant('telemetryOptedIn', null) - .constant('telemetryUrl', 'not.a.valid.url.0'); diff --git a/src/legacy/core_plugins/telemetry/public/hacks/fetch_telemetry.js b/src/legacy/core_plugins/telemetry/public/hacks/fetch_telemetry.js deleted file mode 100644 index ede81f638a3fc..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/fetch_telemetry.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 uiChrome from 'ui/chrome'; -import moment from 'moment'; - -/** - * Fetch Telemetry data by calling the Kibana API. - * - * @param {Object} $http The HTTP handler - * @param {String} basePath The base URI - * @param {Function} _moment moment.js, but injectable for tests - * @return {Promise} An array of cluster Telemetry objects. - */ -export function fetchTelemetry( - $http, - { basePath = uiChrome.getBasePath(), _moment = moment, unencrypted = false } = {} -) { - return $http.post(`${basePath}/api/telemetry/v2/clusters/_stats`, { - unencrypted, - timeRange: { - min: _moment() - .subtract(20, 'minutes') - .toISOString(), - max: _moment().toISOString(), - }, - }); -} diff --git a/src/legacy/core_plugins/telemetry/public/hacks/telemetry_opt_in.js b/src/legacy/core_plugins/telemetry/public/hacks/telemetry_opt_in.js deleted file mode 100644 index 4e53c7ecd7030..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/telemetry_opt_in.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 { uiModules } from 'ui/modules'; - -import { injectBanner } from './welcome_banner'; - -uiModules.get('telemetry/hacks').run(injectBanner); diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.js b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.js deleted file mode 100644 index 44971e2466794..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 React from 'react'; - -import { banners, toastNotifications } from 'ui/notify'; -import { EuiText } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; - -/** - * Handle clicks from the user on the opt-in banner. - * - * @param {Object} telemetryOptInProvider the telemetry opt-in provider - * @param {Boolean} optIn {@code true} to opt into telemetry. - * @param {Object} _banners Singleton banners. Can be overridden for tests. - * @param {Object} _toastNotifications Singleton toast notifications. Can be overridden for tests. - */ -export async function clickBanner( - telemetryOptInProvider, - optIn, - { _banners = banners, _toastNotifications = toastNotifications } = {} -) { - const bannerId = telemetryOptInProvider.getBannerId(); - let set = false; - - try { - set = await telemetryOptInProvider.setOptIn(optIn); - } catch (err) { - // set is already false - console.log('Unexpected error while trying to save setting.', err); - } - - if (set) { - _banners.remove(bannerId); - } else { - _toastNotifications.addDanger({ - title: ( - - ), - text: ( - -

- -

- - - -
- ), - }); - } -} diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.test.js b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.test.js deleted file mode 100644 index 0caabe826ae57..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.test.js +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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 { mockInjectedMetadata } from '../../services/telemetry_opt_in.test.mocks'; - -import sinon from 'sinon'; -import { uiModules } from 'ui/modules'; - -uiModules - .get('kibana') - // disable stat reporting while running tests, - // MockInjector used in these tests is not impacted - .constant('telemetryOptedIn', null); - -import { clickBanner } from './click_banner'; -import { TelemetryOptInProvider } from '../../services/telemetry_opt_in'; - -const getMockInjector = ({ simulateFailure }) => { - const get = sinon.stub(); - - const mockHttp = { - post: sinon.stub(), - }; - - if (simulateFailure) { - mockHttp.post.returns(Promise.reject(new Error('something happened'))); - } else { - mockHttp.post.returns(Promise.resolve({})); - } - - get.withArgs('$http').returns(mockHttp); - - return { get }; -}; - -const getTelemetryOptInProvider = ({ simulateFailure = false, simulateError = false } = {}) => { - const injector = getMockInjector({ simulateFailure }); - const chrome = { - addBasePath: url => url, - }; - - const provider = new TelemetryOptInProvider(injector, chrome, false); - - if (simulateError) { - provider.setOptIn = () => Promise.reject('unhandled error'); - } - - return provider; -}; - -describe('click_banner', () => { - it('sets setting successfully and removes banner', async () => { - const banners = { - remove: sinon.spy(), - }; - - const optIn = true; - const bannerId = 'bruce-banner'; - mockInjectedMetadata({ telemetryOptedIn: optIn, allowChangingOptInStatus: true }); - const telemetryOptInProvider = getTelemetryOptInProvider(); - - telemetryOptInProvider.setBannerId(bannerId); - - await clickBanner(telemetryOptInProvider, optIn, { _banners: banners }); - - expect(telemetryOptInProvider.getOptIn()).toBe(optIn); - expect(banners.remove.calledOnce).toBe(true); - expect(banners.remove.calledWith(bannerId)).toBe(true); - }); - - it('sets setting unsuccessfully, adds toast, and does not touch banner', async () => { - const toastNotifications = { - addDanger: sinon.spy(), - }; - const banners = { - remove: sinon.spy(), - }; - const optIn = true; - mockInjectedMetadata({ telemetryOptedIn: null, allowChangingOptInStatus: true }); - const telemetryOptInProvider = getTelemetryOptInProvider({ simulateFailure: true }); - - await clickBanner(telemetryOptInProvider, optIn, { - _banners: banners, - _toastNotifications: toastNotifications, - }); - - expect(telemetryOptInProvider.getOptIn()).toBe(null); - expect(toastNotifications.addDanger.calledOnce).toBe(true); - expect(banners.remove.notCalled).toBe(true); - }); - - it('sets setting unsuccessfully with error, adds toast, and does not touch banner', async () => { - const toastNotifications = { - addDanger: sinon.spy(), - }; - const banners = { - remove: sinon.spy(), - }; - const optIn = false; - mockInjectedMetadata({ telemetryOptedIn: null, allowChangingOptInStatus: true }); - const telemetryOptInProvider = getTelemetryOptInProvider({ simulateError: true }); - - await clickBanner(telemetryOptInProvider, optIn, { - _banners: banners, - _toastNotifications: toastNotifications, - }); - - expect(telemetryOptInProvider.getOptIn()).toBe(null); - expect(toastNotifications.addDanger.calledOnce).toBe(true); - expect(banners.remove.notCalled).toBe(true); - }); -}); diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/handle_old_settings.test.js b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/handle_old_settings.test.js deleted file mode 100644 index 8f05675565a5e..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/handle_old_settings.test.js +++ /dev/null @@ -1,208 +0,0 @@ -/* - * 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 { mockInjectedMetadata } from '../../services/telemetry_opt_in.test.mocks'; - -import sinon from 'sinon'; - -import { CONFIG_TELEMETRY } from '../../../common/constants'; -import { handleOldSettings } from './handle_old_settings'; -import { TelemetryOptInProvider } from '../../services/telemetry_opt_in'; - -const getTelemetryOptInProvider = (enabled, { simulateFailure = false } = {}) => { - const $http = { - post: async () => { - if (simulateFailure) { - return Promise.reject(new Error('something happened')); - } - return {}; - }, - }; - - const chrome = { - addBasePath: url => url, - }; - mockInjectedMetadata({ telemetryOptedIn: enabled, allowChangingOptInStatus: true }); - - const $injector = { - get: key => { - if (key === '$http') { - return $http; - } - throw new Error(`unexpected mock injector usage for ${key}`); - }, - }; - - return new TelemetryOptInProvider($injector, chrome, false); -}; - -describe('handle_old_settings', () => { - it('re-uses old "allowReport" setting and stays opted in', async () => { - const config = { - get: sinon.stub(), - remove: sinon.spy(), - set: sinon.stub(), - }; - - const telemetryOptInProvider = getTelemetryOptInProvider(null); - expect(telemetryOptInProvider.getOptIn()).toBe(null); - - config.get.withArgs('xPackMonitoring:allowReport', null).returns(true); - config.set.withArgs(CONFIG_TELEMETRY, true).returns(Promise.resolve(true)); - - expect(await handleOldSettings(config, telemetryOptInProvider)).toBe(false); - - expect(config.get.calledTwice).toBe(true); - expect(config.set.called).toBe(false); - - expect(config.remove.calledThrice).toBe(true); - expect(config.remove.getCall(0).args[0]).toBe('xPackMonitoring:allowReport'); - expect(config.remove.getCall(1).args[0]).toBe('xPackMonitoring:showBanner'); - expect(config.remove.getCall(2).args[0]).toBe(CONFIG_TELEMETRY); - - expect(telemetryOptInProvider.getOptIn()).toBe(true); - }); - - it('re-uses old "telemetry:optIn" setting and stays opted in', async () => { - const config = { - get: sinon.stub(), - remove: sinon.spy(), - set: sinon.stub(), - }; - - const telemetryOptInProvider = getTelemetryOptInProvider(null); - expect(telemetryOptInProvider.getOptIn()).toBe(null); - - config.get.withArgs('xPackMonitoring:allowReport', null).returns(false); - config.get.withArgs(CONFIG_TELEMETRY, null).returns(true); - - expect(await handleOldSettings(config, telemetryOptInProvider)).toBe(false); - - expect(config.get.calledTwice).toBe(true); - expect(config.set.called).toBe(false); - - expect(config.remove.calledThrice).toBe(true); - expect(config.remove.getCall(0).args[0]).toBe('xPackMonitoring:allowReport'); - expect(config.remove.getCall(1).args[0]).toBe('xPackMonitoring:showBanner'); - expect(config.remove.getCall(2).args[0]).toBe(CONFIG_TELEMETRY); - - expect(telemetryOptInProvider.getOptIn()).toBe(true); - }); - - it('re-uses old "allowReport" setting and stays opted out', async () => { - const config = { - get: sinon.stub(), - remove: sinon.spy(), - set: sinon.stub(), - }; - - const telemetryOptInProvider = getTelemetryOptInProvider(null); - expect(telemetryOptInProvider.getOptIn()).toBe(null); - - config.get.withArgs('xPackMonitoring:allowReport', null).returns(false); - config.set.withArgs(CONFIG_TELEMETRY, false).returns(Promise.resolve(true)); - - expect(await handleOldSettings(config, telemetryOptInProvider)).toBe(false); - - expect(config.get.calledTwice).toBe(true); - expect(config.set.called).toBe(false); - expect(config.remove.calledThrice).toBe(true); - expect(config.remove.getCall(0).args[0]).toBe('xPackMonitoring:allowReport'); - expect(config.remove.getCall(1).args[0]).toBe('xPackMonitoring:showBanner'); - expect(config.remove.getCall(2).args[0]).toBe(CONFIG_TELEMETRY); - - expect(telemetryOptInProvider.getOptIn()).toBe(false); - }); - - it('re-uses old "telemetry:optIn" setting and stays opted out', async () => { - const config = { - get: sinon.stub(), - remove: sinon.spy(), - set: sinon.stub(), - }; - - const telemetryOptInProvider = getTelemetryOptInProvider(null); - - config.get.withArgs(CONFIG_TELEMETRY, null).returns(false); - config.get.withArgs('xPackMonitoring:allowReport', null).returns(true); - - expect(await handleOldSettings(config, telemetryOptInProvider)).toBe(false); - - expect(config.get.calledTwice).toBe(true); - expect(config.set.called).toBe(false); - expect(config.remove.calledThrice).toBe(true); - expect(config.remove.getCall(0).args[0]).toBe('xPackMonitoring:allowReport'); - expect(config.remove.getCall(1).args[0]).toBe('xPackMonitoring:showBanner'); - expect(config.remove.getCall(2).args[0]).toBe(CONFIG_TELEMETRY); - - expect(telemetryOptInProvider.getOptIn()).toBe(false); - }); - - it('acknowledges users old setting even if re-setting fails', async () => { - const config = { - get: sinon.stub(), - set: sinon.stub(), - }; - - const telemetryOptInProvider = getTelemetryOptInProvider(null, { simulateFailure: true }); - - config.get.withArgs('xPackMonitoring:allowReport', null).returns(false); - //todo: make the new version of this fail! - config.set.withArgs(CONFIG_TELEMETRY, false).returns(Promise.resolve(false)); - - // note: because it doesn't remove the old settings _and_ returns false, there's no risk of suddenly being opted in - expect(await handleOldSettings(config, telemetryOptInProvider)).toBe(false); - - expect(config.get.calledTwice).toBe(true); - expect(config.set.called).toBe(false); - }); - - it('removes show banner setting and presents user with choice', async () => { - const config = { - get: sinon.stub(), - remove: sinon.spy(), - }; - - const telemetryOptInProvider = getTelemetryOptInProvider(null); - - config.get.withArgs('xPackMonitoring:allowReport', null).returns(null); - config.get.withArgs('xPackMonitoring:showBanner', null).returns(false); - - expect(await handleOldSettings(config, telemetryOptInProvider)).toBe(true); - - expect(config.get.calledThrice).toBe(true); - expect(config.remove.calledOnce).toBe(true); - expect(config.remove.getCall(0).args[0]).toBe('xPackMonitoring:showBanner'); - }); - - it('is effectively ignored on fresh installs', async () => { - const config = { - get: sinon.stub(), - }; - - const telemetryOptInProvider = getTelemetryOptInProvider(null); - - config.get.withArgs('xPackMonitoring:allowReport', null).returns(null); - config.get.withArgs('xPackMonitoring:showBanner', null).returns(null); - - expect(await handleOldSettings(config, telemetryOptInProvider)).toBe(true); - - expect(config.get.calledThrice).toBe(true); - }); -}); diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/inject_banner.js b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/inject_banner.js deleted file mode 100644 index cc3ef32c9b618..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/inject_banner.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 chrome from 'ui/chrome'; - -import { fetchTelemetry } from '../fetch_telemetry'; -import { renderBanner } from './render_banner'; -import { shouldShowBanner } from './should_show_banner'; -import { TelemetryOptInProvider, isUnauthenticated } from '../../services'; -import { npStart } from 'ui/new_platform'; - -/** - * Add the Telemetry opt-in banner if the user has not already made a decision. - * - * Note: this is an async function, but Angular fails to use it as one. Its usage does not need to be awaited, - * and thus it can be wrapped in the run method to just be a normal, non-async function. - * - * @param {Object} $injector The Angular injector - */ -async function asyncInjectBanner($injector) { - const Private = $injector.get('Private'); - const telemetryOptInProvider = Private(TelemetryOptInProvider); - const config = $injector.get('config'); - - // and no banner for non-logged in users - if (isUnauthenticated()) { - return; - } - - // and no banner on status page - if (chrome.getApp().id === 'status_page') { - return; - } - - const $http = $injector.get('$http'); - - // determine if the banner should be displayed - if (await shouldShowBanner(telemetryOptInProvider, config)) { - renderBanner(telemetryOptInProvider, () => fetchTelemetry($http, { unencrypted: true })); - } -} - -/** - * Add the Telemetry opt-in banner when appropriate. - * - * @param {Object} $injector The Angular injector - */ -export function injectBanner($injector) { - const telemetryEnabled = npStart.core.injectedMetadata.getInjectedVar('telemetryEnabled'); - const telemetryBanner = npStart.core.injectedMetadata.getInjectedVar('telemetryBanner'); - if (telemetryEnabled && telemetryBanner) { - asyncInjectBanner($injector); - } -} diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_banner.js b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_banner.js deleted file mode 100644 index 70b5030866620..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_banner.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 React from 'react'; - -import { banners } from 'ui/notify'; - -import { clickBanner } from './click_banner'; -import { OptInBanner } from '../../components/opt_in_banner_component'; - -/** - * Render the Telemetry Opt-in banner. - * - * @param {Object} telemetryOptInProvider The telemetry opt-in provider. - * @param {Function} fetchTelemetry Function to pull telemetry on demand. - * @param {Object} _banners Banners singleton, which can be overridden for tests. - */ -export function renderBanner(telemetryOptInProvider, fetchTelemetry, { _banners = banners } = {}) { - const bannerId = _banners.add({ - component: ( - clickBanner(telemetryOptInProvider, optIn)} - fetchTelemetry={fetchTelemetry} - /> - ), - priority: 10000, - }); - - telemetryOptInProvider.setBannerId(bannerId); -} diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_banner.js b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_banner.js deleted file mode 100644 index ee55f6cc76266..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_banner.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 { handleOldSettings } from './handle_old_settings'; - -/** - * Determine if the banner should be displayed. - * - * This method can have side-effects related to deprecated config settings. - * - * @param {Object} config The advanced settings config object. - * @param {Object} _handleOldSettings handleOldSettings function, but overridable for tests. - * @return {Boolean} {@code true} if the banner should be displayed. {@code false} otherwise. - */ -export async function shouldShowBanner( - telemetryOptInProvider, - config, - { _handleOldSettings = handleOldSettings } = {} -) { - return ( - telemetryOptInProvider.getOptIn() === null && - (await _handleOldSettings(config, telemetryOptInProvider)) - ); -} diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_banner.test.js b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_banner.test.js deleted file mode 100644 index 9578d462bc85c..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/should_show_banner.test.js +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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 { mockInjectedMetadata } from '../../services/telemetry_opt_in.test.mocks'; - -import sinon from 'sinon'; - -import { CONFIG_TELEMETRY } from '../../../common/constants'; -import { shouldShowBanner } from './should_show_banner'; -import { TelemetryOptInProvider } from '../../services'; - -const getMockInjector = () => { - const get = sinon.stub(); - - const mockHttp = { - post: sinon.stub(), - }; - - get.withArgs('$http').returns(mockHttp); - - return { get }; -}; - -const getTelemetryOptInProvider = ({ telemetryOptedIn = null } = {}) => { - mockInjectedMetadata({ telemetryOptedIn, allowChangingOptInStatus: true }); - const injector = getMockInjector(); - const chrome = { - addBasePath: url => url, - }; - - return new TelemetryOptInProvider(injector, chrome); -}; - -describe('should_show_banner', () => { - it('returns whatever handleOldSettings does when telemetry opt-in setting is unset', async () => { - const config = { get: sinon.stub() }; - const telemetryOptInProvider = getTelemetryOptInProvider(); - const handleOldSettingsTrue = sinon.stub(); - const handleOldSettingsFalse = sinon.stub(); - - config.get.withArgs(CONFIG_TELEMETRY, null).returns(null); - handleOldSettingsTrue.returns(Promise.resolve(true)); - handleOldSettingsFalse.returns(Promise.resolve(false)); - - const showBannerTrue = await shouldShowBanner(telemetryOptInProvider, config, { - _handleOldSettings: handleOldSettingsTrue, - }); - const showBannerFalse = await shouldShowBanner(telemetryOptInProvider, config, { - _handleOldSettings: handleOldSettingsFalse, - }); - - expect(showBannerTrue).toBe(true); - expect(showBannerFalse).toBe(false); - - expect(config.get.callCount).toBe(0); - expect(handleOldSettingsTrue.calledOnce).toBe(true); - expect(handleOldSettingsFalse.calledOnce).toBe(true); - }); - - it('returns false if telemetry opt-in setting is set to true', async () => { - const config = { get: sinon.stub() }; - - const telemetryOptInProvider = getTelemetryOptInProvider({ telemetryOptedIn: true }); - - expect(await shouldShowBanner(telemetryOptInProvider, config)).toBe(false); - }); - - it('returns false if telemetry opt-in setting is set to false', async () => { - const config = { get: sinon.stub() }; - - const telemetryOptInProvider = getTelemetryOptInProvider({ telemetryOptedIn: false }); - - expect(await shouldShowBanner(telemetryOptInProvider, config)).toBe(false); - }); -}); diff --git a/src/legacy/core_plugins/telemetry/public/services/index.ts b/src/legacy/core_plugins/telemetry/public/services/index.ts deleted file mode 100644 index 8b02f8ce4c5b0..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/services/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 { TelemetryOptInProvider } from './telemetry_opt_in'; -export { isUnauthenticated } from './path'; diff --git a/src/legacy/core_plugins/telemetry/public/services/path.ts b/src/legacy/core_plugins/telemetry/public/services/path.ts deleted file mode 100644 index 4af545e982eaa..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/services/path.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 chrome from 'ui/chrome'; - -export function isUnauthenticated() { - const path = (chrome as any).removeBasePath(window.location.pathname); - return path === '/login' || path === '/logout' || path === '/logged_out' || path === '/status'; -} diff --git a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.js b/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.js deleted file mode 100644 index 494ed24bcc1cb..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.js +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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 { mockInjectedMetadata } from './telemetry_opt_in.test.mocks'; -import { TelemetryOptInProvider } from './telemetry_opt_in'; - -describe('TelemetryOptInProvider', () => { - const setup = ({ optedIn, simulatePostError, simulatePutError }) => { - const mockHttp = { - post: jest.fn(async () => { - if (simulatePostError) { - return Promise.reject('Something happened'); - } - }), - put: jest.fn(async () => { - if (simulatePutError) { - return Promise.reject('Something happened'); - } - }), - }; - - const mockChrome = { - addBasePath: url => url, - }; - - mockInjectedMetadata({ - telemetryOptedIn: optedIn, - allowChangingOptInStatus: true, - telemetryNotifyUserAboutOptInDefault: true, - }); - - const mockInjector = { - get: key => { - switch (key) { - case '$http': { - return mockHttp; - } - default: - throw new Error('unexpected injector request: ' + key); - } - }, - }; - - const provider = new TelemetryOptInProvider(mockInjector, mockChrome, false); - return { - provider, - mockHttp, - }; - }; - - it('should return the current opt-in status', () => { - const { provider: optedInProvider } = setup({ optedIn: true }); - expect(optedInProvider.getOptIn()).toEqual(true); - - const { provider: optedOutProvider } = setup({ optedIn: false }); - expect(optedOutProvider.getOptIn()).toEqual(false); - }); - - it('should allow an opt-out to take place', async () => { - const { provider, mockHttp } = setup({ optedIn: true }); - await provider.setOptIn(false); - - expect(mockHttp.post).toHaveBeenCalledWith(`/api/telemetry/v2/optIn`, { enabled: false }); - - expect(provider.getOptIn()).toEqual(false); - }); - - it('should allow an opt-in to take place', async () => { - const { provider, mockHttp } = setup({ optedIn: false }); - await provider.setOptIn(true); - - expect(mockHttp.post).toHaveBeenCalledWith(`/api/telemetry/v2/optIn`, { enabled: true }); - - expect(provider.getOptIn()).toEqual(true); - }); - - it('should gracefully handle errors', async () => { - const { provider, mockHttp } = setup({ optedIn: false, simulatePostError: true }); - await provider.setOptIn(true); - - expect(mockHttp.post).toHaveBeenCalledWith(`/api/telemetry/v2/optIn`, { enabled: true }); - - // opt-in change should not be reflected - expect(provider.getOptIn()).toEqual(false); - }); - - it('should return the current bannerId', () => { - const { provider } = setup({}); - const bannerId = 'bruce-banner'; - provider.setBannerId(bannerId); - expect(provider.getBannerId()).toEqual(bannerId); - }); - - describe('Notice Banner', () => { - it('should return the current bannerId', () => { - const { provider } = setup({}); - const bannerId = 'bruce-wayne'; - provider.setOptInBannerNoticeId(bannerId); - - expect(provider.getOptInBannerNoticeId()).toEqual(bannerId); - expect(provider.getBannerId()).not.toEqual(bannerId); - }); - - it('should persist that a user has seen the notice', async () => { - const { provider, mockHttp } = setup({}); - await provider.setOptInNoticeSeen(); - - expect(mockHttp.put).toHaveBeenCalledWith(`/api/telemetry/v2/userHasSeenNotice`); - - expect(provider.notifyUserAboutOptInDefault()).toEqual(false); - }); - - it('should only call the API once', async () => { - const { provider, mockHttp } = setup({}); - await provider.setOptInNoticeSeen(); - await provider.setOptInNoticeSeen(); - - expect(mockHttp.put).toHaveBeenCalledTimes(1); - - expect(provider.notifyUserAboutOptInDefault()).toEqual(false); - }); - - it('should gracefully handle errors', async () => { - const { provider } = setup({ simulatePutError: true }); - - await provider.setOptInNoticeSeen(); - - // opt-in change should not be reflected - expect(provider.notifyUserAboutOptInDefault()).toEqual(true); - }); - }); -}); diff --git a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.mocks.js b/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.mocks.js deleted file mode 100644 index 4543266be46df..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.mocks.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 { - injectedMetadataServiceMock, - notificationServiceMock, - overlayServiceMock, -} from '../../../../../core/public/mocks'; -const injectedMetadataMock = injectedMetadataServiceMock.createStartContract(); - -export function mockInjectedMetadata({ - telemetryOptedIn, - allowChangingOptInStatus, - telemetryNotifyUserAboutOptInDefault, -}) { - const mockGetInjectedVar = jest.fn().mockImplementation(key => { - switch (key) { - case 'telemetryOptedIn': - return telemetryOptedIn; - case 'allowChangingOptInStatus': - return allowChangingOptInStatus; - case 'telemetryNotifyUserAboutOptInDefault': - return telemetryNotifyUserAboutOptInDefault; - default: - throw new Error(`unexpected injectedVar ${key}`); - } - }); - - injectedMetadataMock.getInjectedVar = mockGetInjectedVar; -} - -jest.doMock('ui/new_platform', () => ({ - npSetup: { - core: { - notifications: notificationServiceMock.createSetupContract(), - }, - }, - npStart: { - core: { - injectedMetadata: injectedMetadataMock, - overlays: overlayServiceMock.createStartContract(), - }, - }, -})); diff --git a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts b/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts deleted file mode 100644 index af908bea7f4b1..0000000000000 --- a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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 moment from 'moment'; -// @ts-ignore -import { banners, toastNotifications } from 'ui/notify'; -import { npStart } from 'ui/new_platform'; -import { i18n } from '@kbn/i18n'; - -let bannerId: string | null = null; -let optInBannerNoticeId: string | null = null; -let currentOptInStatus = false; -let telemetryNotifyUserAboutOptInDefault = true; - -async function sendOptInStatus($injector: any, chrome: any, enabled: boolean) { - const telemetryOptInStatusUrl = npStart.core.injectedMetadata.getInjectedVar( - 'telemetryOptInStatusUrl' - ) as string; - const $http = $injector.get('$http'); - - try { - const optInStatus = await $http.post( - chrome.addBasePath('/api/telemetry/v2/clusters/_opt_in_stats'), - { - enabled, - unencrypted: false, - } - ); - - if (optInStatus.data && optInStatus.data.length) { - return await fetch(telemetryOptInStatusUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(optInStatus.data), - }); - } - } catch (err) { - // Sending the ping is best-effort. Telemetry tries to send the ping once and discards it immediately if sending fails. - // swallow any errors - } -} -export function TelemetryOptInProvider($injector: any, chrome: any, sendOptInStatusChange = true) { - currentOptInStatus = npStart.core.injectedMetadata.getInjectedVar('telemetryOptedIn') as boolean; - - const allowChangingOptInStatus = npStart.core.injectedMetadata.getInjectedVar( - 'allowChangingOptInStatus' - ) as boolean; - - telemetryNotifyUserAboutOptInDefault = npStart.core.injectedMetadata.getInjectedVar( - 'telemetryNotifyUserAboutOptInDefault' - ) as boolean; - - const provider = { - getBannerId: () => bannerId, - getOptInBannerNoticeId: () => optInBannerNoticeId, - getOptIn: () => currentOptInStatus, - canChangeOptInStatus: () => allowChangingOptInStatus, - notifyUserAboutOptInDefault: () => telemetryNotifyUserAboutOptInDefault, - setBannerId(id: string) { - bannerId = id; - }, - setOptInBannerNoticeId(id: string) { - optInBannerNoticeId = id; - }, - setOptInNoticeSeen: async () => { - const $http = $injector.get('$http'); - - // If they've seen the notice don't spam the API - if (!telemetryNotifyUserAboutOptInDefault) { - return telemetryNotifyUserAboutOptInDefault; - } - - if (optInBannerNoticeId) { - banners.remove(optInBannerNoticeId); - } - - try { - await $http.put(chrome.addBasePath('/api/telemetry/v2/userHasSeenNotice')); - telemetryNotifyUserAboutOptInDefault = false; - } catch (error) { - toastNotifications.addError(error, { - title: i18n.translate('telemetry.optInNoticeSeenErrorTitle', { - defaultMessage: 'Error', - }), - toastMessage: i18n.translate('telemetry.optInNoticeSeenErrorToastText', { - defaultMessage: 'An error occurred dismissing the notice', - }), - }); - telemetryNotifyUserAboutOptInDefault = true; - } - - return telemetryNotifyUserAboutOptInDefault; - }, - setOptIn: async (enabled: boolean) => { - if (!allowChangingOptInStatus) { - return; - } - const $http = $injector.get('$http'); - - try { - await $http.post(chrome.addBasePath('/api/telemetry/v2/optIn'), { enabled }); - if (sendOptInStatusChange) { - await sendOptInStatus($injector, chrome, enabled); - } - currentOptInStatus = enabled; - } catch (error) { - toastNotifications.addError(error, { - title: i18n.translate('telemetry.optInErrorToastTitle', { - defaultMessage: 'Error', - }), - toastMessage: i18n.translate('telemetry.optInErrorToastText', { - defaultMessage: - 'An error occurred while trying to set the usage statistics preference.', - }), - }); - return false; - } - - return true; - }, - fetchExample: async () => { - const $http = $injector.get('$http'); - return $http.post(chrome.addBasePath(`/api/telemetry/v2/clusters/_stats`), { - unencrypted: true, - timeRange: { - min: moment() - .subtract(20, 'minutes') - .toISOString(), - max: moment().toISOString(), - }, - }); - }, - }; - - return provider; -} diff --git a/src/legacy/core_plugins/telemetry/public/views/management/index.js b/src/legacy/core_plugins/telemetry/public/views/management/index.ts similarity index 100% rename from src/legacy/core_plugins/telemetry/public/views/management/index.js rename to src/legacy/core_plugins/telemetry/public/views/management/index.ts diff --git a/src/legacy/core_plugins/telemetry/public/views/management/management.js b/src/legacy/core_plugins/telemetry/public/views/management/management.tsx similarity index 60% rename from src/legacy/core_plugins/telemetry/public/views/management/management.js rename to src/legacy/core_plugins/telemetry/public/views/management/management.tsx index 796caf1c8cfe6..58ced6fbd599d 100644 --- a/src/legacy/core_plugins/telemetry/public/views/management/management.js +++ b/src/legacy/core_plugins/telemetry/public/views/management/management.tsx @@ -18,25 +18,26 @@ */ import React from 'react'; import routes from 'ui/routes'; - import { registerSettingsComponent, PAGE_FOOTER_COMPONENT } from 'ui/management'; -import { TelemetryOptInProvider } from '../../services'; -import { TelemetryForm } from '../../components'; +import { npStart } from 'ui/new_platform'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { TelemetryManagementSection } from '../../../../../../plugins/telemetry/public/components'; routes.defaults(/\/management/, { resolve: { - telemetryManagementSection: function(Private) { - const telemetryOptInProvider = Private(TelemetryOptInProvider); - - const Component = props => ( - - ); + telemetryManagementSection() { + if ((npStart.plugins as any).telemetry) { + const { telemetryService } = (npStart.plugins as any).telemetry; + const Component = (props: any) => ( + + ); - registerSettingsComponent(PAGE_FOOTER_COMPONENT, Component, true); + registerSettingsComponent(PAGE_FOOTER_COMPONENT, Component, true); + } }, }, }); diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/handle_old_settings.js b/src/legacy/core_plugins/telemetry/server/handle_old_settings/handle_old_settings.ts similarity index 51% rename from src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/handle_old_settings.js rename to src/legacy/core_plugins/telemetry/server/handle_old_settings/handle_old_settings.ts index c03fdb85c4d1c..e01abcad41bfb 100644 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/handle_old_settings.js +++ b/src/legacy/core_plugins/telemetry/server/handle_old_settings/handle_old_settings.ts @@ -17,8 +17,6 @@ * under the License. */ -import { CONFIG_TELEMETRY } from '../../../common/constants'; - /** * Clean up any old, deprecated settings and determine if we should continue. * @@ -27,13 +25,21 @@ import { CONFIG_TELEMETRY } from '../../../common/constants'; * @param {Object} config The advanced settings config object. * @return {Boolean} {@code true} if the banner should still be displayed. {@code false} if the banner should not be displayed. */ + +import { Server } from 'hapi'; +import { CONFIG_TELEMETRY } from '../../common/constants'; +import { updateTelemetrySavedObject } from '../telemetry_repository'; + const CONFIG_ALLOW_REPORT = 'xPackMonitoring:allowReport'; -export async function handleOldSettings(config, telemetryOptInProvider) { - const CONFIG_SHOW_BANNER = 'xPackMonitoring:showBanner'; - const oldAllowReportSetting = config.get(CONFIG_ALLOW_REPORT, null); - const oldTelemetrySetting = config.get(CONFIG_TELEMETRY, null); +export async function handleOldSettings(server: Server) { + const { getSavedObjectsRepository } = server.savedObjects; + const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin'); + const savedObjectsClient = getSavedObjectsRepository(callWithInternalUser); + const uiSettings = server.uiSettingsServiceFactory({ savedObjectsClient }); + const oldTelemetrySetting = await uiSettings.get(CONFIG_TELEMETRY); + const oldAllowReportSetting = await uiSettings.get(CONFIG_ALLOW_REPORT); let legacyOptInValue = null; if (typeof oldTelemetrySetting === 'boolean') { @@ -43,43 +49,8 @@ export async function handleOldSettings(config, telemetryOptInProvider) { } if (legacyOptInValue !== null) { - try { - await telemetryOptInProvider.setOptIn(legacyOptInValue); - - // delete old keys once we've successfully changed the setting (if it fails, we just wait until next time) - config.remove(CONFIG_ALLOW_REPORT); - config.remove(CONFIG_SHOW_BANNER); - config.remove(CONFIG_TELEMETRY); - } finally { - return false; - } - } - - const oldShowSetting = config.get(CONFIG_SHOW_BANNER, null); - - if (oldShowSetting !== null) { - config.remove(CONFIG_SHOW_BANNER); - } - - return true; -} - -export async function isOptInHandleOldSettings(config, telemetryOptInProvider) { - const currentOptInSettting = telemetryOptInProvider.getOptIn(); - - if (typeof currentOptInSettting === 'boolean') { - return currentOptInSettting; + await updateTelemetrySavedObject(savedObjectsClient, { + enabled: legacyOptInValue, + }); } - - const oldTelemetrySetting = config.get(CONFIG_TELEMETRY, null); - if (typeof oldTelemetrySetting === 'boolean') { - return oldTelemetrySetting; - } - - const oldAllowReportSetting = config.get(CONFIG_ALLOW_REPORT, null); - if (typeof oldAllowReportSetting === 'boolean') { - return oldAllowReportSetting; - } - - return null; } diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/index.js b/src/legacy/core_plugins/telemetry/server/handle_old_settings/index.ts similarity index 93% rename from src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/index.js rename to src/legacy/core_plugins/telemetry/server/handle_old_settings/index.ts index ffb0e88c60a0d..77eae0d80db61 100644 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/index.js +++ b/src/legacy/core_plugins/telemetry/server/handle_old_settings/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { injectBanner } from './inject_banner'; +export { handleOldSettings } from './handle_old_settings'; diff --git a/src/legacy/core_plugins/telemetry/server/index.ts b/src/legacy/core_plugins/telemetry/server/index.ts index 6c62d03adf25c..85d7d80234ffc 100644 --- a/src/legacy/core_plugins/telemetry/server/index.ts +++ b/src/legacy/core_plugins/telemetry/server/index.ts @@ -23,6 +23,7 @@ import * as constants from '../common/constants'; export { FetcherTask } from './fetcher'; export { replaceTelemetryInjectedVars } from './telemetry_config'; +export { handleOldSettings } from './handle_old_settings'; export { telemetryCollectionManager } from './collection_manager'; export { PluginsSetup } from './plugin'; export const telemetryPlugin = (initializerContext: PluginInitializerContext) => diff --git a/src/plugins/telemetry/public/components/index.ts b/src/plugins/telemetry/public/components/index.ts index 0adbeb493a222..e8e6dbdef7eab 100644 --- a/src/plugins/telemetry/public/components/index.ts +++ b/src/plugins/telemetry/public/components/index.ts @@ -18,3 +18,4 @@ */ export { OptInExampleFlyout } from './opt_in_example_flyout'; +export { TelemetryManagementSection } from './telemetry_management_section'; diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_banner.test.js b/src/plugins/telemetry/public/components/opt_in_banner.test.ts similarity index 100% rename from src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/render_banner.test.js rename to src/plugins/telemetry/public/components/opt_in_banner.test.ts diff --git a/src/legacy/core_plugins/telemetry/public/components/opt_in_banner_component.tsx b/src/plugins/telemetry/public/components/opt_in_banner.tsx similarity index 84% rename from src/legacy/core_plugins/telemetry/public/components/opt_in_banner_component.tsx rename to src/plugins/telemetry/public/components/opt_in_banner.tsx index 2813af9c499e7..d474fb81c5be2 100644 --- a/src/legacy/core_plugins/telemetry/public/components/opt_in_banner_component.tsx +++ b/src/plugins/telemetry/public/components/opt_in_banner.tsx @@ -23,15 +23,12 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { OptInMessage } from './opt_in_message'; interface Props { - fetchTelemetry: () => Promise; - optInClick: (optIn: boolean) => void; + onChangeOptInClick: (isOptIn: boolean) => void; } -/** - * React component for displaying the Telemetry opt-in banner. - */ export class OptInBanner extends React.PureComponent { render() { + const { onChangeOptInClick } = this.props; const title = ( { ); return ( - + - this.props.optInClick(true)}> + onChangeOptInClick(true)}> { - this.props.optInClick(false)}> + onChangeOptInClick(false)}> Promise; -} - interface State { showDetails: boolean; showExample: boolean; } -export class OptInMessage extends React.PureComponent { +export class OptInMessage extends React.PureComponent<{}, State> { public readonly state: State = { showDetails: false, showExample: false, diff --git a/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx b/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx index 5d6a12df419bb..15ec6d273a79d 100644 --- a/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx +++ b/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx @@ -41,7 +41,7 @@ export class OptedInBanner extends React.PureComponent { values={{ privacyStatementLink: ( void; + showAppliesSettingMessage: boolean; + enableSaving: boolean; + query?: any; +} + +interface State { + processing: boolean; + showExample: boolean; + queryMatches: boolean | null; +} - state = { +export class TelemetryManagementSection extends Component { + state: State = { processing: false, showExample: false, queryMatches: null, }; - UNSAFE_componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps: Props) { const { query } = nextProps; const searchTerm = (query.text || '').toLowerCase(); @@ -71,11 +76,10 @@ export class TelemetryForm extends Component { } render() { - const { telemetryOptInProvider } = this.props; - + const { telemetryService } = this.props; const { showExample, queryMatches } = this.state; - if (!telemetryOptInProvider.canChangeOptInStatus()) { + if (!telemetryService.getCanChangeOptInStatus()) { return null; } @@ -87,7 +91,7 @@ export class TelemetryForm extends Component { {showExample && ( telemetryOptInProvider.fetchExample()} + fetchExample={telemetryService.fetchExample} onClose={this.toggleExample} /> )} @@ -108,7 +112,7 @@ export class TelemetryForm extends Component { ); - toggleOptIn = async () => { - const newOptInValue = !this.props.telemetryOptInProvider.getOptIn(); + toggleOptIn = async (): Promise => { + const { telemetryService } = this.props; + const newOptInValue = !telemetryService.getIsOptedIn(); return new Promise((resolve, reject) => { - this.setState( - { - enabled: newOptInValue, - processing: true, - }, - () => { - this.props.telemetryOptInProvider.setOptIn(newOptInValue).then( - () => { - this.setState({ processing: false }); - resolve(); - }, - e => { - // something went wrong - this.setState({ processing: false }); - reject(e); - } - ); + this.setState({ processing: true }, async () => { + try { + await telemetryService.setOptIn(newOptInValue); + this.setState({ processing: false }); + resolve(); + } catch (err) { + this.setState({ processing: false }); + reject(err); } - ); + }); }); }; diff --git a/src/plugins/telemetry/public/index.ts b/src/plugins/telemetry/public/index.ts index b826785ded948..8d1e430080b2d 100644 --- a/src/plugins/telemetry/public/index.ts +++ b/src/plugins/telemetry/public/index.ts @@ -17,10 +17,9 @@ * under the License. */ -import { PluginInitializerContext } from '../../../core/public'; import { TelemetryPlugin } from './plugin'; export { TelemetryPluginStart } from './plugin'; -export function plugin(initializerContext: PluginInitializerContext) { - return new TelemetryPlugin(initializerContext); +export function plugin() { + return new TelemetryPlugin(); } diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index ab484ca94c12b..60be9e83ed650 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -16,21 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -import { - // PluginInitializerContext, - Plugin, - CoreSetup, - CoreStart, - HttpSetup, -} from '../../../core/public'; -import { TelemetrySender, TelemetryService, TelemetryNotifications } from './services'; +import { Plugin, CoreSetup, CoreStart, HttpSetup } from '../../../core/public'; -// interface PublicConfigType { -// uiMetric: { -// enabled: boolean; -// debug: boolean; -// }; -// } +import { TelemetrySender, TelemetryService, TelemetryNotifications } from './services'; export interface TelemetryPluginStart { telemetryService: TelemetryService; @@ -38,18 +26,21 @@ export interface TelemetryPluginStart { } export class TelemetryPlugin implements Plugin { - // private config: PublicConfigType; - private isUnauthenticated: boolean = false; - // constructor(initializerContext: PluginInitializerContext) { - // this.config = initializerContext.config.get(); - // } + private isUnauthenticated: boolean = true; public setup({ http }: CoreSetup) { this.isUnauthenticated = this.getIsUnauthenticated(http); } - public start({ injectedMetadata, http, notifications, overlays }: CoreStart): TelemetryStart { + public start({ + injectedMetadata, + application, + http, + notifications, + overlays, + }: CoreStart): TelemetryPluginStart { const isPluginEnabled = injectedMetadata.getInjectedVar('telemetryEnabled') as boolean; + const telemetryBanner = injectedMetadata.getInjectedVar('telemetryBanner') as boolean; const sendUsageFrom = injectedMetadata.getInjectedVar('telemetrySendUsageFrom') as | 'browser' | 'server'; @@ -61,10 +52,8 @@ export class TelemetryPlugin implements Plugin { }); const telemetryNotifications = new TelemetryNotifications({ - http, - injectedMetadata, - notifications, overlays, + telemetryService, }); this.maybeStartTelemetryPoller({ @@ -73,9 +62,15 @@ export class TelemetryPlugin implements Plugin { sendUsageFrom, }); - this.maybeShowOptedInNotificationBanner({ - telemetryNotifications, - }); + // const isStatusPage = chrome.getApp().id === 'status_page'; + if (telemetryBanner) { + this.maybeShowOptedInNotificationBanner({ + telemetryNotifications, + }); + this.maybeShowOptInBanner({ + telemetryNotifications, + }); + } return { telemetryService, @@ -99,7 +94,7 @@ export class TelemetryPlugin implements Plugin { }) { if (isPluginEnabled && sendUsageFrom === 'browser') { const sender = new TelemetrySender(telemetryService); - // no telemetry for non-logged in users + if (!this.isUnauthenticated) { sender.startChecking(); } @@ -114,14 +109,25 @@ export class TelemetryPlugin implements Plugin { if (this.isUnauthenticated) { return; } - // and no banner on status page - // if (chrome.getApp().id === 'status_page') { - // return; - // } const shouldShowBanner = telemetryNotifications.shouldShowOptedInNoticeBanner(); if (shouldShowBanner) { telemetryNotifications.renderOptedInNoticeBanner(); } } + + private maybeShowOptInBanner({ + telemetryNotifications, + }: { + telemetryNotifications: TelemetryNotifications; + }) { + if (this.isUnauthenticated) { + return; + } + + const shouldShowBanner = telemetryNotifications.shouldShowOptInBanner(); + if (shouldShowBanner) { + telemetryNotifications.renderOptInBanner(); + } + } } diff --git a/src/legacy/core_plugins/telemetry/public/components/opt_in_details_component.test.tsx b/src/plugins/telemetry/public/services/telemetry_notifications/render_opt_in_banner.tsx similarity index 61% rename from src/legacy/core_plugins/telemetry/public/components/opt_in_details_component.test.tsx rename to src/plugins/telemetry/public/services/telemetry_notifications/render_opt_in_banner.tsx index 3676430b55a41..6e0164df6403a 100644 --- a/src/legacy/core_plugins/telemetry/public/components/opt_in_details_component.test.tsx +++ b/src/plugins/telemetry/public/services/telemetry_notifications/render_opt_in_banner.tsx @@ -16,19 +16,20 @@ * specific language governing permissions and limitations * under the License. */ + import React from 'react'; -import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { OptInExampleFlyout } from './opt_in_details_component'; +import { CoreStart } from 'kibana/public'; +import { OptInBanner } from '../../components/opt_in_banner'; +import { toMountPoint } from '../../../../kibana_react/public'; + +interface RenderBannerConfig { + overlays: CoreStart['overlays']; + setOptIn: (isOptIn: boolean) => Promise; +} + +export function renderOptInBanner({ setOptIn, overlays }: RenderBannerConfig) { + const mount = toMountPoint(); + const bannerId = overlays.banners.add(mount, 10000); -describe('OptInDetailsComponent', () => { - it('renders as expected', () => { - expect( - shallowWithIntl( - ({ data: [] }))} - onClose={jest.fn()} - /> - ) - ).toMatchSnapshot(); - }); -}); + return bannerId; +} diff --git a/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx index 85838f3d12e7b..91ea254725c98 100644 --- a/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx +++ b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx @@ -27,7 +27,7 @@ interface RenderBannerConfig { overlays: CoreStart['overlays']; onSeen: () => void; } -export function renderOptedInBanner({ onSeen, overlays }: RenderBannerConfig) { +export function renderOptedInNoticeBanner({ onSeen, overlays }: RenderBannerConfig) { const mount = toMountPoint( { + it('sets setting successfully and removes banner', async () => { + const optIn = true; + const bannerId = 'bruce-banner'; + + const telemetryService = mockTelemetryService(); + telemetryService.setOptIn = jest.fn(); + const telemetryNotifications = mockTelemetryNotifications({ telemetryService }); + telemetryNotifications.optInBannerId = bannerId; + + await telemetryNotifications.onSetOptInClick(optIn); + expect(telemetryNotifications.overlays.banners.remove).toBeCalledTimes(1); + expect(telemetryNotifications.overlays.banners.remove).toBeCalledWith(bannerId); + expect(telemetryService.setOptIn).toBeCalledTimes(1); + expect(telemetryService.setOptIn).toBeCalledWith(optIn); + }); +}); + +describe('setOptedInNoticeSeen', () => { + it('sets setting successfully and removes banner', async () => { + const bannerId = 'bruce-banner'; + + const telemetryService = mockTelemetryService(); + telemetryService.setUserHasSeenNotice = jest.fn(); + const telemetryNotifications = mockTelemetryNotifications({ telemetryService }); + telemetryNotifications.optedInNoticeBannerId = bannerId; + await telemetryNotifications.setOptedInNoticeSeen(); + + expect(telemetryNotifications.overlays.banners.remove).toBeCalledTimes(1); + expect(telemetryNotifications.overlays.banners.remove).toBeCalledWith(bannerId); + expect(telemetryService.setUserHasSeenNotice).toBeCalledTimes(1); + }); +}); diff --git a/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts index 174ad520abe51..f6906a0c9dd16 100644 --- a/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts +++ b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts @@ -18,81 +18,67 @@ */ import { CoreStart } from 'kibana/public'; -import { i18n } from '@kbn/i18n'; -import { renderOptedInBanner } from './render_opted_in_notice_banner'; +import { renderOptedInNoticeBanner } from './render_opted_in_notice_banner'; +import { renderOptInBanner } from './render_opt_in_banner'; +import { TelemetryService } from '../telemetry_service'; interface TelemetryNotificationsConstructor { - notifications: CoreStart['notifications']; - injectedMetadata: CoreStart['injectedMetadata']; overlays: CoreStart['overlays']; - http: CoreStart['http']; + telemetryService: TelemetryService; } export class TelemetryNotifications { private readonly overlays: CoreStart['overlays']; - private readonly notifications: CoreStart['notifications']; - private readonly http: CoreStart['http']; + private readonly telemetryService: TelemetryService; + private optedInNoticeBannerId?: string; + private optInBannerId?: string; - private optedInBannerNoticeId?: string; - // private optInBannerId?: string; - private showOptedInNoticeBanner: boolean; - - constructor({ - notifications, - http, - overlays, - injectedMetadata, - }: TelemetryNotificationsConstructor) { - this.notifications = notifications; - this.http = http; + constructor({ overlays, telemetryService }: TelemetryNotificationsConstructor) { + this.telemetryService = telemetryService; this.overlays = overlays; - - this.showOptedInNoticeBanner = injectedMetadata.getInjectedVar( - 'telemetryNotifyUserAboutOptInDefault' - ) as boolean; } - public shouldShowOptedInNoticeBanner() { - return this.showOptedInNoticeBanner; - } + public shouldShowOptedInNoticeBanner = (): boolean => { + const userHasSeenOptedInNotice = this.telemetryService.getUserHasSeenOptedInNotice(); + return userHasSeenOptedInNotice; + }; - public renderOptedInNoticeBanner = () => { - const bannerId = renderOptedInBanner({ + public renderOptedInNoticeBanner = (): void => { + const bannerId = renderOptedInNoticeBanner({ onSeen: this.setOptedInNoticeSeen, overlays: this.overlays, }); - this.optedInBannerNoticeId = bannerId; + this.optedInNoticeBannerId = bannerId; }; - public setOptedInNoticeSeen = async (): Promise => { - // If they've seen the notice don't spam the API - if (!this.showOptedInNoticeBanner) { - return; - } + public shouldShowOptInBanner = (): boolean => { + const isOptedIn = this.telemetryService.getIsOptedIn(); + return isOptedIn === null; + }; - const optInBannerNoticeId = this.optedInBannerNoticeId; - if (optInBannerNoticeId) { - this.overlays.banners.remove(optInBannerNoticeId); + public renderOptInBanner = (): void => { + const bannerId = renderOptInBanner({ + setOptIn: this.onSetOptInClick, + overlays: this.overlays, + }); + + this.optInBannerId = bannerId; + }; + + private onSetOptInClick = async (isOptIn: boolean) => { + if (this.optInBannerId) { + this.overlays.banners.remove(this.optInBannerId); } - try { - await this.http.put('/api/telemetry/v2/userHasSeenNotice'); - this.showOptedInNoticeBanner = false; - } catch (error) { - this.notifications.toasts.addError(error, { - title: i18n.translate('telemetry.optInNoticeSeenErrorTitle', { - defaultMessage: 'Error', - }), - toastMessage: i18n.translate('telemetry.optInNoticeSeenErrorToastText', { - defaultMessage: 'An error occurred dismissing the notice', - }), - }); - this.showOptedInNoticeBanner = true; + await this.telemetryService.setOptIn(isOptIn); + }; + + public setOptedInNoticeSeen = async (): Promise => { + if (this.optedInNoticeBannerId) { + this.overlays.banners.remove(this.optedInNoticeBannerId); } + + await this.telemetryService.setUserHasSeenNotice(); }; } - -// telemetryEnabled && -// !telemetryOptInService.getOptIn() && -// telemetryOptInService.canChangeOptInStatus() diff --git a/src/legacy/core_plugins/telemetry/public/hacks/__tests__/fetch_telemetry.js b/src/plugins/telemetry/public/services/telemetry_service.test.ts similarity index 57% rename from src/legacy/core_plugins/telemetry/public/hacks/__tests__/fetch_telemetry.js rename to src/plugins/telemetry/public/services/telemetry_service.test.ts index ad9ee0998e3bb..a837fb31dcd4a 100644 --- a/src/legacy/core_plugins/telemetry/public/hacks/__tests__/fetch_telemetry.js +++ b/src/plugins/telemetry/public/services/telemetry_service.test.ts @@ -53,3 +53,34 @@ describe('fetch_telemetry', () => { ); }); }); + +// describe('setOptIn', () => { +// it('sets setting unsuccessfully, adds toast, and does not touch banner', async () => { +// const optIn = true; +// const telemetryService = mockTelemetryService(); +// const telemetryNotifications = mockTelemetryNotifications({ telemetryService }); +// await telemetryNotifications['onSetOptInClick'](optIn); + +// }); + +// // it('sets setting unsuccessfully with error, adds toast, and does not touch banner', async () => { +// // const toastNotifications = { +// // addDanger: sinon.spy(), +// // }; +// // const banners = { +// // remove: sinon.spy(), +// // }; +// // const optIn = false; +// // mockInjectedMetadata({ telemetryOptedIn: null, allowChangingOptInStatus: true }); +// // const telemetryOptInProvider = getTelemetryOptInProvider({ simulateError: true }); + +// // await clickBanner(telemetryOptInProvider, optIn, { +// // _banners: banners, +// // _toastNotifications: toastNotifications, +// // }); + +// // expect(telemetryOptInProvider.getOptIn()).toBe(null); +// // expect(toastNotifications.addDanger.calledOnce).toBe(true); +// // expect(banners.remove.notCalled).toBe(true); +// // }); +// }) diff --git a/src/plugins/telemetry/public/services/telemetry_service.ts b/src/plugins/telemetry/public/services/telemetry_service.ts index d212c01d38a31..e4ec8d38db452 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.ts @@ -18,8 +18,6 @@ */ import moment from 'moment'; -// @ts-ignore -// import { banners, toastNotifications } from 'ui/notify'; import { i18n } from '@kbn/i18n'; import { CoreStart } from 'kibana/public'; @@ -35,7 +33,8 @@ export class TelemetryService { private readonly injectedMetadata: CoreStart['injectedMetadata']; private readonly reportOptInStatusChange: boolean; private readonly notifications: CoreStart['notifications']; - private isOptedIn: boolean; + private isOptedIn: boolean | null; + private userHasSeenOptedInNotice: boolean; constructor({ http, @@ -43,14 +42,17 @@ export class TelemetryService { notifications, reportOptInStatusChange = true, }: TelemetryServiceConstructor) { - const isOptedIn = injectedMetadata.getInjectedVar('telemetryOptedIn') as boolean; - + const isOptedIn = injectedMetadata.getInjectedVar('telemetryOptedIn') as boolean | null; + const userHasSeenOptedInNotice = injectedMetadata.getInjectedVar( + 'telemetryNotifyUserAboutOptInDefault' + ) as boolean; this.reportOptInStatusChange = reportOptInStatusChange; this.injectedMetadata = injectedMetadata; this.notifications = notifications; this.http = http; this.isOptedIn = isOptedIn; + this.userHasSeenOptedInNotice = userHasSeenOptedInNotice; } public getCanChangeOptInStatus = () => { @@ -72,6 +74,10 @@ export class TelemetryService { return telemetryUrl; }; + public getUserHasSeenOptedInNotice = () => { + return this.userHasSeenOptedInNotice; + }; + public getIsOptedIn = () => { return this.isOptedIn; }; @@ -123,14 +129,27 @@ export class TelemetryService { return true; }; + public setUserHasSeenNotice = async (): Promise => { + try { + await this.http.put('/api/telemetry/v2/userHasSeenNotice'); + this.userHasSeenOptedInNotice = true; + } catch (error) { + this.notifications.toasts.addError(error, { + title: i18n.translate('telemetry.optInNoticeSeenErrorTitle', { + defaultMessage: 'Error', + }), + toastMessage: i18n.translate('telemetry.optInNoticeSeenErrorToastText', { + defaultMessage: 'An error occurred dismissing the notice', + }), + }); + this.userHasSeenOptedInNotice = false; + } + }; + private reportOptInStatus = async (OptInStatus: boolean): Promise => { const telemetryOptInStatusUrl = this.getOptInStatusUrl(); try { - // const optInStatus = await this.http.post('/api/telemetry/v2/clusters/_opt_in_stats', { - // body: JSON.stringify({ enabled, unencrypted: false }) - // }); - await fetch(telemetryOptInStatusUrl, { method: 'POST', headers: { @@ -144,11 +163,3 @@ export class TelemetryService { } }; } - -// export const shouldShowTelemetryOptIn = () => { -// return ( -// telemetryEnabled && -// !telemetryOptInService.getOptIn() && -// telemetryOptInService.canChangeOptInStatus() -// ); -// }; diff --git a/src/plugins/telemetry/public/telemetry.mock.ts b/src/plugins/telemetry/public/telemetry.mock.ts new file mode 100644 index 0000000000000..510be7cf9de87 --- /dev/null +++ b/src/plugins/telemetry/public/telemetry.mock.ts @@ -0,0 +1,81 @@ +/* + * 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. + */ + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { overlayServiceMock } from '../../../core/public/overlays/overlay_service.mock'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { httpServiceMock } from '../../../core/public/http/http_service.mock'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { notificationServiceMock } from '../../../core/public/notifications/notifications_service.mock'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { injectedMetadataServiceMock } from '../../../core/public/injected_metadata/injected_metadata_service.mock'; +import { TelemetryService, TelemetryNotifications } from './services'; +import { TelemetryPluginStart } from './plugin'; + +export function mockTelemetryService() { + const injectedMetadata = injectedMetadataServiceMock.createStartContract(); + injectedMetadata.getInjectedVar.mockImplementation((key: string) => { + switch (key) { + case 'telemetryNotifyUserAboutOptInDefault': + return true; + case 'allowChangingOptInStatus': + return true; + case 'telemetryOptedIn': + return true; + default: { + throw Error(`Unhandled getInjectedVar key "${key}".`); + } + } + }); + + return new TelemetryService({ + injectedMetadata, + http: httpServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }); +} + +export function mockTelemetryNotifications({ + telemetryService, +}: { + telemetryService: TelemetryService; +}) { + return new TelemetryNotifications({ + overlays: overlayServiceMock.createStartContract(), + telemetryService, + }); +} + +export type Setup = jest.Mocked; + +export const telemetryPluginMock = { + createSetupContract, +}; + +function createSetupContract(): Setup { + const telemetryService = mockTelemetryService(); + const telemetryNotifications = mockTelemetryNotifications({ telemetryService }); + + const setupContract: Setup = { + telemetryService, + telemetryNotifications, + }; + + return setupContract; +} diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts b/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts index b97f6bd237755..291ad7d588c66 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts @@ -4,6 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -export { TelemetryPluginStart } from '../../../../../../../../src/plugins/telemetry/public'; +import { TelemetryPluginStart } from '../../../../../../../../src/plugins/telemetry/public'; + export { OptInExampleFlyout } from '../../../../../../../../src/plugins/telemetry/public/components'; export { PRIVACY_STATEMENT_URL } from '../../../../../../../../src/plugins/telemetry/common/constants'; +export { TelemetryPluginStart, shouldShowTelemetryOptIn }; + +function shouldShowTelemetryOptIn( + telemetry?: TelemetryPluginStart +): telemetry is TelemetryPluginStart { + if (telemetry) { + const { telemetryService } = telemetry; + const isOptedIn = telemetryService.getIsOptedIn(); + const canChangeOptInStatus = telemetryService.getCanChangeOptInStatus(); + return canChangeOptInStatus && !isOptedIn; + } + + return false; +} diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx index 77a75d1dced6b..f6f10bdffce79 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx @@ -26,17 +26,16 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { TelemetryOptIn } from '../../../components/telemetry_opt_in'; import { EXTERNAL_LINKS } from '../../../../../../common/constants'; import { getDocLinks } from '../../../lib/docs_links'; -import { TelemetryPluginStart } from '../../../lib/telemetry'; +import { TelemetryPluginStart, shouldShowTelemetryOptIn } from '../../../lib/telemetry'; interface Props { loadTrialStatus: () => void; startLicenseTrial: () => void; - telemetry: TelemetryPluginStart; + telemetry?: TelemetryPluginStart; shouldShowStartTrial: boolean; } interface State { - shouldShowTelemetryOptIn: boolean; showConfirmation: boolean; isOptingInToTelemetry: boolean; } @@ -46,7 +45,6 @@ export class StartTrial extends Component { confirmRef: any; state: State = { - shouldShowTelemetryOptIn: true, showConfirmation: false, isOptingInToTelemetry: false, }; @@ -58,18 +56,21 @@ export class StartTrial extends Component { onOptInChange = (isOptingInToTelemetry: boolean) => { this.setState({ isOptingInToTelemetry }); }; + onStartLicenseTrial = () => { const { telemetry, startLicenseTrial } = this.props; - if (this.state.isOptingInToTelemetry) { + if (this.state.isOptingInToTelemetry && telemetry) { telemetry.telemetryService.setOptIn(true); } startLicenseTrial(); }; + cancel = () => { this.setState({ showConfirmation: false }); }; acknowledgeModal() { - const { showConfirmation } = this.state; + const { showConfirmation, isOptingInToTelemetry } = this.state; + const { telemetry } = this.props; if (!showConfirmation) { return null; } @@ -180,12 +181,12 @@ export class StartTrial extends Component { - {this.state.shouldShowTelemetryOptIn && ( + {shouldShowTelemetryOptIn(telemetry) && ( )} diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js index 8522219124dd9..8460065639303 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js @@ -22,16 +22,14 @@ import { EuiPageContentBody, } from '@elastic/eui'; import { TelemetryOptIn } from '../../components/telemetry_opt_in'; +import { shouldShowTelemetryOptIn } from '../../lib/telemetry'; import { FormattedMessage } from '@kbn/i18n/react'; export class UploadLicense extends React.PureComponent { - constructor(props) { - super(props); - this.state = { - isOptingInToTelemetry: false, - shouldShowTelemetryOptIn: true, - }; - } + state = { + isOptingInToTelemetry: false, + }; + componentDidMount() { this.props.setBreadcrumb('upload'); this.props.addUploadErrorMessage(''); @@ -45,7 +43,7 @@ export class UploadLicense extends React.PureComponent { fr.onload = ({ target: { result } }) => { if (this.state.isOptingInToTelemetry) { - this.props.telemetry.telemetryService.setOptIn(true); + this.props.telemetry?.telemetryService.setOptIn(true); } this.props.uploadLicense(result, this.props.currentLicenseType, acknowledge); }; @@ -126,7 +124,7 @@ export class UploadLicense extends React.PureComponent { } }; render() { - const { currentLicenseType, applying } = this.props; + const { currentLicenseType, applying, telemetry } = this.props; return ( @@ -180,11 +178,11 @@ export class UploadLicense extends React.PureComponent { - {this.state.shouldShowTelemetryOptIn && ( + {shouldShowTelemetryOptIn(telemetry) && ( )} From f16ef88239691f31313d8a8cbc7c83de3f1569f4 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Thu, 30 Jan 2020 22:56:39 +0200 Subject: [PATCH 05/41] finish components testing --- .../__snapshots__/opt_in_banner.test.tsx.snap | 54 ++++++++++++++++ .../opt_in_example_flyout.test.tsx.snap | 50 +++++++++++++++ .../opt_in_message.test.tsx.snap | 24 +++++++ .../opted_in_notice_banner.test.tsx.snap | 51 +++++++++++++++ .../telemetry/public/components/index.ts | 1 + .../public/components/opt_in_banner.test.tsx | 64 +++++++++++++++++++ .../public/components/opt_in_banner.tsx | 4 +- .../public/components/opt_in_message.test.tsx | 4 +- .../public/components/opt_in_message.tsx | 18 +----- .../opted_in_notice_banner.test.tsx | 6 +- .../components/opted_in_notice_banner.tsx | 11 ++-- ....test.tsx => render_opt_in_banner.test.ts} | 27 ++++---- .../render_opted_in_notice_banner.test.ts} | 31 ++++----- .../render_opted_in_notice_banner.tsx | 11 +--- .../public/services/telemetry_sender.test.ts | 3 +- 15 files changed, 291 insertions(+), 68 deletions(-) create mode 100644 src/plugins/telemetry/public/components/__snapshots__/opt_in_banner.test.tsx.snap create mode 100644 src/plugins/telemetry/public/components/__snapshots__/opt_in_example_flyout.test.tsx.snap create mode 100644 src/plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap create mode 100644 src/plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap create mode 100644 src/plugins/telemetry/public/components/opt_in_banner.test.tsx rename src/plugins/telemetry/public/services/telemetry_notifications/{render_opted_in_notice_banner.test.tsx => render_opt_in_banner.test.ts} (56%) rename src/plugins/telemetry/public/{components/opt_in_banner.test.ts => services/telemetry_notifications/render_opted_in_notice_banner.test.ts} (52%) diff --git a/src/plugins/telemetry/public/components/__snapshots__/opt_in_banner.test.tsx.snap b/src/plugins/telemetry/public/components/__snapshots__/opt_in_banner.test.tsx.snap new file mode 100644 index 0000000000000..87e60869f6c21 --- /dev/null +++ b/src/plugins/telemetry/public/components/__snapshots__/opt_in_banner.test.tsx.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`OptInDetailsComponent renders as expected 1`] = ` + + } +> + + + + + + + + + + + + + + + +`; diff --git a/src/plugins/telemetry/public/components/__snapshots__/opt_in_example_flyout.test.tsx.snap b/src/plugins/telemetry/public/components/__snapshots__/opt_in_example_flyout.test.tsx.snap new file mode 100644 index 0000000000000..2601f691cd184 --- /dev/null +++ b/src/plugins/telemetry/public/components/__snapshots__/opt_in_example_flyout.test.tsx.snap @@ -0,0 +1,50 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`OptInDetailsComponent renders as expected 1`] = ` + + + + +

+ +

+
+ + + + + +
+ + + + + + + +
+
+`; diff --git a/src/plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap b/src/plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap new file mode 100644 index 0000000000000..c80485332fa8a --- /dev/null +++ b/src/plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`OptInMessage renders as expected 1`] = ` + + + + , + } + } + /> + +`; diff --git a/src/plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap b/src/plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap new file mode 100644 index 0000000000000..193205cd394e2 --- /dev/null +++ b/src/plugins/telemetry/public/components/__snapshots__/opted_in_notice_banner.test.tsx.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`OptInDetailsComponent renders as expected 1`] = ` + + + + , + "privacyStatementLink": + + , + } + } + /> + + + + + +`; diff --git a/src/plugins/telemetry/public/components/index.ts b/src/plugins/telemetry/public/components/index.ts index e8e6dbdef7eab..f4341154f527a 100644 --- a/src/plugins/telemetry/public/components/index.ts +++ b/src/plugins/telemetry/public/components/index.ts @@ -19,3 +19,4 @@ export { OptInExampleFlyout } from './opt_in_example_flyout'; export { TelemetryManagementSection } from './telemetry_management_section'; +export { OptedInNoticeBanner } from './opted_in_notice_banner'; diff --git a/src/plugins/telemetry/public/components/opt_in_banner.test.tsx b/src/plugins/telemetry/public/components/opt_in_banner.test.tsx new file mode 100644 index 0000000000000..0dc76d65f8879 --- /dev/null +++ b/src/plugins/telemetry/public/components/opt_in_banner.test.tsx @@ -0,0 +1,64 @@ +/* + * 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 React from 'react'; +import { EuiButton } from '@elastic/eui'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { OptInBanner } from './opt_in_banner'; + +describe('OptInDetailsComponent', () => { + it('renders as expected', () => { + expect(shallowWithIntl( {}} />)).toMatchSnapshot(); + }); + + it('fires the "onChangeOptInClick" prop with true when a enable is clicked', () => { + const onClick = jest.fn(); + const component = shallowWithIntl(); + + const enableButton = component.findWhere(n => { + const props = n.props(); + return n.type() === EuiButton && props['data-test-subj'] === 'enable'; + }); + + if (!enableButton) { + throw new Error(`Couldn't find any opt in enable button.`); + } + + enableButton.simulate('click'); + expect(onClick).toHaveBeenCalled(); + expect(onClick).toBeCalledWith(true); + }); + + it('fires the "onChangeOptInClick" with false when a disable is clicked', () => { + const onClick = jest.fn(); + const component = shallowWithIntl(); + + const disableButton = component.findWhere(n => { + const props = n.props(); + return n.type() === EuiButton && props['data-test-subj'] === 'disable'; + }); + + if (!disableButton) { + throw new Error(`Couldn't find any opt in disable button.`); + } + + disableButton.simulate('click'); + expect(onClick).toHaveBeenCalled(); + expect(onClick).toBeCalledWith(false); + }); +}); diff --git a/src/plugins/telemetry/public/components/opt_in_banner.tsx b/src/plugins/telemetry/public/components/opt_in_banner.tsx index d474fb81c5be2..adf7b8bc84719 100644 --- a/src/plugins/telemetry/public/components/opt_in_banner.tsx +++ b/src/plugins/telemetry/public/components/opt_in_banner.tsx @@ -41,7 +41,7 @@ export class OptInBanner extends React.PureComponent { - onChangeOptInClick(true)}> + onChangeOptInClick(true)}> { - onChangeOptInClick(false)}> + onChangeOptInClick(false)}> { it('renders as expected', () => { - expect( - shallowWithIntl( [])} />) - ).toMatchSnapshot(); + expect(shallowWithIntl()).toMatchSnapshot(); }); }); diff --git a/src/plugins/telemetry/public/components/opt_in_message.tsx b/src/plugins/telemetry/public/components/opt_in_message.tsx index a31693efbd7ce..d36d5259fe27a 100644 --- a/src/plugins/telemetry/public/components/opt_in_message.tsx +++ b/src/plugins/telemetry/public/components/opt_in_message.tsx @@ -22,23 +22,7 @@ import { EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { PRIVACY_STATEMENT_URL } from '../../common/constants'; -interface State { - showDetails: boolean; - showExample: boolean; -} - -export class OptInMessage extends React.PureComponent<{}, State> { - public readonly state: State = { - showDetails: false, - showExample: false, - }; - - toggleShowExample = () => { - this.setState(prevState => ({ - showExample: !prevState.showExample, - })); - }; - +export class OptInMessage extends React.PureComponent { render() { return ( diff --git a/src/plugins/telemetry/public/components/opted_in_notice_banner.test.tsx b/src/plugins/telemetry/public/components/opted_in_notice_banner.test.tsx index 008603526be38..f004f20a4a1c4 100644 --- a/src/plugins/telemetry/public/components/opted_in_notice_banner.test.tsx +++ b/src/plugins/telemetry/public/components/opted_in_notice_banner.test.tsx @@ -19,16 +19,16 @@ import React from 'react'; import { EuiButton } from '@elastic/eui'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { OptedInBanner } from './opted_in_notice_banner'; +import { OptedInNoticeBanner } from './opted_in_notice_banner'; describe('OptInDetailsComponent', () => { it('renders as expected', () => { - expect(shallowWithIntl( {}} />)).toMatchSnapshot(); + expect(shallowWithIntl( {}} />)).toMatchSnapshot(); }); it('fires the "onSeenBanner" prop when a link is clicked', () => { const onLinkClick = jest.fn(); - const component = shallowWithIntl(); + const component = shallowWithIntl(); const button = component.findWhere(n => n.type() === EuiButton); diff --git a/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx b/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx index 15ec6d273a79d..21ac7ff600c53 100644 --- a/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx +++ b/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx @@ -22,16 +22,15 @@ import * as React from 'react'; import { EuiButton, EuiLink, EuiCallOut, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { PATH_TO_ADVANCED_SETTINGS, PRIVACY_STATEMENT_URL } from '../../common/constants'; interface Props { onSeenBanner: () => any; - privacyStatementHref: string; - disableTelemetryHref: string; } -export class OptedInBanner extends React.PureComponent { +export class OptedInNoticeBanner extends React.PureComponent { render() { - const { privacyStatementHref, disableTelemetryHref, onSeenBanner } = this.props; + const { onSeenBanner } = this.props; return ( @@ -42,7 +41,7 @@ export class OptedInBanner extends React.PureComponent { privacyStatementLink: ( @@ -53,7 +52,7 @@ export class OptedInBanner extends React.PureComponent { ), disableLink: ( - + { +describe('renderOptInBanner', () => { it('adds a banner to banners with priority of 10000', () => { const bannerID = 'brucer-wayne'; + const overlays = overlayServiceMock.createStartContract(); + overlays.banners.add.mockReturnValue(bannerID); - const telemetryOptInProvider = { setOptInBannerNoticeId: jest.fn() }; - const banners = { add: jest.fn().mockReturnValue(bannerID) }; + const returnedBannerId = renderOptInBanner({ + setOptIn: jest.fn(), + overlays, + }); - renderOptedInBanner(telemetryOptInProvider, { _banners: banners }); + expect(overlays.banners.add).toBeCalledTimes(1); - expect(banners.add).toBeCalledTimes(1); - expect(telemetryOptInProvider.setOptInBannerNoticeId).toBeCalledWith(bannerID); + expect(returnedBannerId).toBe(bannerID); + const bannerConfig = overlays.banners.add.mock.calls[0]; - const bannerConfig = banners.add.mock.calls[0][0]; - - expect(bannerConfig.component).not.toBe(undefined); - expect(bannerConfig.priority).toBe(10000); + expect(bannerConfig[0]).not.toBe(undefined); + expect(bannerConfig[1]).toBe(10000); }); }); diff --git a/src/plugins/telemetry/public/components/opt_in_banner.test.ts b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.test.ts similarity index 52% rename from src/plugins/telemetry/public/components/opt_in_banner.test.ts rename to src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.test.ts index b4a86b36d922f..2d175024a74fb 100644 --- a/src/plugins/telemetry/public/components/opt_in_banner.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.test.ts @@ -17,26 +17,27 @@ * under the License. */ -import '../../services/telemetry_opt_in.test.mocks'; -import { renderBanner } from './render_banner'; +import { renderOptedInNoticeBanner } from './render_opted_in_notice_banner'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { overlayServiceMock } from '../../../../../core/public/overlays/overlay_service.mock'; -describe('render_banner', () => { +describe('renderOptedInNoticeBanner', () => { it('adds a banner to banners with priority of 10000', () => { - const bannerID = 'brucer-banner'; + const bannerID = 'brucer-wayne'; + const overlays = overlayServiceMock.createStartContract(); + overlays.banners.add.mockReturnValue(bannerID); - const telemetryOptInProvider = { setBannerId: jest.fn() }; - const banners = { add: jest.fn().mockReturnValue(bannerID) }; - const fetchTelemetry = jest.fn(); + const returnedBannerId = renderOptedInNoticeBanner({ + onSeen: jest.fn(), + overlays, + }); - renderBanner(telemetryOptInProvider, fetchTelemetry, { _banners: banners }); + expect(overlays.banners.add).toBeCalledTimes(1); - expect(banners.add).toBeCalledTimes(1); - expect(fetchTelemetry).toBeCalledTimes(0); - expect(telemetryOptInProvider.setBannerId).toBeCalledWith(bannerID); + expect(returnedBannerId).toBe(bannerID); + const bannerConfig = overlays.banners.add.mock.calls[0]; - const bannerConfig = banners.add.mock.calls[0][0]; - - expect(bannerConfig.component).not.toBe(undefined); - expect(bannerConfig.priority).toBe(10000); + expect(bannerConfig[0]).not.toBe(undefined); + expect(bannerConfig[1]).toBe(10000); }); }); diff --git a/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx index 91ea254725c98..41997421d9c55 100644 --- a/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx +++ b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx @@ -19,8 +19,7 @@ import React from 'react'; import { CoreStart } from 'kibana/public'; -import { OptedInBanner } from '../../components/opted_in_notice_banner'; -import { PATH_TO_ADVANCED_SETTINGS, PRIVACY_STATEMENT_URL } from '../../../common/constants'; +import { OptedInNoticeBanner } from '../../components'; import { toMountPoint } from '../../../../kibana_react/public'; interface RenderBannerConfig { @@ -28,13 +27,7 @@ interface RenderBannerConfig { onSeen: () => void; } export function renderOptedInNoticeBanner({ onSeen, overlays }: RenderBannerConfig) { - const mount = toMountPoint( - - ); + const mount = toMountPoint(); const bannerId = overlays.banners.add(mount, 10000); return bannerId; diff --git a/src/plugins/telemetry/public/services/telemetry_sender.test.ts b/src/plugins/telemetry/public/services/telemetry_sender.test.ts index bd6fe55376f10..33aba5ec1ce31 100644 --- a/src/plugins/telemetry/public/services/telemetry_sender.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_sender.test.ts @@ -18,9 +18,10 @@ */ import { TelemetrySender } from './telemetry_sender'; +import { mockTelemetryService } from '../telemetry.mock'; import { REPORT_INTERVAL_MS, LOCALSTORAGE_KEY } from '../../common/constants'; -describe('telemetry class', () => { +describe('TelemetrySender', () => { const clusters = [{ cluster_uuid: 'fake-123' }, { cluster_uuid: 'fake-456' }]; const telemetryUrl = 'https://not.a.valid.url.0'; const mockFetchTelemetry = () => Promise.resolve({ data: clusters }); From 7bdb66772dee17b1932f086f5aacfdb3464cae84 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Fri, 31 Jan 2020 16:34:23 +0200 Subject: [PATCH 06/41] fix all tests --- src/plugins/telemetry/public/plugin.ts | 1 - .../render_opted_in_notice_banner.tsx | 2 +- .../public/services/telemetry_sender.test.ts | 458 +++++++++--------- .../public/services/telemetry_sender.ts | 22 +- .../public/services/telemetry_service.test.ts | 171 ++++--- .../public/services/telemetry_service.ts | 2 +- .../telemetry/public/telemetry.mock.ts | 8 +- 7 files changed, 346 insertions(+), 318 deletions(-) diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index 60be9e83ed650..d61af80995e61 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -34,7 +34,6 @@ export class TelemetryPlugin implements Plugin { public start({ injectedMetadata, - application, http, notifications, overlays, diff --git a/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx index 41997421d9c55..e63e46af6e8ca 100644 --- a/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx +++ b/src/plugins/telemetry/public/services/telemetry_notifications/render_opted_in_notice_banner.tsx @@ -19,7 +19,7 @@ import React from 'react'; import { CoreStart } from 'kibana/public'; -import { OptedInNoticeBanner } from '../../components'; +import { OptedInNoticeBanner } from '../../components/opted_in_notice_banner'; import { toMountPoint } from '../../../../kibana_react/public'; interface RenderBannerConfig { diff --git a/src/plugins/telemetry/public/services/telemetry_sender.test.ts b/src/plugins/telemetry/public/services/telemetry_sender.test.ts index 33aba5ec1ce31..5b0474550728b 100644 --- a/src/plugins/telemetry/public/services/telemetry_sender.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_sender.test.ts @@ -21,287 +21,261 @@ import { TelemetrySender } from './telemetry_sender'; import { mockTelemetryService } from '../telemetry.mock'; import { REPORT_INTERVAL_MS, LOCALSTORAGE_KEY } from '../../common/constants'; +class LocalStorageMock implements Partial { + getItem = jest.fn(); + setItem = jest.fn(); +} + describe('TelemetrySender', () => { - const clusters = [{ cluster_uuid: 'fake-123' }, { cluster_uuid: 'fake-456' }]; - const telemetryUrl = 'https://not.a.valid.url.0'; - const mockFetchTelemetry = () => Promise.resolve({ data: clusters }); - // returns a function that behaves like the injector by fetching the requested key from the object directly - // for example: - // { '$http': jest.fn() } would be how to mock the '$http' injector value - const mockInjectorFromObject = object => { - return { get: key => object[key] }; - }; + let originalLocalStorage: Storage; + let mockLocalStorage: LocalStorageMock; + beforeAll(() => { + originalLocalStorage = window.localStorage; + }); + + // @ts-ignore + beforeEach(() => (window.localStorage = mockLocalStorage = new LocalStorageMock())); + // @ts-ignore + afterAll(() => (window.localStorage = originalLocalStorage)); + + // const clusters = [{ cluster_uuid: 'fake-123' }, { cluster_uuid: 'fake-456' }]; + // const telemetryUrl = 'https://not.a.valid.url.0'; + // const mockFetchTelemetry = () => Promise.resolve({ data: clusters }); + // // returns a function that behaves like the injector by fetching the requested key from the object directly + // // for example: + // // { '$http': jest.fn() } would be how to mock the '$http' injector value + // const mockInjectorFromObject = object => { + // return { get: key => object[key] }; + // }; describe('constructor', () => { - test('defaults lastReport if unset', () => { - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce(undefined), - }, - $http: jest.fn(), - telemetryOptedIn: true, - telemetryUrl, - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); - - expect(telemetry._storage).toBe(injector.localStorage); - expect(telemetry._$http).toBe(injector.$http); - expect(telemetry._telemetryOptedIn).toBe(injector.telemetryOptedIn); - expect(telemetry._telemetryUrl).toBe(injector.telemetryUrl); - expect(telemetry._fetchTelemetry).toBe(mockFetchTelemetry); - expect(telemetry._sending).toBe(false); - expect(telemetry._lastReport).toBeUndefined(); - - expect(injector.localStorage.get).toHaveBeenCalledTimes(1); - expect(injector.localStorage.get).toHaveBeenCalledWith(LOCALSTORAGE_KEY); + it('defaults lastReport if unset', () => { + const telemetryService = mockTelemetryService(); + const telemetrySender = new TelemetrySender(telemetryService); + expect(telemetrySender.lastReported).toBeUndefined(); + expect(mockLocalStorage.getItem).toBeCalledTimes(1); + expect(mockLocalStorage.getItem).toHaveBeenCalledWith(LOCALSTORAGE_KEY); }); - test('uses lastReport if set', () => { - const lastReport = Date.now(); - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce({ lastReport }), - }, - $http: jest.fn(), - telemetryOptedIn: true, - telemetryUrl, - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); - - expect(telemetry._storage).toBe(injector.localStorage); - expect(telemetry._$http).toBe(injector.$http); - expect(telemetry._telemetryOptedIn).toBe(injector.telemetryOptedIn); - expect(telemetry._telemetryUrl).toBe(injector.telemetryUrl); - expect(telemetry._fetchTelemetry).toBe(mockFetchTelemetry); - expect(telemetry._sending).toBe(false); - expect(telemetry._lastReport).toBe(lastReport); - - expect(injector.localStorage.get).toHaveBeenCalledTimes(1); - expect(injector.localStorage.get).toHaveBeenCalledWith(LOCALSTORAGE_KEY); + it('uses lastReport if set', () => { + const lastReport = `${Date.now()}`; + mockLocalStorage.getItem.mockReturnValueOnce(JSON.stringify({ lastReport })); + const telemetryService = mockTelemetryService(); + const telemetrySender = new TelemetrySender(telemetryService); + expect(telemetrySender.lastReported).toBe(lastReport); }); }); - test('_saveToBrowser uses _lastReport', () => { - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce({ random: 'junk', gets: 'thrown away' }), - set: jest.fn(), - }, - }; - const lastReport = Date.now(); - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); - telemetry._lastReport = lastReport; - - telemetry._saveToBrowser(); - - expect(injector.localStorage.set).toHaveBeenCalledTimes(1); - expect(injector.localStorage.set).toHaveBeenCalledWith(LOCALSTORAGE_KEY, { lastReport }); + describe('saveToBrowser', () => { + it('uses lastReport', () => { + const lastReport = `${Date.now()}`; + const telemetryService = mockTelemetryService(); + const telemetrySender = new TelemetrySender(telemetryService); + telemetrySender.lastReported = lastReport; + telemetrySender.saveToBrowser(); + + expect(mockLocalStorage.setItem).toHaveBeenCalledTimes(1); + expect(mockLocalStorage.setItem).toHaveBeenCalledWith( + LOCALSTORAGE_KEY, + JSON.stringify({ lastReport }) + ); + }); }); - describe('_checkReportStatus', () => { - // send the report if we get to check the time - const lastReportShouldSendNow = Date.now() - REPORT_INTERVAL_MS - 1; + describe('shouldSendReport', () => { + it('returns false whenever optIn is false', () => { + const telemetryService = mockTelemetryService(); + telemetryService.getIsOptedIn = jest.fn().mockReturnValue(false); + const telemetrySender = new TelemetrySender(telemetryService); + const shouldSendRerpot = telemetrySender.shouldSendReport(); - test('returns false whenever telemetryOptedIn is null', () => { - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce({ lastReport: lastReportShouldSendNow }), - }, - telemetryOptedIn: null, // not yet opted in - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); - - expect(telemetry._checkReportStatus()).toBe(false); + expect(telemetryService.getIsOptedIn).toBeCalledTimes(1); + expect(shouldSendRerpot).toBe(false); }); - test('returns false whenever telemetryOptedIn is false', () => { - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce({ lastReport: lastReportShouldSendNow }), - }, - telemetryOptedIn: false, // opted out explicitly - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); + it('returns true if lastReported is undefined', () => { + const telemetryService = mockTelemetryService(); + telemetryService.getIsOptedIn = jest.fn().mockReturnValue(true); + const telemetrySender = new TelemetrySender(telemetryService); + const shouldSendRerpot = telemetrySender.shouldSendReport(); - expect(telemetry._checkReportStatus()).toBe(false); + expect(telemetrySender.lastReported).toBeUndefined(); + expect(shouldSendRerpot).toBe(true); }); - // FLAKY: https://github.com/elastic/kibana/issues/27922 - test.skip('returns false if last report is too recent', () => { - const injector = { - localStorage: { - // we expect '>', not '>=' - get: jest.fn().mockReturnValueOnce({ lastReport: Date.now() - REPORT_INTERVAL_MS }), - }, - telemetryOptedIn: true, - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); - - expect(telemetry._checkReportStatus()).toBe(false); + it('returns true if lastReported passed REPORT_INTERVAL_MS', () => { + const lastReported = Date.now() - (REPORT_INTERVAL_MS + 1000); + + const telemetryService = mockTelemetryService(); + telemetryService.getIsOptedIn = jest.fn().mockReturnValue(true); + const telemetrySender = new TelemetrySender(telemetryService); + telemetrySender.lastReported = `${lastReported}`; + const shouldSendRerpot = telemetrySender.shouldSendReport(); + expect(shouldSendRerpot).toBe(true); }); - test('returns true if last report is not defined', () => { - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce({}), - }, - telemetryOptedIn: true, - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); + it('returns false if lastReported is within REPORT_INTERVAL_MS', () => { + const lastReported = Date.now() + 1000; - expect(telemetry._checkReportStatus()).toBe(true); + const telemetryService = mockTelemetryService(); + telemetryService.getIsOptedIn = jest.fn().mockReturnValue(true); + const telemetrySender = new TelemetrySender(telemetryService); + telemetrySender.lastReported = `${lastReported}`; + const shouldSendRerpot = telemetrySender.shouldSendReport(); + expect(shouldSendRerpot).toBe(false); }); - test('returns true if last report is defined and old enough', () => { - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce({ lastReport: lastReportShouldSendNow }), - }, - telemetryOptedIn: true, - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); - - expect(telemetry._checkReportStatus()).toBe(true); + it('returns true if lastReported is malformed', () => { + const telemetryService = mockTelemetryService(); + telemetryService.getIsOptedIn = jest.fn().mockReturnValue(true); + const telemetrySender = new TelemetrySender(telemetryService); + telemetrySender.lastReported = `random_malformed_string`; + const shouldSendRerpot = telemetrySender.shouldSendReport(); + expect(shouldSendRerpot).toBe(true); }); - test('returns true if last report is defined and old enough as a string', () => { - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce({ lastReport: lastReportShouldSendNow.toString() }), - }, - telemetryOptedIn: true, - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); + describe('sendIfDue', () => { + let originalFetch: typeof window['fetch']; + let mockFetch: jest.Mock; - expect(telemetry._checkReportStatus()).toBe(true); - }); + beforeAll(() => { + originalFetch = window.fetch; + }); - test('returns true if last report is defined and malformed', () => { - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce({ lastReport: { not: { a: 'number' } } }), - }, - telemetryOptedIn: true, - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); + // @ts-ignore + beforeEach(() => (window.fetch = mockFetch = jest.fn())); + // @ts-ignore + afterAll(() => (window.fetch = originalFetch)); - expect(telemetry._checkReportStatus()).toBe(true); - }); - }); + it('does not send if already sending', async () => { + const telemetryService = mockTelemetryService(); + const telemetrySender = new TelemetrySender(telemetryService); + telemetrySender.shouldSendReport = jest.fn(); + telemetrySender.isSending = true; + await telemetrySender.sendIfDue(); - describe('_sendIfDue', () => { - test('ignores and returns false if already sending', () => { - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce(undefined), // never sent - }, - telemetryOptedIn: true, - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); - telemetry._sending = true; - - return expect(telemetry._sendIfDue()).resolves.toBe(false); - }); + expect(telemetrySender.shouldSendReport).toBeCalledTimes(0); + expect(mockFetch).toBeCalledTimes(0); + }); - test('ignores and returns false if _checkReportStatus says so', () => { - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce(undefined), // never sent, so it would try if opted in - }, - telemetryOptedIn: false, // opted out - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); + it('does not send if shouldSendReport returns false', async () => { + const telemetryService = mockTelemetryService(); + const telemetrySender = new TelemetrySender(telemetryService); + telemetrySender.shouldSendReport = jest.fn().mockReturnValue(false); + telemetrySender.isSending = false; + await telemetrySender.sendIfDue(); - return expect(telemetry._sendIfDue()).resolves.toBe(false); - }); + expect(telemetrySender.shouldSendReport).toBeCalledTimes(1); + expect(mockFetch).toBeCalledTimes(0); + }); - test('sends telemetry when requested', () => { - const now = Date.now(); - const injector = { - $http: jest.fn().mockResolvedValue({}), // ignored response - localStorage: { - get: jest.fn().mockReturnValueOnce({ lastReport: now - REPORT_INTERVAL_MS - 1 }), - set: jest.fn(), - }, - telemetryOptedIn: true, - telemetryUrl, - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); - - expect.hasAssertions(); - - return telemetry._sendIfDue().then(result => { - expect(result).toBe(true); - expect(telemetry._sending).toBe(false); - - // should be updated - const lastReport = telemetry._lastReport; - - // if the test runs fast enough it should be exactly equal, but probably a few ms greater - expect(lastReport).toBeGreaterThanOrEqual(now); - - expect(injector.$http).toHaveBeenCalledTimes(2); - // assert that it sent every cluster's telemetry - clusters.forEach(cluster => { - expect(injector.$http).toHaveBeenCalledWith({ - method: 'POST', - url: telemetryUrl, - data: cluster, - kbnXsrfToken: false, - }); + it('sends report if due', async () => { + const mockTelemetryUrl = 'telemetry_cluster_url'; + const mockTelemetryPayload = ['hashed_cluster_usage_data1']; + + const telemetryService = mockTelemetryService(); + const telemetrySender = new TelemetrySender(telemetryService); + telemetryService.getTelemetryUrl = jest.fn().mockReturnValue(mockTelemetryUrl); + telemetryService.fetchTelemetry = jest.fn().mockReturnValue(mockTelemetryPayload); + telemetrySender.shouldSendReport = jest.fn().mockReturnValue(true); + telemetrySender.isSending = false; + await telemetrySender.sendIfDue(); + + expect(telemetryService.fetchTelemetry).toBeCalledTimes(1); + expect(mockFetch).toBeCalledTimes(1); + expect(mockFetch).toBeCalledWith(mockTelemetryUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: mockTelemetryPayload[0], }); + }); + + it('sends report separately for every cluster', async () => { + const mockTelemetryUrl = 'telemetry_cluster_url'; + const mockTelemetryPayload = ['hashed_cluster_usage_data1', 'hashed_cluster_usage_data2']; - expect(injector.localStorage.set).toHaveBeenCalledTimes(1); - expect(injector.localStorage.set).toHaveBeenCalledWith(LOCALSTORAGE_KEY, { lastReport }); + const telemetryService = mockTelemetryService(); + const telemetrySender = new TelemetrySender(telemetryService); + telemetryService.getTelemetryUrl = jest.fn().mockReturnValue(mockTelemetryUrl); + telemetryService.fetchTelemetry = jest.fn().mockReturnValue(mockTelemetryPayload); + telemetrySender.shouldSendReport = jest.fn().mockReturnValue(true); + telemetrySender.isSending = false; + await telemetrySender.sendIfDue(); + + expect(telemetryService.fetchTelemetry).toBeCalledTimes(1); + expect(mockFetch).toBeCalledTimes(2); }); - }); - test('sends telemetry when requested and catches exceptions', () => { - const lastReport = Date.now() - REPORT_INTERVAL_MS - 1; - const injector = { - $http: jest.fn().mockRejectedValue(new Error('TEST - expected')), // caught failure - localStorage: { - get: jest.fn().mockReturnValueOnce({ lastReport }), - set: jest.fn(), - }, - telemetryOptedIn: true, - telemetryUrl, - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); - - expect.hasAssertions(); - - return telemetry._sendIfDue().then(result => { - expect(result).toBe(true); // attempted to send - expect(telemetry._sending).toBe(false); - - // should be unchanged - expect(telemetry._lastReport).toBe(lastReport); - expect(injector.localStorage.set).toHaveBeenCalledTimes(0); - - expect(injector.$http).toHaveBeenCalledTimes(2); - // assert that it sent every cluster's telemetry - clusters.forEach(cluster => { - expect(injector.$http).toHaveBeenCalledWith({ - method: 'POST', - url: telemetryUrl, - data: cluster, - kbnXsrfToken: false, - }); + it('updates last lastReported and calls saveToBrowser', async () => { + const mockTelemetryUrl = 'telemetry_cluster_url'; + const mockTelemetryPayload = ['hashed_cluster_usage_data1']; + + const telemetryService = mockTelemetryService(); + const telemetrySender = new TelemetrySender(telemetryService); + telemetryService.getTelemetryUrl = jest.fn().mockReturnValue(mockTelemetryUrl); + telemetryService.fetchTelemetry = jest.fn().mockReturnValue(mockTelemetryPayload); + telemetrySender.shouldSendReport = jest.fn().mockReturnValue(true); + telemetrySender.saveToBrowser = jest.fn(); + + await telemetrySender.sendIfDue(); + + expect(mockFetch).toBeCalledTimes(1); + expect(telemetrySender.lastReported).toBeDefined(); + expect(telemetrySender.saveToBrowser).toBeCalledTimes(1); + expect(telemetrySender.isSending).toBe(false); + }); + + it('catches fetchTelemetry errors and sets isSending to false', async () => { + const telemetryService = mockTelemetryService(); + const telemetrySender = new TelemetrySender(telemetryService); + telemetryService.getTelemetryUrl = jest.fn(); + telemetryService.fetchTelemetry = jest.fn().mockImplementation(() => { + throw Error('Error fetching usage'); }); + await telemetrySender.sendIfDue(); + expect(telemetryService.fetchTelemetry).toBeCalledTimes(1); + expect(telemetrySender.lastReported).toBeUndefined(); + expect(telemetrySender.isSending).toBe(false); + }); + + it('catches fetch errors and sets isSending to false', async () => { + const mockTelemetryPayload = ['hashed_cluster_usage_data1', 'hashed_cluster_usage_data2']; + const telemetryService = mockTelemetryService(); + const telemetrySender = new TelemetrySender(telemetryService); + telemetryService.getTelemetryUrl = jest.fn(); + telemetryService.fetchTelemetry = jest.fn().mockReturnValue(mockTelemetryPayload); + mockFetch.mockImplementation(() => { + throw Error('Error sending usage'); + }); + await telemetrySender.sendIfDue(); + expect(telemetryService.fetchTelemetry).toBeCalledTimes(1); + expect(mockFetch).toBeCalledTimes(2); + expect(telemetrySender.lastReported).toBeUndefined(); + expect(telemetrySender.isSending).toBe(false); }); }); }); + describe('startChecking', () => { + let originalSetInterval: typeof window['setInterval']; + let mockSetInterval: jest.Mock; - test('start', () => { - const injector = { - localStorage: { - get: jest.fn().mockReturnValueOnce(undefined), - }, - telemetryOptedIn: false, // opted out - }; - const telemetry = new Telemetry(mockInjectorFromObject(injector), mockFetchTelemetry); + beforeAll(() => { + originalSetInterval = window.setInterval; + }); - clearInterval(telemetry.start()); + // @ts-ignore + beforeEach(() => (window.setInterval = mockSetInterval = jest.fn())); + // @ts-ignore + afterAll(() => (window.setInterval = originalSetInterval)); + + it('calls sendIfDue every 60000 ms', () => { + const telemetryService = mockTelemetryService(); + const telemetrySender = new TelemetrySender(telemetryService); + telemetrySender.startChecking(); + expect(mockSetInterval).toBeCalledTimes(1); + expect(mockSetInterval).toBeCalledWith(telemetrySender.sendIfDue, 60000); + }); }); }); diff --git a/src/plugins/telemetry/public/services/telemetry_sender.ts b/src/plugins/telemetry/public/services/telemetry_sender.ts index d64ec539e8c81..b0fb947f510bb 100644 --- a/src/plugins/telemetry/public/services/telemetry_sender.ts +++ b/src/plugins/telemetry/public/services/telemetry_sender.ts @@ -37,29 +37,29 @@ export class TelemetrySender { } } - private saveToBrowser() { + private saveToBrowser = () => { // we are the only code that manipulates this key, so it's safe to blindly overwrite the whole object this.storage.set(LOCALSTORAGE_KEY, { lastReport: this.lastReported }); - } + }; - private shouldSendReport(): boolean { + private shouldSendReport = (): boolean => { // check if opt-in for telemetry is enabled if (this.telemetryService.getIsOptedIn()) { if (!this.lastReported) { return true; } // returns NaN for any malformed or unset (null/undefined) value - const lastReport = parseInt(this.lastReported, 10); + const lastReported = parseInt(this.lastReported, 10); // If it's been a day since we last sent telemetry - if (isNaN(lastReport) || Date.now() - lastReport > REPORT_INTERVAL_MS) { + if (isNaN(lastReported) || Date.now() - lastReported > REPORT_INTERVAL_MS) { return true; } } return false; - } + }; - private async sendIfDue() { + private sendIfDue = async (): Promise => { if (this.isSending || !this.shouldSendReport()) { return; } @@ -89,9 +89,9 @@ export class TelemetrySender { } finally { this.isSending = false; } - } + }; - public startChecking() { - return setInterval(() => this.sendIfDue(), 60000); - } + public startChecking = () => { + return setInterval(this.sendIfDue, 60000); + }; } diff --git a/src/plugins/telemetry/public/services/telemetry_service.test.ts b/src/plugins/telemetry/public/services/telemetry_service.test.ts index a837fb31dcd4a..87740b19720bc 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.test.ts @@ -16,71 +16,122 @@ * specific language governing permissions and limitations * under the License. */ +import { mockTelemetryService } from '../telemetry.mock'; -import expect from '@kbn/expect'; -import sinon from 'sinon'; - -import { fetchTelemetry } from '../fetch_telemetry'; +const mockSubtract = jest.fn().mockImplementation(() => { + return { + toISOString: jest.fn(), + }; +}); -describe('fetch_telemetry', () => { - it('fetchTelemetry calls expected URL with 20 minutes - now', () => { - const response = Promise.resolve(); - const $http = { - post: sinon.stub(), - }; - const basePath = 'fake'; - const moment = { - subtract: sinon.stub(), - toISOString: () => 'max123', +jest.mock('moment', () => { + return jest.fn().mockImplementation(() => { + return { + subtract: mockSubtract, + toISOString: jest.fn(), }; + }); +}); - moment.subtract.withArgs(20, 'minutes').returns({ - toISOString: () => 'min456', +describe('TelemetryService', () => { + describe('fetchTelemetry', () => { + it('calls expected URL with 20 minutes - now', async () => { + const telemetryService = mockTelemetryService(); + await telemetryService.fetchTelemetry(); + expect(telemetryService.http.post).toBeCalledWith('/api/telemetry/v2/clusters/_stats', { + body: JSON.stringify({ unencrypted: false, timeRange: {} }), + }); + expect(mockSubtract).toBeCalledWith(20, 'minutes'); }); + }); - $http.post - .withArgs(`fake/api/telemetry/v2/clusters/_stats`, { - unencrypted: true, - timeRange: { - min: 'min456', - max: 'max123', - }, - }) - .returns(response); - - expect(fetchTelemetry($http, { basePath, _moment: () => moment, unencrypted: true })).to.be( - response - ); + describe('fetchExample', () => { + it('calls fetchTelemetry with unencrupted: true', async () => { + const telemetryService = mockTelemetryService(); + telemetryService.fetchTelemetry = jest.fn(); + await telemetryService.fetchExample(); + expect(telemetryService.fetchTelemetry).toBeCalledWith({ unencrypted: true }); + }); }); -}); -// describe('setOptIn', () => { -// it('sets setting unsuccessfully, adds toast, and does not touch banner', async () => { -// const optIn = true; -// const telemetryService = mockTelemetryService(); -// const telemetryNotifications = mockTelemetryNotifications({ telemetryService }); -// await telemetryNotifications['onSetOptInClick'](optIn); - -// }); - -// // it('sets setting unsuccessfully with error, adds toast, and does not touch banner', async () => { -// // const toastNotifications = { -// // addDanger: sinon.spy(), -// // }; -// // const banners = { -// // remove: sinon.spy(), -// // }; -// // const optIn = false; -// // mockInjectedMetadata({ telemetryOptedIn: null, allowChangingOptInStatus: true }); -// // const telemetryOptInProvider = getTelemetryOptInProvider({ simulateError: true }); - -// // await clickBanner(telemetryOptInProvider, optIn, { -// // _banners: banners, -// // _toastNotifications: toastNotifications, -// // }); - -// // expect(telemetryOptInProvider.getOptIn()).toBe(null); -// // expect(toastNotifications.addDanger.calledOnce).toBe(true); -// // expect(banners.remove.notCalled).toBe(true); -// // }); -// }) + describe('setOptIn', () => { + it('calls api if canChangeOptInStatus', async () => { + const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); + telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); + await telemetryService.setOptIn(true); + + expect(telemetryService.http.post).toBeCalledTimes(1); + }); + + it('sends enabled true if optedIn: true', async () => { + const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); + telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); + const optedIn = true; + await telemetryService.setOptIn(optedIn); + + expect(telemetryService.http.post).toBeCalledWith('/api/telemetry/v2/optIn', { + body: JSON.stringify({ enabled: optedIn }), + }); + }); + + it('sends enabled false if optedIn: false', async () => { + const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); + telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); + const optedIn = false; + await telemetryService.setOptIn(optedIn); + + expect(telemetryService.http.post).toBeCalledWith('/api/telemetry/v2/optIn', { + body: JSON.stringify({ enabled: optedIn }), + }); + }); + + it('does not call reportOptInStatus if reportOptInStatusChange is false', async () => { + const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); + telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); + telemetryService.reportOptInStatus = jest.fn(); + await telemetryService.setOptIn(true); + + expect(telemetryService.reportOptInStatus).toBeCalledTimes(0); + expect(telemetryService.http.post).toBeCalledTimes(1); + }); + + it('calls reportOptInStatus if reportOptInStatusChange is true', async () => { + const telemetryService = mockTelemetryService({ reportOptInStatusChange: true }); + telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); + telemetryService.reportOptInStatus = jest.fn(); + await telemetryService.setOptIn(true); + + expect(telemetryService.reportOptInStatus).toBeCalledTimes(1); + expect(telemetryService.http.post).toBeCalledTimes(1); + }); + + it('adds an error toast on api error', async () => { + const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); + telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); + telemetryService.reportOptInStatus = jest.fn(); + telemetryService.http.post = jest.fn().mockImplementation((url: string) => { + if (url === '/api/telemetry/v2/optIn') { + throw Error('failed to update opt in.'); + } + }); + + await telemetryService.setOptIn(true); + expect(telemetryService.http.post).toBeCalledTimes(1); + expect(telemetryService.reportOptInStatus).toBeCalledTimes(0); + expect(telemetryService.notifications.toasts.addError).toBeCalledTimes(1); + }); + + it('adds an error toast on reportOptInStatus error', async () => { + const telemetryService = mockTelemetryService({ reportOptInStatusChange: true }); + telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); + telemetryService.reportOptInStatus = jest.fn().mockImplementation(() => { + throw Error('failed to report OptIn Status.'); + }); + + await telemetryService.setOptIn(true); + expect(telemetryService.http.post).toBeCalledTimes(1); + expect(telemetryService.reportOptInStatus).toBeCalledTimes(1); + expect(telemetryService.notifications.toasts.addError).toBeCalledTimes(1); + }); + }); +}); diff --git a/src/plugins/telemetry/public/services/telemetry_service.ts b/src/plugins/telemetry/public/services/telemetry_service.ts index e4ec8d38db452..073886e7d1327 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.ts @@ -88,7 +88,7 @@ export class TelemetryService { public fetchTelemetry = async ({ unencrypted = false } = {}) => { const now = moment(); - return this.http.post(`/api/telemetry/v2/clusters/_stats`, { + return this.http.post('/api/telemetry/v2/clusters/_stats', { body: JSON.stringify({ unencrypted, timeRange: { diff --git a/src/plugins/telemetry/public/telemetry.mock.ts b/src/plugins/telemetry/public/telemetry.mock.ts index 510be7cf9de87..93dc13c327509 100644 --- a/src/plugins/telemetry/public/telemetry.mock.ts +++ b/src/plugins/telemetry/public/telemetry.mock.ts @@ -25,10 +25,13 @@ import { httpServiceMock } from '../../../core/public/http/http_service.mock'; import { notificationServiceMock } from '../../../core/public/notifications/notifications_service.mock'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { injectedMetadataServiceMock } from '../../../core/public/injected_metadata/injected_metadata_service.mock'; -import { TelemetryService, TelemetryNotifications } from './services'; +import { TelemetryService } from './services/telemetry_service'; +import { TelemetryNotifications } from './services/telemetry_notifications/telemetry_notifications'; import { TelemetryPluginStart } from './plugin'; -export function mockTelemetryService() { +export function mockTelemetryService({ + reportOptInStatusChange, +}: { reportOptInStatusChange?: boolean } = {}) { const injectedMetadata = injectedMetadataServiceMock.createStartContract(); injectedMetadata.getInjectedVar.mockImplementation((key: string) => { switch (key) { @@ -48,6 +51,7 @@ export function mockTelemetryService() { injectedMetadata, http: httpServiceMock.createStartContract(), notifications: notificationServiceMock.createStartContract(), + reportOptInStatusChange, }); } From 9f7e69b78c5774be812bdd7710745ef3d5d5f4d1 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Fri, 31 Jan 2020 16:47:29 +0200 Subject: [PATCH 07/41] self code review --- .../kibana/public/home/np_ready/components/welcome.tsx | 7 ++----- src/legacy/core_plugins/kibana/public/home/plugin.ts | 2 +- src/legacy/core_plugins/telemetry/index.ts | 1 - .../server/handle_old_settings/handle_old_settings.ts | 7 +++++-- .../telemetry/public/components/opt_in_message.tsx | 2 +- .../telemetry/public/services/telemetry_sender.test.ts | 10 ---------- 6 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx index b146005f83b62..7906caeda1b38 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx @@ -39,6 +39,7 @@ import { METRIC_TYPE } from '@kbn/analytics'; import { FormattedMessage } from '@kbn/i18n/react'; import { getServices } from '../../kibana_services'; import { TelemetryPluginStart } from '../../../../../../../plugins/telemetry/public'; +import { PRIVACY_STATEMENT_URL } from '../../../../../../../plugins/telemetry/common/constants'; import { SampleDataCard } from './sample_data'; interface Props { @@ -170,11 +171,7 @@ export class Welcome extends React.Component { id="kbn.home.dataManagementDisclaimerPrivacy" defaultMessage="To learn about how usage data helps us manage and improve our products and services, see our " /> - + { allowChangingOptInStatus: config.get('telemetry.allowChangingOptInStatus'), telemetrySendUsageFrom: config.get('telemetry.sendUsageFrom'), telemetryNotifyUserAboutOptInDefault: false, - telemetryBanner1: 'lol', }; }, mappings, diff --git a/src/legacy/core_plugins/telemetry/server/handle_old_settings/handle_old_settings.ts b/src/legacy/core_plugins/telemetry/server/handle_old_settings/handle_old_settings.ts index e01abcad41bfb..143f5e982da6e 100644 --- a/src/legacy/core_plugins/telemetry/server/handle_old_settings/handle_old_settings.ts +++ b/src/legacy/core_plugins/telemetry/server/handle_old_settings/handle_old_settings.ts @@ -42,9 +42,12 @@ export async function handleOldSettings(server: Server) { const oldAllowReportSetting = await uiSettings.get(CONFIG_ALLOW_REPORT); let legacyOptInValue = null; - if (typeof oldTelemetrySetting === 'boolean') { + if (typeof oldTelemetrySetting === 'boolean' && uiSettings.isOverridden(CONFIG_TELEMETRY)) { legacyOptInValue = oldTelemetrySetting; - } else if (typeof oldAllowReportSetting === 'boolean') { + } else if ( + typeof oldAllowReportSetting === 'boolean' && + uiSettings.isOverridden(CONFIG_ALLOW_REPORT) + ) { legacyOptInValue = oldAllowReportSetting; } diff --git a/src/plugins/telemetry/public/components/opt_in_message.tsx b/src/plugins/telemetry/public/components/opt_in_message.tsx index d36d5259fe27a..590a115b2bb6c 100644 --- a/src/plugins/telemetry/public/components/opt_in_message.tsx +++ b/src/plugins/telemetry/public/components/opt_in_message.tsx @@ -31,7 +31,7 @@ export class OptInMessage extends React.PureComponent { defaultMessage="Want to help us improve the Elastic Stack? Data usage collection is currently disabled. Enabling data usage collection helps us manage and improve our products and services. See our {privacyStatementLink} for more details." values={{ privacyStatementLink: ( - + { // @ts-ignore afterAll(() => (window.localStorage = originalLocalStorage)); - // const clusters = [{ cluster_uuid: 'fake-123' }, { cluster_uuid: 'fake-456' }]; - // const telemetryUrl = 'https://not.a.valid.url.0'; - // const mockFetchTelemetry = () => Promise.resolve({ data: clusters }); - // // returns a function that behaves like the injector by fetching the requested key from the object directly - // // for example: - // // { '$http': jest.fn() } would be how to mock the '$http' injector value - // const mockInjectorFromObject = object => { - // return { get: key => object[key] }; - // }; - describe('constructor', () => { it('defaults lastReport if unset', () => { const telemetryService = mockTelemetryService(); From b7ef41349081fa9683ffb8ea4eecc608eae272cf Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Fri, 31 Jan 2020 16:49:48 +0200 Subject: [PATCH 08/41] remove commented code --- src/plugins/telemetry/public/plugin.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index d61af80995e61..c25b03637f339 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -61,7 +61,6 @@ export class TelemetryPlugin implements Plugin { sendUsageFrom, }); - // const isStatusPage = chrome.getApp().id === 'status_page'; if (telemetryBanner) { this.maybeShowOptedInNotificationBanner({ telemetryNotifications, From ad2afa140397e89326a6e78566e0d68db1d4dde9 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Fri, 31 Jan 2020 18:00:46 +0200 Subject: [PATCH 09/41] bracket notication for private methods --- .../public/services/telemetry_service.test.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/plugins/telemetry/public/services/telemetry_service.test.ts b/src/plugins/telemetry/public/services/telemetry_service.test.ts index 87740b19720bc..fe34963bbb5c9 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.test.ts @@ -16,6 +16,8 @@ * specific language governing permissions and limitations * under the License. */ + +/* eslint-disable dot-notation */ import { mockTelemetryService } from '../telemetry.mock'; const mockSubtract = jest.fn().mockImplementation(() => { @@ -38,7 +40,7 @@ describe('TelemetryService', () => { it('calls expected URL with 20 minutes - now', async () => { const telemetryService = mockTelemetryService(); await telemetryService.fetchTelemetry(); - expect(telemetryService.http.post).toBeCalledWith('/api/telemetry/v2/clusters/_stats', { + expect(telemetryService['http'].post).toBeCalledWith('/api/telemetry/v2/clusters/_stats', { body: JSON.stringify({ unencrypted: false, timeRange: {} }), }); expect(mockSubtract).toBeCalledWith(20, 'minutes'); @@ -60,7 +62,7 @@ describe('TelemetryService', () => { telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); await telemetryService.setOptIn(true); - expect(telemetryService.http.post).toBeCalledTimes(1); + expect(telemetryService['http'].post).toBeCalledTimes(1); }); it('sends enabled true if optedIn: true', async () => { @@ -69,7 +71,7 @@ describe('TelemetryService', () => { const optedIn = true; await telemetryService.setOptIn(optedIn); - expect(telemetryService.http.post).toBeCalledWith('/api/telemetry/v2/optIn', { + expect(telemetryService['http'].post).toBeCalledWith('/api/telemetry/v2/optIn', { body: JSON.stringify({ enabled: optedIn }), }); }); @@ -80,7 +82,7 @@ describe('TelemetryService', () => { const optedIn = false; await telemetryService.setOptIn(optedIn); - expect(telemetryService.http.post).toBeCalledWith('/api/telemetry/v2/optIn', { + expect(telemetryService['http'].post).toBeCalledWith('/api/telemetry/v2/optIn', { body: JSON.stringify({ enabled: optedIn }), }); }); @@ -92,7 +94,7 @@ describe('TelemetryService', () => { await telemetryService.setOptIn(true); expect(telemetryService.reportOptInStatus).toBeCalledTimes(0); - expect(telemetryService.http.post).toBeCalledTimes(1); + expect(telemetryService['http'].post).toBeCalledTimes(1); }); it('calls reportOptInStatus if reportOptInStatusChange is true', async () => { @@ -102,21 +104,21 @@ describe('TelemetryService', () => { await telemetryService.setOptIn(true); expect(telemetryService.reportOptInStatus).toBeCalledTimes(1); - expect(telemetryService.http.post).toBeCalledTimes(1); + expect(telemetryService['http'].post).toBeCalledTimes(1); }); it('adds an error toast on api error', async () => { const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); telemetryService.reportOptInStatus = jest.fn(); - telemetryService.http.post = jest.fn().mockImplementation((url: string) => { + telemetryService['http'].post = jest.fn().mockImplementation((url: string) => { if (url === '/api/telemetry/v2/optIn') { throw Error('failed to update opt in.'); } }); await telemetryService.setOptIn(true); - expect(telemetryService.http.post).toBeCalledTimes(1); + expect(telemetryService['http'].post).toBeCalledTimes(1); expect(telemetryService.reportOptInStatus).toBeCalledTimes(0); expect(telemetryService.notifications.toasts.addError).toBeCalledTimes(1); }); @@ -129,7 +131,7 @@ describe('TelemetryService', () => { }); await telemetryService.setOptIn(true); - expect(telemetryService.http.post).toBeCalledTimes(1); + expect(telemetryService['http'].post).toBeCalledTimes(1); expect(telemetryService.reportOptInStatus).toBeCalledTimes(1); expect(telemetryService.notifications.toasts.addError).toBeCalledTimes(1); }); From 569c404e0a58a707cebac11aaf404d72f0895974 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Fri, 31 Jan 2020 18:02:48 +0200 Subject: [PATCH 10/41] bracket notication for private methods --- .../public/services/telemetry_sender.test.ts | 81 ++++++++++--------- .../public/services/telemetry_service.test.ts | 20 ++--- 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/src/plugins/telemetry/public/services/telemetry_sender.test.ts b/src/plugins/telemetry/public/services/telemetry_sender.test.ts index 0593b80b17648..9db4ef30d8eed 100644 --- a/src/plugins/telemetry/public/services/telemetry_sender.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_sender.test.ts @@ -17,6 +17,7 @@ * under the License. */ +/* eslint-disable dot-notation */ import { TelemetrySender } from './telemetry_sender'; import { mockTelemetryService } from '../telemetry.mock'; import { REPORT_INTERVAL_MS, LOCALSTORAGE_KEY } from '../../common/constants'; @@ -42,7 +43,7 @@ describe('TelemetrySender', () => { it('defaults lastReport if unset', () => { const telemetryService = mockTelemetryService(); const telemetrySender = new TelemetrySender(telemetryService); - expect(telemetrySender.lastReported).toBeUndefined(); + expect(telemetrySender['lastReported']).toBeUndefined(); expect(mockLocalStorage.getItem).toBeCalledTimes(1); expect(mockLocalStorage.getItem).toHaveBeenCalledWith(LOCALSTORAGE_KEY); }); @@ -52,7 +53,7 @@ describe('TelemetrySender', () => { mockLocalStorage.getItem.mockReturnValueOnce(JSON.stringify({ lastReport })); const telemetryService = mockTelemetryService(); const telemetrySender = new TelemetrySender(telemetryService); - expect(telemetrySender.lastReported).toBe(lastReport); + expect(telemetrySender['lastReported']).toBe(lastReport); }); }); @@ -61,8 +62,8 @@ describe('TelemetrySender', () => { const lastReport = `${Date.now()}`; const telemetryService = mockTelemetryService(); const telemetrySender = new TelemetrySender(telemetryService); - telemetrySender.lastReported = lastReport; - telemetrySender.saveToBrowser(); + telemetrySender['lastReported'] = lastReport; + telemetrySender['saveToBrowser'](); expect(mockLocalStorage.setItem).toHaveBeenCalledTimes(1); expect(mockLocalStorage.setItem).toHaveBeenCalledWith( @@ -77,7 +78,7 @@ describe('TelemetrySender', () => { const telemetryService = mockTelemetryService(); telemetryService.getIsOptedIn = jest.fn().mockReturnValue(false); const telemetrySender = new TelemetrySender(telemetryService); - const shouldSendRerpot = telemetrySender.shouldSendReport(); + const shouldSendRerpot = telemetrySender['shouldSendReport'](); expect(telemetryService.getIsOptedIn).toBeCalledTimes(1); expect(shouldSendRerpot).toBe(false); @@ -87,9 +88,9 @@ describe('TelemetrySender', () => { const telemetryService = mockTelemetryService(); telemetryService.getIsOptedIn = jest.fn().mockReturnValue(true); const telemetrySender = new TelemetrySender(telemetryService); - const shouldSendRerpot = telemetrySender.shouldSendReport(); + const shouldSendRerpot = telemetrySender['shouldSendReport'](); - expect(telemetrySender.lastReported).toBeUndefined(); + expect(telemetrySender['lastReported']).toBeUndefined(); expect(shouldSendRerpot).toBe(true); }); @@ -99,8 +100,8 @@ describe('TelemetrySender', () => { const telemetryService = mockTelemetryService(); telemetryService.getIsOptedIn = jest.fn().mockReturnValue(true); const telemetrySender = new TelemetrySender(telemetryService); - telemetrySender.lastReported = `${lastReported}`; - const shouldSendRerpot = telemetrySender.shouldSendReport(); + telemetrySender['lastReported'] = `${lastReported}`; + const shouldSendRerpot = telemetrySender['shouldSendReport'](); expect(shouldSendRerpot).toBe(true); }); @@ -110,8 +111,8 @@ describe('TelemetrySender', () => { const telemetryService = mockTelemetryService(); telemetryService.getIsOptedIn = jest.fn().mockReturnValue(true); const telemetrySender = new TelemetrySender(telemetryService); - telemetrySender.lastReported = `${lastReported}`; - const shouldSendRerpot = telemetrySender.shouldSendReport(); + telemetrySender['lastReported'] = `${lastReported}`; + const shouldSendRerpot = telemetrySender['shouldSendReport'](); expect(shouldSendRerpot).toBe(false); }); @@ -119,8 +120,8 @@ describe('TelemetrySender', () => { const telemetryService = mockTelemetryService(); telemetryService.getIsOptedIn = jest.fn().mockReturnValue(true); const telemetrySender = new TelemetrySender(telemetryService); - telemetrySender.lastReported = `random_malformed_string`; - const shouldSendRerpot = telemetrySender.shouldSendReport(); + telemetrySender['lastReported'] = `random_malformed_string`; + const shouldSendRerpot = telemetrySender['shouldSendReport'](); expect(shouldSendRerpot).toBe(true); }); @@ -140,22 +141,22 @@ describe('TelemetrySender', () => { it('does not send if already sending', async () => { const telemetryService = mockTelemetryService(); const telemetrySender = new TelemetrySender(telemetryService); - telemetrySender.shouldSendReport = jest.fn(); - telemetrySender.isSending = true; - await telemetrySender.sendIfDue(); + telemetrySender['shouldSendReport'] = jest.fn(); + telemetrySender['isSending'] = true; + await telemetrySender['sendIfDue'](); - expect(telemetrySender.shouldSendReport).toBeCalledTimes(0); + expect(telemetrySender['shouldSendReport']).toBeCalledTimes(0); expect(mockFetch).toBeCalledTimes(0); }); it('does not send if shouldSendReport returns false', async () => { const telemetryService = mockTelemetryService(); const telemetrySender = new TelemetrySender(telemetryService); - telemetrySender.shouldSendReport = jest.fn().mockReturnValue(false); - telemetrySender.isSending = false; - await telemetrySender.sendIfDue(); + telemetrySender['shouldSendReport'] = jest.fn().mockReturnValue(false); + telemetrySender['isSending'] = false; + await telemetrySender['sendIfDue'](); - expect(telemetrySender.shouldSendReport).toBeCalledTimes(1); + expect(telemetrySender['shouldSendReport']).toBeCalledTimes(1); expect(mockFetch).toBeCalledTimes(0); }); @@ -167,9 +168,9 @@ describe('TelemetrySender', () => { const telemetrySender = new TelemetrySender(telemetryService); telemetryService.getTelemetryUrl = jest.fn().mockReturnValue(mockTelemetryUrl); telemetryService.fetchTelemetry = jest.fn().mockReturnValue(mockTelemetryPayload); - telemetrySender.shouldSendReport = jest.fn().mockReturnValue(true); - telemetrySender.isSending = false; - await telemetrySender.sendIfDue(); + telemetrySender['shouldSendReport'] = jest.fn().mockReturnValue(true); + telemetrySender['isSending'] = false; + await telemetrySender['sendIfDue'](); expect(telemetryService.fetchTelemetry).toBeCalledTimes(1); expect(mockFetch).toBeCalledTimes(1); @@ -190,9 +191,9 @@ describe('TelemetrySender', () => { const telemetrySender = new TelemetrySender(telemetryService); telemetryService.getTelemetryUrl = jest.fn().mockReturnValue(mockTelemetryUrl); telemetryService.fetchTelemetry = jest.fn().mockReturnValue(mockTelemetryPayload); - telemetrySender.shouldSendReport = jest.fn().mockReturnValue(true); - telemetrySender.isSending = false; - await telemetrySender.sendIfDue(); + telemetrySender['shouldSendReport'] = jest.fn().mockReturnValue(true); + telemetrySender['isSending'] = false; + await telemetrySender['sendIfDue'](); expect(telemetryService.fetchTelemetry).toBeCalledTimes(1); expect(mockFetch).toBeCalledTimes(2); @@ -206,15 +207,15 @@ describe('TelemetrySender', () => { const telemetrySender = new TelemetrySender(telemetryService); telemetryService.getTelemetryUrl = jest.fn().mockReturnValue(mockTelemetryUrl); telemetryService.fetchTelemetry = jest.fn().mockReturnValue(mockTelemetryPayload); - telemetrySender.shouldSendReport = jest.fn().mockReturnValue(true); - telemetrySender.saveToBrowser = jest.fn(); + telemetrySender['shouldSendReport'] = jest.fn().mockReturnValue(true); + telemetrySender['saveToBrowser'] = jest.fn(); - await telemetrySender.sendIfDue(); + await telemetrySender['sendIfDue'](); expect(mockFetch).toBeCalledTimes(1); - expect(telemetrySender.lastReported).toBeDefined(); - expect(telemetrySender.saveToBrowser).toBeCalledTimes(1); - expect(telemetrySender.isSending).toBe(false); + expect(telemetrySender['lastReported']).toBeDefined(); + expect(telemetrySender['saveToBrowser']).toBeCalledTimes(1); + expect(telemetrySender['isSending']).toBe(false); }); it('catches fetchTelemetry errors and sets isSending to false', async () => { @@ -224,10 +225,10 @@ describe('TelemetrySender', () => { telemetryService.fetchTelemetry = jest.fn().mockImplementation(() => { throw Error('Error fetching usage'); }); - await telemetrySender.sendIfDue(); + await telemetrySender['sendIfDue'](); expect(telemetryService.fetchTelemetry).toBeCalledTimes(1); - expect(telemetrySender.lastReported).toBeUndefined(); - expect(telemetrySender.isSending).toBe(false); + expect(telemetrySender['lastReported']).toBeUndefined(); + expect(telemetrySender['isSending']).toBe(false); }); it('catches fetch errors and sets isSending to false', async () => { @@ -239,11 +240,11 @@ describe('TelemetrySender', () => { mockFetch.mockImplementation(() => { throw Error('Error sending usage'); }); - await telemetrySender.sendIfDue(); + await telemetrySender['sendIfDue'](); expect(telemetryService.fetchTelemetry).toBeCalledTimes(1); expect(mockFetch).toBeCalledTimes(2); - expect(telemetrySender.lastReported).toBeUndefined(); - expect(telemetrySender.isSending).toBe(false); + expect(telemetrySender['lastReported']).toBeUndefined(); + expect(telemetrySender['isSending']).toBe(false); }); }); }); @@ -265,7 +266,7 @@ describe('TelemetrySender', () => { const telemetrySender = new TelemetrySender(telemetryService); telemetrySender.startChecking(); expect(mockSetInterval).toBeCalledTimes(1); - expect(mockSetInterval).toBeCalledWith(telemetrySender.sendIfDue, 60000); + expect(mockSetInterval).toBeCalledWith(telemetrySender['sendIfDue'], 60000); }); }); }); diff --git a/src/plugins/telemetry/public/services/telemetry_service.test.ts b/src/plugins/telemetry/public/services/telemetry_service.test.ts index fe34963bbb5c9..c6f44e82a4a2e 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.test.ts @@ -90,27 +90,27 @@ describe('TelemetryService', () => { it('does not call reportOptInStatus if reportOptInStatusChange is false', async () => { const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); - telemetryService.reportOptInStatus = jest.fn(); + telemetryService['reportOptInStatus'] = jest.fn(); await telemetryService.setOptIn(true); - expect(telemetryService.reportOptInStatus).toBeCalledTimes(0); + expect(telemetryService['reportOptInStatus']).toBeCalledTimes(0); expect(telemetryService['http'].post).toBeCalledTimes(1); }); it('calls reportOptInStatus if reportOptInStatusChange is true', async () => { const telemetryService = mockTelemetryService({ reportOptInStatusChange: true }); telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); - telemetryService.reportOptInStatus = jest.fn(); + telemetryService['reportOptInStatus'] = jest.fn(); await telemetryService.setOptIn(true); - expect(telemetryService.reportOptInStatus).toBeCalledTimes(1); + expect(telemetryService['reportOptInStatus']).toBeCalledTimes(1); expect(telemetryService['http'].post).toBeCalledTimes(1); }); it('adds an error toast on api error', async () => { const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); - telemetryService.reportOptInStatus = jest.fn(); + telemetryService['reportOptInStatus'] = jest.fn(); telemetryService['http'].post = jest.fn().mockImplementation((url: string) => { if (url === '/api/telemetry/v2/optIn') { throw Error('failed to update opt in.'); @@ -119,21 +119,21 @@ describe('TelemetryService', () => { await telemetryService.setOptIn(true); expect(telemetryService['http'].post).toBeCalledTimes(1); - expect(telemetryService.reportOptInStatus).toBeCalledTimes(0); - expect(telemetryService.notifications.toasts.addError).toBeCalledTimes(1); + expect(telemetryService['reportOptInStatus']).toBeCalledTimes(0); + expect(telemetryService['notifications'].toasts.addError).toBeCalledTimes(1); }); it('adds an error toast on reportOptInStatus error', async () => { const telemetryService = mockTelemetryService({ reportOptInStatusChange: true }); telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); - telemetryService.reportOptInStatus = jest.fn().mockImplementation(() => { + telemetryService['reportOptInStatus'] = jest.fn().mockImplementation(() => { throw Error('failed to report OptIn Status.'); }); await telemetryService.setOptIn(true); expect(telemetryService['http'].post).toBeCalledTimes(1); - expect(telemetryService.reportOptInStatus).toBeCalledTimes(1); - expect(telemetryService.notifications.toasts.addError).toBeCalledTimes(1); + expect(telemetryService['reportOptInStatus']).toBeCalledTimes(1); + expect(telemetryService['notifications'].toasts.addError).toBeCalledTimes(1); }); }); }); From b393639f759a623fdc250d90bb7a67cba745acff Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Fri, 31 Jan 2020 18:21:08 +0200 Subject: [PATCH 11/41] update license management tests --- .../telemetry_opt_in.test.js.snap | 576 ------------------ .../upload_license.test.tsx.snap | 5 - .../__jest__/telemetry_opt_in.test.js | 43 -- 3 files changed, 624 deletions(-) delete mode 100644 x-pack/legacy/plugins/license_management/__jest__/__snapshots__/telemetry_opt_in.test.js.snap delete mode 100644 x-pack/legacy/plugins/license_management/__jest__/telemetry_opt_in.test.js diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/telemetry_opt_in.test.js.snap b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/telemetry_opt_in.test.js.snap deleted file mode 100644 index 575c47205f9c0..0000000000000 --- a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/telemetry_opt_in.test.js.snap +++ /dev/null @@ -1,576 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TelemetryOptIn should display when telemetry not opted in 1`] = ` - - -
- - -

- - Help Elastic support provide better service - -

-
- -
- - - - - - } - className="eui-AlignBaseline" - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - id="readMorePopover" - isOpen={false} - ownFocus={true} - panelPaddingSize="m" - > - -

- - - , - "telemetryPrivacyStatementLink": - - , - } - } - /> -

-
- , - } - } - /> - - } - onChange={[Function]} - > -
- -
- -
- - -`; - -exports[`TelemetryOptIn should not display when telemetry is opted in 1`] = ` - -`; - -exports[`TelemetryOptIn shouldn't display when telemetry optIn status can't change 1`] = ` - -`; diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap index 9256bee4e756b..40ed7a20f8d96 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap +++ b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap @@ -967,7 +967,6 @@ exports[`UploadLicense should display a modal when license requires acknowledgem className="euiSpacer euiSpacer--m" /> - @@ -1437,7 +1436,6 @@ exports[`UploadLicense should display an error when ES says license is expired 1 className="euiSpacer euiSpacer--m" /> - @@ -1907,7 +1905,6 @@ exports[`UploadLicense should display an error when ES says license is invalid 1 className="euiSpacer euiSpacer--m" /> - @@ -2372,7 +2369,6 @@ exports[`UploadLicense should display an error when submitting invalid JSON 1`] className="euiSpacer euiSpacer--m" /> - @@ -2842,7 +2838,6 @@ exports[`UploadLicense should display error when ES returns error 1`] = ` className="euiSpacer euiSpacer--m" /> - diff --git a/x-pack/legacy/plugins/license_management/__jest__/telemetry_opt_in.test.js b/x-pack/legacy/plugins/license_management/__jest__/telemetry_opt_in.test.js deleted file mode 100644 index 1b03ce869e52b..0000000000000 --- a/x-pack/legacy/plugins/license_management/__jest__/telemetry_opt_in.test.js +++ /dev/null @@ -1,43 +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 React from 'react'; -import { - setTelemetryEnabled, - setTelemetryOptInService, -} from '../public/np_ready/application/lib/telemetry'; -import { TelemetryOptIn } from '../public/np_ready/application/components/telemetry_opt_in'; -import { mountWithIntl } from '../../../../test_utils/enzyme_helpers'; - -jest.mock('ui/new_platform'); - -setTelemetryEnabled(true); - -describe('TelemetryOptIn', () => { - test('should display when telemetry not opted in', () => { - setTelemetryOptInService({ - getOptIn: () => false, - canChangeOptInStatus: () => true, - }); - const rendered = mountWithIntl(); - expect(rendered).toMatchSnapshot(); - }); - test('should not display when telemetry is opted in', () => { - setTelemetryOptInService({ - getOptIn: () => true, - canChangeOptInStatus: () => true, - }); - const rendered = mountWithIntl(); - expect(rendered).toMatchSnapshot(); - }); - test(`shouldn't display when telemetry optIn status can't change`, () => { - setTelemetryOptInService({ - getOptIn: () => false, - canChangeOptInStatus: () => false, - }); - const rendered = mountWithIntl(); - expect(rendered).toMatchSnapshot(); - }); -}); From e0f417f3672d6163c126a795fca1f9f7ce4e4721 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Fri, 31 Jan 2020 18:55:09 +0200 Subject: [PATCH 12/41] afharo code review fixes --- .../application/sections/license_dashboard/license_dashboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js index 6b8709c8d532a..57b5663f002b6 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js @@ -27,7 +27,7 @@ export const LicenseDashboard = ({ setBreadcrumb } = { setBreadcrumb: () => {} } - + From 75b4b290f2f9a81dfa97cfdbd68a105e01188ef6 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Fri, 31 Jan 2020 19:04:04 +0200 Subject: [PATCH 13/41] type safe private method access in tests --- .../telemetry_notifications.test.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.test.ts b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.test.ts index 78639481e9af0..9464dbf49c3ac 100644 --- a/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.test.ts @@ -17,6 +17,7 @@ * under the License. */ +/* eslint-disable dot-notation */ import { mockTelemetryNotifications, mockTelemetryService } from '../../telemetry.mock'; describe('onSetOptInClick', () => { @@ -27,11 +28,11 @@ describe('onSetOptInClick', () => { const telemetryService = mockTelemetryService(); telemetryService.setOptIn = jest.fn(); const telemetryNotifications = mockTelemetryNotifications({ telemetryService }); - telemetryNotifications.optInBannerId = bannerId; + telemetryNotifications['optInBannerId'] = bannerId; - await telemetryNotifications.onSetOptInClick(optIn); - expect(telemetryNotifications.overlays.banners.remove).toBeCalledTimes(1); - expect(telemetryNotifications.overlays.banners.remove).toBeCalledWith(bannerId); + await telemetryNotifications['onSetOptInClick'](optIn); + expect(telemetryNotifications['overlays'].banners.remove).toBeCalledTimes(1); + expect(telemetryNotifications['overlays'].banners.remove).toBeCalledWith(bannerId); expect(telemetryService.setOptIn).toBeCalledTimes(1); expect(telemetryService.setOptIn).toBeCalledWith(optIn); }); @@ -44,11 +45,11 @@ describe('setOptedInNoticeSeen', () => { const telemetryService = mockTelemetryService(); telemetryService.setUserHasSeenNotice = jest.fn(); const telemetryNotifications = mockTelemetryNotifications({ telemetryService }); - telemetryNotifications.optedInNoticeBannerId = bannerId; + telemetryNotifications['optedInNoticeBannerId'] = bannerId; await telemetryNotifications.setOptedInNoticeSeen(); - expect(telemetryNotifications.overlays.banners.remove).toBeCalledTimes(1); - expect(telemetryNotifications.overlays.banners.remove).toBeCalledWith(bannerId); + expect(telemetryNotifications['overlays'].banners.remove).toBeCalledTimes(1); + expect(telemetryNotifications['overlays'].banners.remove).toBeCalledWith(bannerId); expect(telemetryService.setUserHasSeenNotice).toBeCalledTimes(1); }); }); From 5bdb6a381e3e8e6badf25d3bfcacd923ee78c474 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Fri, 31 Jan 2020 19:06:53 +0200 Subject: [PATCH 14/41] fix typecheck --- .../telemetry_management_section.tsx | 20 ++++++++++--------- .../start_trial/start_trial.tsx | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/plugins/telemetry/public/components/telemetry_management_section.tsx b/src/plugins/telemetry/public/components/telemetry_management_section.tsx index ac9a42afd5497..cd0e5d7a3644f 100644 --- a/src/plugins/telemetry/public/components/telemetry_management_section.tsx +++ b/src/plugins/telemetry/public/components/telemetry_management_section.tsx @@ -110,15 +110,17 @@ export class TelemetryManagementSection extends Component { {this.maybeGetAppliesSettingMessage()} Date: Fri, 31 Jan 2020 20:21:10 +0200 Subject: [PATCH 15/41] more type check fixes --- src/legacy/core_plugins/kibana/public/home/index.ts | 2 +- src/legacy/core_plugins/telemetry/index.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/home/index.ts b/src/legacy/core_plugins/kibana/public/home/index.ts index 2798ee20c5e89..c1915dd9129c1 100644 --- a/src/legacy/core_plugins/kibana/public/home/index.ts +++ b/src/legacy/core_plugins/kibana/public/home/index.ts @@ -47,5 +47,5 @@ let copiedLegacyCatalogue = false; }); instance.start(npStart.core, { ...npStart.plugins, - }); + } as any); })(); diff --git a/src/legacy/core_plugins/telemetry/index.ts b/src/legacy/core_plugins/telemetry/index.ts index 94a3d858eaf10..1157c8994f446 100644 --- a/src/legacy/core_plugins/telemetry/index.ts +++ b/src/legacy/core_plugins/telemetry/index.ts @@ -22,12 +22,10 @@ import { resolve } from 'path'; import JoiNamespace from 'joi'; import { Server } from 'hapi'; import { CoreSetup, PluginInitializerContext } from 'src/core/server'; -import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { getConfigPath } from '../../../core/server/path'; // @ts-ignore import mappings from './mappings.json'; -import { CONFIG_TELEMETRY, getConfigTelemetryDesc } from './common/constants'; import { getXpackConfigWithDeprecated } from './common/get_xpack_config_with_deprecated'; import { telemetryPlugin, From becd967b64a6ea878aef184789a38611928a1c76 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Fri, 31 Jan 2020 21:56:32 +0200 Subject: [PATCH 16/41] i18n check --- .i18nrc.json | 5 ++++- x-pack/plugins/translations/translations/ja-JP.json | 6 +----- x-pack/plugins/translations/translations/zh-CN.json | 6 +----- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.i18nrc.json b/.i18nrc.json index 1230151212f57..bac8c73e97e43 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -31,7 +31,10 @@ "regionMap": "src/legacy/core_plugins/region_map", "server": "src/legacy/server", "statusPage": "src/legacy/core_plugins/status_page", - "telemetry": "src/legacy/core_plugins/telemetry", + "telemetry": [ + "src/legacy/core_plugins/telemetry", + "src/plugins/telemetry" + ], "tileMap": "src/legacy/core_plugins/tile_map", "timelion": ["src/legacy/core_plugins/timelion", "src/legacy/core_plugins/vis_type_timelion", "src/plugins/timelion"], "uiActions": "src/plugins/ui_actions", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 47e11817ffa5d..5c2e952fc5e96 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2515,10 +2515,6 @@ "telemetry.seeExampleOfWhatWeCollectLinkText": "収集されるデータの例を見る", "telemetry.telemetryBannerDescription": "Elastic Stackの改善にご協力ください使用状況データの収集は現在無効です。使用状況データの収集を有効にすると、製品とサービスを管理して改善することができます。詳細については、{privacyStatementLink}をご覧ください。", "telemetry.telemetryConfigDescription": "基本的な機能の利用状況に関する統計情報を提供して、Elastic Stack の改善にご協力ください。このデータは Elastic 社外と共有されません。", - "telemetry.telemetryConfigTitle": "遠隔測定オプトイン", - "telemetry.telemetryErrorNotificationMessageDescription.tryAgainText": "Kibana と Elasticsearch が現在も実行中であることを確認し、再試行してください。", - "telemetry.telemetryErrorNotificationMessageDescription.unableToSaveTelemetryPreferenceText": "遠隔測定設定を保存できません。", - "telemetry.telemetryErrorNotificationMessageTitle": "遠隔測定エラー", "telemetry.telemetryOptedInDisableUsage": "ここで使用状況データを無効にする", "telemetry.telemetryOptedInDismissMessage": "閉じる", "telemetry.telemetryOptedInNoticeDescription": "使用状況データがどのように製品とサービスの管理と改善につながるのかに関する詳細については、{privacyStatementLink}をご覧ください。収集を停止するには、{disableLink}。", @@ -13201,4 +13197,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。", "xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。" } -} +} \ No newline at end of file diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 86d9a69dc0900..55f18512213cd 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2515,10 +2515,6 @@ "telemetry.seeExampleOfWhatWeCollectLinkText": "查看我们收集的内容示例", "telemetry.telemetryBannerDescription": "想帮助我们改进 Elastic Stack?数据使用情况收集当前已禁用。启用数据使用情况收集可帮助我们管理并改善产品和服务。有关详情,请参阅我们的{privacyStatementLink}。", "telemetry.telemetryConfigDescription": "通过提供基本功能的使用情况统计信息,来帮助我们改进 Elastic Stack。我们不会在 Elastic 之外共享此数据。", - "telemetry.telemetryConfigTitle": "遥测选择加入", - "telemetry.telemetryErrorNotificationMessageDescription.tryAgainText": "确认 Kibana 和 Elasticsearch 仍在运行,然后重试。", - "telemetry.telemetryErrorNotificationMessageDescription.unableToSaveTelemetryPreferenceText": "无法保存遥测首选项。", - "telemetry.telemetryErrorNotificationMessageTitle": "遥测错误", "telemetry.telemetryOptedInDisableUsage": "请在此禁用使用情况数据", "telemetry.telemetryOptedInDismissMessage": "关闭", "telemetry.telemetryOptedInNoticeDescription": "要了解使用情况数据如何帮助我们管理和改善产品和服务,请参阅我们的{privacyStatementLink}。要停止收集,{disableLink}。", @@ -13200,4 +13196,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} +} \ No newline at end of file From 93cbe84eccd6af59c388c88bc533f8ae89029386 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 3 Feb 2020 09:52:05 +0200 Subject: [PATCH 17/41] fix welcome page tests --- .../__snapshots__/welcome.test.tsx.snap | 46 ++----------------- .../home/np_ready/components/welcome.test.tsx | 25 ++++++---- 2 files changed, 20 insertions(+), 51 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/welcome.test.tsx.snap b/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/welcome.test.tsx.snap index 6f76ceecbba13..df7cc7bcbaed0 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/welcome.test.tsx.snap +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/welcome.test.tsx.snap @@ -67,44 +67,6 @@ exports[`should render a Welcome screen with no telemetry disclaimer 1`] = ` - - - - - - - - - - -
@@ -200,16 +162,16 @@ exports[`should render a Welcome screen with the telemetry disclaimer 1`] = ` /> diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx index 55c469fa58fc6..11aac772d0963 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { Welcome } from './welcome'; - +import { telemetryPluginMock } from '../../../../../../../plugins/telemetry/public/telemetry.mock'; jest.mock('../../kibana_services', () => ({ getServices: () => ({ addBasePath: (path: string) => `root${path}`, @@ -29,27 +29,32 @@ jest.mock('../../kibana_services', () => ({ })); test('should render a Welcome screen with the telemetry disclaimer', () => { + const telemetry = telemetryPluginMock.createSetupContract(); const component = shallow( // @ts-ignore - {}} onOptInSeen={() => {}} /> + {}} telemetry={telemetry} /> ); expect(component).toMatchSnapshot(); }); test('should render a Welcome screen with the telemetry disclaimer when optIn is true', () => { + const telemetry = telemetryPluginMock.createSetupContract(); + telemetry.telemetryService.getIsOptedIn = jest.fn().mockReturnValue(true); const component = shallow( // @ts-ignore - {}} onOptInSeen={() => {}} currentOptInStatus={true} /> + {}} telemetry={telemetry} /> ); expect(component).toMatchSnapshot(); }); test('should render a Welcome screen with the telemetry disclaimer when optIn is false', () => { + const telemetry = telemetryPluginMock.createSetupContract(); + telemetry.telemetryService.getIsOptedIn = jest.fn().mockReturnValue(false); const component = shallow( // @ts-ignore - {}} onOptInSeen={() => {}} currentOptInStatus={false} /> + {}} telemetry={telemetry} /> ); expect(component).toMatchSnapshot(); @@ -59,19 +64,21 @@ test('should render a Welcome screen with no telemetry disclaimer', () => { // @ts-ignore const component = shallow( // @ts-ignore - {}} onOptInSeen={() => {}} /> + {}} telemetry={null} /> ); expect(component).toMatchSnapshot(); }); test('fires opt-in seen when mounted', () => { - const seen = jest.fn(); - + const telemetry = telemetryPluginMock.createSetupContract(); + const mockSetOptedInNoticeSeen = jest.fn(); + // @ts-ignore + telemetry.telemetryNotifications.setOptedInNoticeSeen = mockSetOptedInNoticeSeen; shallow( // @ts-ignore - {}} onOptInSeen={seen} /> + {}} telemetry={telemetry} /> ); - expect(seen).toHaveBeenCalled(); + expect(mockSetOptedInNoticeSeen).toHaveBeenCalled(); }); From 91e188231de145204acabc15ee0a3ac72c10289e Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 3 Feb 2020 10:06:21 +0200 Subject: [PATCH 18/41] i18n optedInNoticeBanner title --- .../telemetry/public/components/opted_in_notice_banner.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx b/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx index 21ac7ff600c53..090893964c881 100644 --- a/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx +++ b/src/plugins/telemetry/public/components/opted_in_notice_banner.tsx @@ -22,6 +22,7 @@ import * as React from 'react'; import { EuiButton, EuiLink, EuiCallOut, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { PATH_TO_ADVANCED_SETTINGS, PRIVACY_STATEMENT_URL } from '../../common/constants'; interface Props { @@ -31,9 +32,12 @@ interface Props { export class OptedInNoticeBanner extends React.PureComponent { render() { const { onSeenBanner } = this.props; + const bannerTitle = i18n.translate('telemetry.telemetryOptedInNoticeTitle', { + defaultMessage: 'Help us improve the Elastic Stack', + }); return ( - + Date: Mon, 3 Feb 2020 10:23:58 +0200 Subject: [PATCH 19/41] fix advanced settings field settings --- .../public/components/telemetry_management_section.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/telemetry/public/components/telemetry_management_section.tsx b/src/plugins/telemetry/public/components/telemetry_management_section.tsx index cd0e5d7a3644f..5fb255c3e142c 100644 --- a/src/plugins/telemetry/public/components/telemetry_management_section.tsx +++ b/src/plugins/telemetry/public/components/telemetry_management_section.tsx @@ -113,10 +113,16 @@ export class TelemetryManagementSection extends Component { setting={ { type: 'boolean', + name: i18n.translate('telemetry.provideUsageStatisticsTitle', { + defaultMessage: 'telemetry:enabled', + }), + displayName: i18n.translate('telemetry.provideUsageStatisticsTitle', { + defaultMessage: 'Provide usage statistics', + }), value: telemetryService.getIsOptedIn(), description: this.renderDescription(), defVal: true, - ariaName: i18n.translate('telemetry.provideUsageStatisticsLabel', { + ariaName: i18n.translate('telemetry.provideUsageStatisticsAriaName', { defaultMessage: 'Provide usage statistics', }), } as any From f0a1c470563c445f5d3413e95a79c4fccbd0f4e9 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 3 Feb 2020 11:15:23 +0200 Subject: [PATCH 20/41] field name --- .../public/components/telemetry_management_section.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/telemetry/public/components/telemetry_management_section.tsx b/src/plugins/telemetry/public/components/telemetry_management_section.tsx index 5fb255c3e142c..e225e8347f07b 100644 --- a/src/plugins/telemetry/public/components/telemetry_management_section.tsx +++ b/src/plugins/telemetry/public/components/telemetry_management_section.tsx @@ -113,9 +113,7 @@ export class TelemetryManagementSection extends Component { setting={ { type: 'boolean', - name: i18n.translate('telemetry.provideUsageStatisticsTitle', { - defaultMessage: 'telemetry:enabled', - }), + name: 'telemetry:enabled', displayName: i18n.translate('telemetry.provideUsageStatisticsTitle', { defaultMessage: 'Provide usage statistics', }), From d0f4da052dadfdcda2374724affc079a5fc3d3a0 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 3 Feb 2020 11:33:34 +0200 Subject: [PATCH 21/41] fix home snapshots --- .../home/np_ready/components/__snapshots__/home.test.js.snap | 1 - .../public/components/__snapshots__/opt_in_message.test.tsx.snap | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/home.test.js.snap b/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/home.test.js.snap index 4563b633c3dfc..9d27362e62739 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/home.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/home.test.js.snap @@ -1054,7 +1054,6 @@ exports[`home welcome should show the normal home page if welcome screen is disa exports[`home welcome should show the welcome screen if enabled, and there are no index patterns defined 1`] = ` diff --git a/src/plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap b/src/plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap index c80485332fa8a..7fa69a7409c6a 100644 --- a/src/plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap +++ b/src/plugins/telemetry/public/components/__snapshots__/opt_in_message.test.tsx.snap @@ -9,6 +9,7 @@ exports[`OptInMessage renders as expected 1`] = ` Object { "privacyStatementLink": Date: Tue, 4 Feb 2020 14:54:39 +0200 Subject: [PATCH 22/41] listen to app id change --- src/core/public/application/types.ts | 6 ++- src/core/public/plugins/plugin_context.ts | 1 + src/plugins/telemetry/public/plugin.ts | 56 ++++++++++------------- 3 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index 17fdfc627187e..b2f5adce6c15c 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -542,6 +542,8 @@ export interface ApplicationSetup { contextName: T, provider: IContextProvider ): void; + + currentAppId$: Observable; } /** @internal */ @@ -612,6 +614,8 @@ export interface ApplicationStart { contextName: T, provider: IContextProvider ): void; + + currentAppId$: Observable; } /** @internal */ @@ -639,8 +643,8 @@ export interface InternalApplicationStart provider: IContextProvider ): void; - // Internal APIs currentAppId$: Observable; + // Internal APIs getComponent(): JSX.Element | null; } diff --git a/src/core/public/plugins/plugin_context.ts b/src/core/public/plugins/plugin_context.ts index 48100cba4f26e..19cfadf70be1b 100644 --- a/src/core/public/plugins/plugin_context.ts +++ b/src/core/public/plugins/plugin_context.ts @@ -134,6 +134,7 @@ export function createPluginStartContext< ): CoreStart { return { application: { + currentAppId$: deps.application.currentAppId$, capabilities: deps.application.capabilities, navigateToApp: deps.application.navigateToApp, getUrlForApp: deps.application.getUrlForApp, diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index c25b03637f339..526ce03bf1ad2 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { Plugin, CoreSetup, CoreStart, HttpSetup } from '../../../core/public'; +import { Plugin, CoreStart, HttpStart } from '../../../core/public'; import { TelemetrySender, TelemetryService, TelemetryNotifications } from './services'; @@ -26,17 +26,14 @@ export interface TelemetryPluginStart { } export class TelemetryPlugin implements Plugin { - private isUnauthenticated: boolean = true; - - public setup({ http }: CoreSetup) { - this.isUnauthenticated = this.getIsUnauthenticated(http); - } + public setup() {} public start({ injectedMetadata, http, notifications, overlays, + application, }: CoreStart): TelemetryPluginStart { const isPluginEnabled = injectedMetadata.getInjectedVar('telemetryEnabled') as boolean; const telemetryBanner = injectedMetadata.getInjectedVar('telemetryBanner') as boolean; @@ -55,20 +52,26 @@ export class TelemetryPlugin implements Plugin { telemetryService, }); - this.maybeStartTelemetryPoller({ - telemetryService, - isPluginEnabled, - sendUsageFrom, - }); - - if (telemetryBanner) { - this.maybeShowOptedInNotificationBanner({ - telemetryNotifications, - }); - this.maybeShowOptInBanner({ - telemetryNotifications, + application.currentAppId$.subscribe(appId => { + const isUnauthenticated = this.getIsUnauthenticated(http); + if (isUnauthenticated) { + return; + } + this.maybeStartTelemetryPoller({ + telemetryService, + isPluginEnabled, + sendUsageFrom, }); - } + + if (telemetryBanner) { + this.maybeShowOptedInNotificationBanner({ + telemetryNotifications, + }); + this.maybeShowOptInBanner({ + telemetryNotifications, + }); + } + }); return { telemetryService, @@ -76,7 +79,7 @@ export class TelemetryPlugin implements Plugin { }; } - private getIsUnauthenticated(http: HttpSetup) { + private getIsUnauthenticated(http: HttpStart) { const { anonymousPaths } = http; return anonymousPaths.isAnonymous(window.location.pathname); } @@ -92,10 +95,7 @@ export class TelemetryPlugin implements Plugin { }) { if (isPluginEnabled && sendUsageFrom === 'browser') { const sender = new TelemetrySender(telemetryService); - - if (!this.isUnauthenticated) { - sender.startChecking(); - } + sender.startChecking(); } } @@ -104,10 +104,6 @@ export class TelemetryPlugin implements Plugin { }: { telemetryNotifications: TelemetryNotifications; }) { - if (this.isUnauthenticated) { - return; - } - const shouldShowBanner = telemetryNotifications.shouldShowOptedInNoticeBanner(); if (shouldShowBanner) { telemetryNotifications.renderOptedInNoticeBanner(); @@ -119,10 +115,6 @@ export class TelemetryPlugin implements Plugin { }: { telemetryNotifications: TelemetryNotifications; }) { - if (this.isUnauthenticated) { - return; - } - const shouldShowBanner = telemetryNotifications.shouldShowOptInBanner(); if (shouldShowBanner) { telemetryNotifications.renderOptInBanner(); From aac057ec5c764ba647c090d5a7ee634c65ccc100 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Tue, 4 Feb 2020 19:03:39 +0200 Subject: [PATCH 23/41] NP code review fixes --- .../public/application/application_service.mock.ts | 1 + src/core/public/application/types.ts | 11 +++++++---- src/core/public/legacy/legacy_service.ts | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/public/application/application_service.mock.ts b/src/core/public/application/application_service.mock.ts index dee47315fc322..02a1d6b4f8748 100644 --- a/src/core/public/application/application_service.mock.ts +++ b/src/core/public/application/application_service.mock.ts @@ -44,6 +44,7 @@ const createInternalSetupContractMock = (): jest.Mocked => ({ + currentAppId$: currentAppId$.asObservable(), capabilities: capabilitiesServiceMock.createStartContract().capabilities, navigateToApp: jest.fn(), getUrlForApp: jest.fn(), diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index b2f5adce6c15c..493afd1fec9db 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -542,8 +542,6 @@ export interface ApplicationSetup { contextName: T, provider: IContextProvider ): void; - - currentAppId$: Observable; } /** @internal */ @@ -615,12 +613,18 @@ export interface ApplicationStart { provider: IContextProvider ): void; + /** + * An observable that emits the current application id and each subsequent id update. + */ currentAppId$: Observable; } /** @internal */ export interface InternalApplicationStart - extends Pick { + extends Pick< + ApplicationStart, + 'capabilities' | 'navigateToApp' | 'getUrlForApp' | 'currentAppId$' + > { /** * Apps available based on the current capabilities. * Should be used to show navigation links and make routing decisions. @@ -643,7 +647,6 @@ export interface InternalApplicationStart provider: IContextProvider ): void; - currentAppId$: Observable; // Internal APIs getComponent(): JSX.Element | null; } diff --git a/src/core/public/legacy/legacy_service.ts b/src/core/public/legacy/legacy_service.ts index e4788e686dd45..1b7e25f585566 100644 --- a/src/core/public/legacy/legacy_service.ts +++ b/src/core/public/legacy/legacy_service.ts @@ -121,6 +121,7 @@ export class LegacyPlatformService { const legacyCore: LegacyCoreStart = { ...core, application: { + currentAppId$: core.application.currentAppId$, capabilities: core.application.capabilities, getUrlForApp: core.application.getUrlForApp, navigateToApp: core.application.navigateToApp, From e1d95e0a64511ba491dc97ade388fe30fd6a186a Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Tue, 4 Feb 2020 19:38:53 +0200 Subject: [PATCH 24/41] NP code review fixes --- ...-plugin-public.applicationstart.currentappid_.md | 13 +++++++++++++ .../public/kibana-plugin-public.applicationstart.md | 1 + src/core/public/public.api.md | 1 + src/plugins/telemetry/public/plugin.ts | 13 ++++++++----- .../telemetry_notifications.ts | 8 ++++++-- .../telemetry/public/services/telemetry_sender.ts | 5 ++++- 6 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 docs/development/core/public/kibana-plugin-public.applicationstart.currentappid_.md diff --git a/docs/development/core/public/kibana-plugin-public.applicationstart.currentappid_.md b/docs/development/core/public/kibana-plugin-public.applicationstart.currentappid_.md new file mode 100644 index 0000000000000..d3ceeabcd81f4 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.applicationstart.currentappid_.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ApplicationStart](./kibana-plugin-public.applicationstart.md) > [currentAppId$](./kibana-plugin-public.applicationstart.currentappid_.md) + +## ApplicationStart.currentAppId$ property + +An observable that emits the current application id and each subsequent id update. + +Signature: + +```typescript +currentAppId$: Observable; +``` diff --git a/docs/development/core/public/kibana-plugin-public.applicationstart.md b/docs/development/core/public/kibana-plugin-public.applicationstart.md index 3ad7e3b1656d8..433ce87419ae8 100644 --- a/docs/development/core/public/kibana-plugin-public.applicationstart.md +++ b/docs/development/core/public/kibana-plugin-public.applicationstart.md @@ -16,6 +16,7 @@ export interface ApplicationStart | Property | Type | Description | | --- | --- | --- | | [capabilities](./kibana-plugin-public.applicationstart.capabilities.md) | RecursiveReadonly<Capabilities> | Gets the read-only capabilities. | +| [currentAppId$](./kibana-plugin-public.applicationstart.currentappid_.md) | Observable<string | undefined> | An observable that emits the current application id and each subsequent id update. | ## Methods diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 91b4e9be58973..1e98935de4d04 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -97,6 +97,7 @@ export interface ApplicationSetup { // @public (undocumented) export interface ApplicationStart { capabilities: RecursiveReadonly; + currentAppId$: Observable; getUrlForApp(appId: string, options?: { path?: string; }): string; diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index 526ce03bf1ad2..c15c40176e803 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -52,13 +52,16 @@ export class TelemetryPlugin implements Plugin { telemetryService, }); + const telemetrySender = new TelemetrySender(telemetryService); + application.currentAppId$.subscribe(appId => { const isUnauthenticated = this.getIsUnauthenticated(http); if (isUnauthenticated) { return; } + this.maybeStartTelemetryPoller({ - telemetryService, + telemetrySender, isPluginEnabled, sendUsageFrom, }); @@ -67,6 +70,7 @@ export class TelemetryPlugin implements Plugin { this.maybeShowOptedInNotificationBanner({ telemetryNotifications, }); + this.maybeShowOptInBanner({ telemetryNotifications, }); @@ -85,17 +89,16 @@ export class TelemetryPlugin implements Plugin { } private maybeStartTelemetryPoller({ - telemetryService, + telemetrySender, isPluginEnabled, sendUsageFrom, }: { - telemetryService: TelemetryService; + telemetrySender: TelemetrySender; isPluginEnabled: boolean; sendUsageFrom: string; }) { if (isPluginEnabled && sendUsageFrom === 'browser') { - const sender = new TelemetrySender(telemetryService); - sender.startChecking(); + telemetrySender.startChecking(); } } diff --git a/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts index f6906a0c9dd16..bf25bb592db82 100644 --- a/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts +++ b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.ts @@ -40,7 +40,8 @@ export class TelemetryNotifications { public shouldShowOptedInNoticeBanner = (): boolean => { const userHasSeenOptedInNotice = this.telemetryService.getUserHasSeenOptedInNotice(); - return userHasSeenOptedInNotice; + const bannerOnScreen = typeof this.optedInNoticeBannerId !== 'undefined'; + return !bannerOnScreen && userHasSeenOptedInNotice; }; public renderOptedInNoticeBanner = (): void => { @@ -54,7 +55,8 @@ export class TelemetryNotifications { public shouldShowOptInBanner = (): boolean => { const isOptedIn = this.telemetryService.getIsOptedIn(); - return isOptedIn === null; + const bannerOnScreen = typeof this.optInBannerId !== 'undefined'; + return !bannerOnScreen && isOptedIn === null; }; public renderOptInBanner = (): void => { @@ -69,6 +71,7 @@ export class TelemetryNotifications { private onSetOptInClick = async (isOptIn: boolean) => { if (this.optInBannerId) { this.overlays.banners.remove(this.optInBannerId); + this.optInBannerId = undefined; } await this.telemetryService.setOptIn(isOptIn); @@ -77,6 +80,7 @@ export class TelemetryNotifications { public setOptedInNoticeSeen = async (): Promise => { if (this.optedInNoticeBannerId) { this.overlays.banners.remove(this.optedInNoticeBannerId); + this.optedInNoticeBannerId = undefined; } await this.telemetryService.setUserHasSeenNotice(); diff --git a/src/plugins/telemetry/public/services/telemetry_sender.ts b/src/plugins/telemetry/public/services/telemetry_sender.ts index b0fb947f510bb..fec2db0506eb7 100644 --- a/src/plugins/telemetry/public/services/telemetry_sender.ts +++ b/src/plugins/telemetry/public/services/telemetry_sender.ts @@ -26,6 +26,7 @@ export class TelemetrySender { private isSending: boolean = false; private lastReported?: string; private readonly storage: Storage; + private intervalId?: number; constructor(telemetryService: TelemetryService) { this.telemetryService = telemetryService; @@ -92,6 +93,8 @@ export class TelemetrySender { }; public startChecking = () => { - return setInterval(this.sendIfDue, 60000); + if (typeof this.intervalId === 'undefined') { + this.intervalId = window.setInterval(this.sendIfDue, 60000); + } }; } From e96882575d911ea9a2a2d4a5e0d913dc0b98b4a7 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Tue, 4 Feb 2020 20:01:56 +0200 Subject: [PATCH 25/41] update telemetry configs with np deprecations --- .../config/deprecation/core_deprecations.ts | 3 ++ .../get_xpack_config_with_deprecated.ts | 41 ------------------- src/legacy/core_plugins/telemetry/index.ts | 7 ++-- .../usage/telemetry_usage_collector.ts | 3 +- .../core_plugins/telemetry/server/fetcher.ts | 3 +- src/plugins/telemetry/public/plugin.ts | 6 +-- x-pack/legacy/plugins/xpack_main/index.js | 18 -------- 7 files changed, 9 insertions(+), 72 deletions(-) delete mode 100644 src/legacy/core_plugins/telemetry/common/get_xpack_config_with_deprecated.ts diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts index 3aa7f9e2aa8ad..4fa51dcd5a082 100644 --- a/src/core/server/config/deprecation/core_deprecations.ts +++ b/src/core/server/config/deprecation/core_deprecations.ts @@ -115,6 +115,9 @@ export const coreDeprecationProvider: ConfigDeprecationProvider = ({ renameFromRoot('optimize.lazyHost', 'optimize.watchHost'), renameFromRoot('optimize.lazyPrebuild', 'optimize.watchPrebuild'), renameFromRoot('optimize.lazyProxyTimeout', 'optimize.watchProxyTimeout'), + renameFromRoot('xpack.xpack_main.telemetry.config', 'telemetry.config'), + renameFromRoot('xpack.xpack_main.telemetry.url', 'telemetry.url'), + renameFromRoot('xpack.xpack_main.telemetry.enabled', 'telemetry.enabled'), renameFromRoot('xpack.telemetry.enabled', 'telemetry.enabled'), renameFromRoot('xpack.telemetry.config', 'telemetry.config'), renameFromRoot('xpack.telemetry.banner', 'telemetry.banner'), diff --git a/src/legacy/core_plugins/telemetry/common/get_xpack_config_with_deprecated.ts b/src/legacy/core_plugins/telemetry/common/get_xpack_config_with_deprecated.ts deleted file mode 100644 index 3f7a8d3410993..0000000000000 --- a/src/legacy/core_plugins/telemetry/common/get_xpack_config_with_deprecated.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 { KibanaConfig } from 'src/legacy/server/kbn_server'; - -export function getXpackConfigWithDeprecated(config: KibanaConfig, configPath: string) { - try { - const deprecatedXpackmainConfig = config.get(`xpack.xpack_main.${configPath}`); - if (typeof deprecatedXpackmainConfig !== 'undefined') { - return deprecatedXpackmainConfig; - } - } catch (err) { - // swallow error - } - try { - const deprecatedXpackConfig = config.get(`xpack.${configPath}`); - if (typeof deprecatedXpackConfig !== 'undefined') { - return deprecatedXpackConfig; - } - } catch (err) { - // swallow error - } - - return config.get(configPath); -} diff --git a/src/legacy/core_plugins/telemetry/index.ts b/src/legacy/core_plugins/telemetry/index.ts index 1157c8994f446..ec70380d83a0a 100644 --- a/src/legacy/core_plugins/telemetry/index.ts +++ b/src/legacy/core_plugins/telemetry/index.ts @@ -26,7 +26,6 @@ import { CoreSetup, PluginInitializerContext } from 'src/core/server'; import { getConfigPath } from '../../../core/server/path'; // @ts-ignore import mappings from './mappings.json'; -import { getXpackConfigWithDeprecated } from './common/get_xpack_config_with_deprecated'; import { telemetryPlugin, replaceTelemetryInjectedVars, @@ -92,11 +91,11 @@ const telemetry = (kibana: any) => { injectDefaultVars(server: Server) { const config = server.config(); return { - telemetryEnabled: getXpackConfigWithDeprecated(config, 'telemetry.enabled'), - telemetryUrl: getXpackConfigWithDeprecated(config, 'telemetry.url'), + telemetryEnabled: config.get('telemetry.enabled'), + telemetryUrl: config.get('telemetry.url'), telemetryBanner: config.get('telemetry.allowChangingOptInStatus') !== false && - getXpackConfigWithDeprecated(config, 'telemetry.banner'), + config.get('telemetry.banner'), telemetryOptedIn: config.get('telemetry.optIn'), telemetryOptInStatusUrl: config.get('telemetry.optInStatusUrl'), allowChangingOptInStatus: config.get('telemetry.allowChangingOptInStatus'), diff --git a/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts b/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts index 99090cb2fb7ef..6919b6959aa8c 100644 --- a/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts +++ b/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts @@ -24,7 +24,6 @@ import { dirname, join } from 'path'; // look for telemetry.yml in the same places we expect kibana.yml import { ensureDeepObject } from './ensure_deep_object'; -import { getXpackConfigWithDeprecated } from '../../../common/get_xpack_config_with_deprecated'; import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server'; /** @@ -85,7 +84,7 @@ export function createTelemetryUsageCollector( isReady: () => true, fetch: async () => { const config = server.config(); - const configPath = getXpackConfigWithDeprecated(config, 'telemetry.config') as string; + const configPath = config.get('telemetry.config') as string; const telemetryPath = join(dirname(configPath), 'telemetry.yml'); return await readTelemetryFile(telemetryPath); }, diff --git a/src/legacy/core_plugins/telemetry/server/fetcher.ts b/src/legacy/core_plugins/telemetry/server/fetcher.ts index 9edd8457f2b89..6e16328c4abd8 100644 --- a/src/legacy/core_plugins/telemetry/server/fetcher.ts +++ b/src/legacy/core_plugins/telemetry/server/fetcher.ts @@ -24,7 +24,6 @@ import { telemetryCollectionManager } from './collection_manager'; import { getTelemetryOptIn, getTelemetrySendUsageFrom } from './telemetry_config'; import { getTelemetrySavedObject, updateTelemetrySavedObject } from './telemetry_repository'; import { REPORT_INTERVAL_MS } from '../common/constants'; -import { getXpackConfigWithDeprecated } from '../common/get_xpack_config_with_deprecated'; export class FetcherTask { private readonly checkDurationMs = 60 * 1000 * 5; @@ -52,7 +51,7 @@ export class FetcherTask { const configTelemetrySendUsageFrom = config.get('telemetry.sendUsageFrom'); const allowChangingOptInStatus = config.get('telemetry.allowChangingOptInStatus'); const configTelemetryOptIn = config.get('telemetry.optIn'); - const telemetryUrl = getXpackConfigWithDeprecated(config, 'telemetry.url') as string; + const telemetryUrl = config.get('telemetry.url') as string; return { telemetryOptIn: getTelemetryOptIn({ diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index c15c40176e803..3f1570e42fa95 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -35,7 +35,6 @@ export class TelemetryPlugin implements Plugin { overlays, application, }: CoreStart): TelemetryPluginStart { - const isPluginEnabled = injectedMetadata.getInjectedVar('telemetryEnabled') as boolean; const telemetryBanner = injectedMetadata.getInjectedVar('telemetryBanner') as boolean; const sendUsageFrom = injectedMetadata.getInjectedVar('telemetrySendUsageFrom') as | 'browser' @@ -62,7 +61,6 @@ export class TelemetryPlugin implements Plugin { this.maybeStartTelemetryPoller({ telemetrySender, - isPluginEnabled, sendUsageFrom, }); @@ -90,14 +88,12 @@ export class TelemetryPlugin implements Plugin { private maybeStartTelemetryPoller({ telemetrySender, - isPluginEnabled, sendUsageFrom, }: { telemetrySender: TelemetrySender; - isPluginEnabled: boolean; sendUsageFrom: string; }) { - if (isPluginEnabled && sendUsageFrom === 'browser') { + if (sendUsageFrom === 'browser') { telemetrySender.startChecking(); } } diff --git a/x-pack/legacy/plugins/xpack_main/index.js b/x-pack/legacy/plugins/xpack_main/index.js index f3994f7ebcc34..809d90d58d796 100644 --- a/x-pack/legacy/plugins/xpack_main/index.js +++ b/x-pack/legacy/plugins/xpack_main/index.js @@ -11,8 +11,6 @@ import { replaceInjectedVars } from './server/lib/replace_injected_vars'; import { setupXPackMain } from './server/lib/setup_xpack_main'; import { xpackInfoRoute, settingsRoute } from './server/routes/api/v1'; -import { has } from 'lodash'; - export { callClusterFactory } from './server/lib/call_cluster_factory'; import { registerMonitoringCollection } from './server/telemetry_collection'; @@ -82,21 +80,5 @@ export const xpackMain = kibana => { xpackInfoRoute(server); settingsRoute(server, this.kbnServer); }, - deprecations: () => { - function movedToTelemetry(configPath) { - return (settings, log) => { - if (has(settings, configPath)) { - log( - `Config key "xpack.xpack_main.${configPath}" is deprecated. Use "telemetry.${configPath}" instead.` - ); - } - }; - } - return [ - movedToTelemetry('telemetry.config'), - movedToTelemetry('telemetry.url'), - movedToTelemetry('telemetry.enabled'), - ]; - }, }); }; From 5136e28c2e6dd09c43898e51518f8c65ea67b345 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 5 Feb 2020 01:39:02 +0200 Subject: [PATCH 26/41] pass telemetry from setup instead of npStart --- .../application/application_service.mock.ts | 18 +-- .../handle_old_settings.ts | 2 +- src/plugins/telemetry/public/index.ts | 2 +- src/plugins/telemetry/public/plugin.ts | 108 +++++++++--------- .../public/np_ready/application/app.js | 7 +- .../public/np_ready/application/boot.tsx | 8 +- .../np_ready/application/lib/telemetry.ts | 8 +- .../license_dashboard/license_dashboard.js | 6 +- .../start_trial/start_trial.container.js | 1 + .../start_trial/start_trial.tsx | 5 +- .../upload_license.container.js | 3 - .../sections/upload_license/upload_license.js | 1 + .../public/np_ready/plugin.ts | 6 +- .../public/register_route.ts | 1 + 14 files changed, 91 insertions(+), 85 deletions(-) diff --git a/src/core/public/application/application_service.mock.ts b/src/core/public/application/application_service.mock.ts index 02a1d6b4f8748..d2a827d381be5 100644 --- a/src/core/public/application/application_service.mock.ts +++ b/src/core/public/application/application_service.mock.ts @@ -43,13 +43,17 @@ const createInternalSetupContractMock = (): jest.Mocked => ({ - currentAppId$: currentAppId$.asObservable(), - capabilities: capabilitiesServiceMock.createStartContract().capabilities, - navigateToApp: jest.fn(), - getUrlForApp: jest.fn(), - registerMountContext: jest.fn(), -}); +const createStartContractMock = (): jest.Mocked => { + const currentAppId$ = new Subject(); + + return { + currentAppId$: currentAppId$.asObservable(), + capabilities: capabilitiesServiceMock.createStartContract().capabilities, + navigateToApp: jest.fn(), + getUrlForApp: jest.fn(), + registerMountContext: jest.fn(), + }; +}; const createInternalStartContractMock = (): jest.Mocked => { const currentAppId$ = new Subject(); diff --git a/src/legacy/core_plugins/telemetry/server/handle_old_settings/handle_old_settings.ts b/src/legacy/core_plugins/telemetry/server/handle_old_settings/handle_old_settings.ts index 143f5e982da6e..b28a01bffa44d 100644 --- a/src/legacy/core_plugins/telemetry/server/handle_old_settings/handle_old_settings.ts +++ b/src/legacy/core_plugins/telemetry/server/handle_old_settings/handle_old_settings.ts @@ -42,7 +42,7 @@ export async function handleOldSettings(server: Server) { const oldAllowReportSetting = await uiSettings.get(CONFIG_ALLOW_REPORT); let legacyOptInValue = null; - if (typeof oldTelemetrySetting === 'boolean' && uiSettings.isOverridden(CONFIG_TELEMETRY)) { + if (typeof oldTelemetrySetting === 'boolean') { legacyOptInValue = oldTelemetrySetting; } else if ( typeof oldAllowReportSetting === 'boolean' && diff --git a/src/plugins/telemetry/public/index.ts b/src/plugins/telemetry/public/index.ts index 8d1e430080b2d..2f86d7749bb9b 100644 --- a/src/plugins/telemetry/public/index.ts +++ b/src/plugins/telemetry/public/index.ts @@ -18,7 +18,7 @@ */ import { TelemetryPlugin } from './plugin'; -export { TelemetryPluginStart } from './plugin'; +export { TelemetryPluginStart, TelemetryPluginSetup } from './plugin'; export function plugin() { return new TelemetryPlugin(); diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index 3f1570e42fa95..7ba51cacd1949 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -16,68 +16,69 @@ * specific language governing permissions and limitations * under the License. */ -import { Plugin, CoreStart, HttpStart } from '../../../core/public'; +import { Plugin, CoreStart, CoreSetup, HttpStart } from '../../../core/public'; import { TelemetrySender, TelemetryService, TelemetryNotifications } from './services'; +export interface TelemetryPluginSetup { + telemetryService: TelemetryService; +} + export interface TelemetryPluginStart { telemetryService: TelemetryService; telemetryNotifications: TelemetryNotifications; } -export class TelemetryPlugin implements Plugin { - public setup() {} +export class TelemetryPlugin implements Plugin { + private telemetrySender?: TelemetrySender; + private telemetryNotifications?: TelemetryNotifications; + private telemetryService?: TelemetryService; - public start({ - injectedMetadata, - http, - notifications, - overlays, - application, - }: CoreStart): TelemetryPluginStart { - const telemetryBanner = injectedMetadata.getInjectedVar('telemetryBanner') as boolean; - const sendUsageFrom = injectedMetadata.getInjectedVar('telemetrySendUsageFrom') as - | 'browser' - | 'server'; - - const telemetryService = new TelemetryService({ + public setup({ http, injectedMetadata, notifications }: CoreSetup): TelemetryPluginSetup { + this.telemetryService = new TelemetryService({ http, injectedMetadata, notifications, }); - const telemetryNotifications = new TelemetryNotifications({ + this.telemetrySender = new TelemetrySender(this.telemetryService); + + return { + telemetryService: this.telemetryService, + }; + } + + public start({ injectedMetadata, http, overlays, application }: CoreStart): TelemetryPluginStart { + if (!this.telemetryService) { + throw Error('Telemetry plugin failed to initialize properly.'); + } + + const telemetryBanner = injectedMetadata.getInjectedVar('telemetryBanner') as boolean; + const sendUsageFrom = injectedMetadata.getInjectedVar('telemetrySendUsageFrom') as + | 'browser' + | 'server'; + + this.telemetryNotifications = new TelemetryNotifications({ overlays, - telemetryService, + telemetryService: this.telemetryService, }); - const telemetrySender = new TelemetrySender(telemetryService); - application.currentAppId$.subscribe(appId => { const isUnauthenticated = this.getIsUnauthenticated(http); if (isUnauthenticated) { return; } - this.maybeStartTelemetryPoller({ - telemetrySender, - sendUsageFrom, - }); - + this.maybeStartTelemetryPoller({ sendUsageFrom }); if (telemetryBanner) { - this.maybeShowOptedInNotificationBanner({ - telemetryNotifications, - }); - - this.maybeShowOptInBanner({ - telemetryNotifications, - }); + this.maybeShowOptedInNotificationBanner(); + this.maybeShowOptInBanner(); } }); return { - telemetryService, - telemetryNotifications, + telemetryService: this.telemetryService, + telemetryNotifications: this.telemetryNotifications, }; } @@ -86,37 +87,32 @@ export class TelemetryPlugin implements Plugin { return anonymousPaths.isAnonymous(window.location.pathname); } - private maybeStartTelemetryPoller({ - telemetrySender, - sendUsageFrom, - }: { - telemetrySender: TelemetrySender; - sendUsageFrom: string; - }) { + private maybeStartTelemetryPoller({ sendUsageFrom }: { sendUsageFrom: string }) { + if (!this.telemetrySender) { + return; + } if (sendUsageFrom === 'browser') { - telemetrySender.startChecking(); + this.telemetrySender.startChecking(); } } - private maybeShowOptedInNotificationBanner({ - telemetryNotifications, - }: { - telemetryNotifications: TelemetryNotifications; - }) { - const shouldShowBanner = telemetryNotifications.shouldShowOptedInNoticeBanner(); + private maybeShowOptedInNotificationBanner() { + if (!this.telemetryNotifications) { + return; + } + const shouldShowBanner = this.telemetryNotifications.shouldShowOptedInNoticeBanner(); if (shouldShowBanner) { - telemetryNotifications.renderOptedInNoticeBanner(); + this.telemetryNotifications.renderOptedInNoticeBanner(); } } - private maybeShowOptInBanner({ - telemetryNotifications, - }: { - telemetryNotifications: TelemetryNotifications; - }) { - const shouldShowBanner = telemetryNotifications.shouldShowOptInBanner(); + private maybeShowOptInBanner() { + if (!this.telemetryNotifications) { + return; + } + const shouldShowBanner = this.telemetryNotifications.shouldShowOptInBanner(); if (shouldShowBanner) { - telemetryNotifications.renderOptInBanner(); + this.telemetryNotifications.renderOptInBanner(); } } } diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/app.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/app.js index 7c497518b9df5..6a6c38fa6abb6 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/app.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/app.js @@ -18,7 +18,7 @@ export class App extends Component { } render() { - const { hasPermission, permissionsLoading, permissionsError } = this.props; + const { hasPermission, permissionsLoading, permissionsError, telemetry } = this.props; if (permissionsLoading) { return ( @@ -85,11 +85,12 @@ export class App extends Component { ); } + const withTelemetry = Component => props => ; return ( - - + + ); diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx b/x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx index 2780b54230eba..33a5fb28b2c80 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx @@ -12,6 +12,7 @@ import * as history from 'history'; import { DocLinksStart, HttpSetup, ToastsSetup, ChromeStart } from 'src/core/public'; // @ts-ignore +import { TelemetryPluginSetup } from 'src/plugins/telemetry/public'; import { App } from './app.container'; // @ts-ignore import { licenseManagementStore } from './store'; @@ -34,10 +35,11 @@ interface AppDependencies { toasts: ToastsSetup; docLinks: DocLinksStart; http: HttpSetup; + telemetry?: TelemetryPluginSetup; } export const boot = (deps: AppDependencies) => { - const { I18nContext, element, legacy, toasts, docLinks, http, chrome } = deps; + const { I18nContext, element, legacy, toasts, docLinks, http, chrome, telemetry } = deps; const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; const esBase = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}`; const securityDocumentationLink = `${esBase}/security-settings.html`; @@ -56,15 +58,17 @@ export const boot = (deps: AppDependencies) => { toasts, http, chrome, + telemetry, MANAGEMENT_BREADCRUMB: legacy.MANAGEMENT_BREADCRUMB, }; const store = licenseManagementStore(initialState, services); + render( - + , diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts b/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts index 291ad7d588c66..9cc4ec5978fdc 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TelemetryPluginStart } from '../../../../../../../../src/plugins/telemetry/public'; +import { TelemetryPluginSetup } from '../../../../../../../../src/plugins/telemetry/public'; export { OptInExampleFlyout } from '../../../../../../../../src/plugins/telemetry/public/components'; export { PRIVACY_STATEMENT_URL } from '../../../../../../../../src/plugins/telemetry/common/constants'; -export { TelemetryPluginStart, shouldShowTelemetryOptIn }; +export { TelemetryPluginSetup, shouldShowTelemetryOptIn }; function shouldShowTelemetryOptIn( - telemetry?: TelemetryPluginStart -): telemetry is TelemetryPluginStart { + telemetry?: TelemetryPluginSetup +): telemetry is TelemetryPluginSetup { if (telemetry) { const { telemetryService } = telemetry; const isOptedIn = telemetryService.getIsOptedIn(); diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js index 57b5663f002b6..56c307a0d76e5 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js @@ -11,10 +11,8 @@ import { StartTrial } from './start_trial'; import { AddLicense } from './add_license'; import { RequestTrialExtension } from './request_trial_extension'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { npStart } from 'ui/new_platform'; -export const LicenseDashboard = ({ setBreadcrumb } = { setBreadcrumb: () => {} }) => { +export const LicenseDashboard = ({ setBreadcrumb, telemetry } = { setBreadcrumb: () => {} }) => { useEffect(() => { setBreadcrumb('dashboard'); }); @@ -27,7 +25,7 @@ export const LicenseDashboard = ({ setBreadcrumb } = { setBreadcrumb: () => {} } - + diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.container.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.container.js index 7f95ba55db150..926ba8c260020 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.container.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.container.js @@ -11,6 +11,7 @@ import { loadTrialStatus, startLicenseTrial } from '../../../store/actions/start import { shouldShowStartTrial } from '../../../store/reducers/license_management'; const mapStateToProps = state => { + console.log('state!@#!@', state); return { shouldShowStartTrial: shouldShowStartTrial(state), }; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx index 1ebbb6d3e931e..e0f8ade8e45da 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx @@ -26,12 +26,12 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { TelemetryOptIn } from '../../../components/telemetry_opt_in'; import { EXTERNAL_LINKS } from '../../../../../../common/constants'; import { getDocLinks } from '../../../lib/docs_links'; -import { TelemetryPluginStart, shouldShowTelemetryOptIn } from '../../../lib/telemetry'; +import { TelemetryPluginSetup, shouldShowTelemetryOptIn } from '../../../lib/telemetry'; interface Props { loadTrialStatus: () => void; startLicenseTrial: () => void; - telemetry?: TelemetryPluginStart; + telemetry?: TelemetryPluginSetup; shouldShowStartTrial: boolean; } @@ -71,6 +71,7 @@ export class StartTrial extends Component { acknowledgeModal() { const { showConfirmation, isOptingInToTelemetry } = this.state; const { telemetry } = this.props; + if (!showConfirmation) { return null; } diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.container.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.container.js index b6fca484b5e38..67bbd6d33de44 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.container.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.container.js @@ -8,8 +8,6 @@ import { connect } from 'react-redux'; import { setBreadcrumb } from '../../store/actions/set_breadcrumb'; import { uploadLicense, uploadLicenseStatus } from '../../store/actions/upload_license'; import { addUploadErrorMessage } from '../../store/actions/add_error_message'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { npStart } from 'ui/new_platform'; import { getUploadErrorMessage, @@ -29,7 +27,6 @@ const mapStateToProps = state => { errorMessage: getUploadErrorMessage(state), applying: isApplying(state), currentLicenseType: getLicenseType(state) || '', - telemetry: npStart.plugins.telemetry, }; }; const mapDispatchToProps = { diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js index 8460065639303..e8dd9495a8c2d 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js @@ -125,6 +125,7 @@ export class UploadLicense extends React.PureComponent { }; render() { const { currentLicenseType, applying, telemetry } = this.props; + return ( diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/plugin.ts b/x-pack/legacy/plugins/license_management/public/np_ready/plugin.ts index 1da3c942830ca..60876c9b638d1 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/plugin.ts +++ b/x-pack/legacy/plugins/license_management/public/np_ready/plugin.ts @@ -5,11 +5,12 @@ */ import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import { TelemetryPluginSetup } from 'src/plugins/telemetry/public'; import { XPackMainPlugin } from '../../../xpack_main/server/xpack_main'; import { PLUGIN } from '../../common/constants'; import { Breadcrumb } from './application/breadcrumbs'; - export interface Plugins { + telemetry: TelemetryPluginSetup; __LEGACY: { xpackInfo: XPackMainPlugin; refreshXpack: () => void; @@ -18,7 +19,7 @@ export interface Plugins { } export class LicenseManagementUIPlugin implements Plugin { - setup({ application, notifications, http }: CoreSetup, { __LEGACY }: Plugins) { + setup({ application, notifications, http }: CoreSetup, { __LEGACY, telemetry }: Plugins) { application.register({ id: PLUGIN.ID, title: PLUGIN.TITLE, @@ -41,6 +42,7 @@ export class LicenseManagementUIPlugin implements Plugin { http, element, chrome, + telemetry, }); }, }); diff --git a/x-pack/legacy/plugins/license_management/public/register_route.ts b/x-pack/legacy/plugins/license_management/public/register_route.ts index fe34514303424..a8f27a7236a47 100644 --- a/x-pack/legacy/plugins/license_management/public/register_route.ts +++ b/x-pack/legacy/plugins/license_management/public/register_route.ts @@ -74,6 +74,7 @@ if (licenseManagementUiEnabled) { }, }, { + telemetry: (npSetup.plugins as any).telemetry, __LEGACY: { xpackInfo, refreshXpack, MANAGEMENT_BREADCRUMB }, } ); From 02a5412b7128911dd91617a50b8e5bc6c32a13eb Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 5 Feb 2020 01:44:19 +0200 Subject: [PATCH 27/41] type check --- .../license_management/public/np_ready/application/boot.tsx | 2 +- .../components/telemetry_opt_in/telemetry_opt_in.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx b/x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx index 33a5fb28b2c80..49bb4ce984e48 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx @@ -11,8 +11,8 @@ import { render, unmountComponentAtNode } from 'react-dom'; import * as history from 'history'; import { DocLinksStart, HttpSetup, ToastsSetup, ChromeStart } from 'src/core/public'; -// @ts-ignore import { TelemetryPluginSetup } from 'src/plugins/telemetry/public'; +// @ts-ignore import { App } from './app.container'; // @ts-ignore import { licenseManagementStore } from './store'; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.tsx b/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.tsx index cc0f2189c7f98..eff5c6cc21c43 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.tsx +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.tsx @@ -10,7 +10,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { OptInExampleFlyout, PRIVACY_STATEMENT_URL, - TelemetryPluginStart, + TelemetryPluginSetup, } from '../../lib/telemetry'; interface State { @@ -22,7 +22,7 @@ interface Props { onOptInChange: (isOptingInToTelemetry: boolean) => void; isOptingInToTelemetry: boolean; isStartTrial: boolean; - telemetry: TelemetryPluginStart; + telemetry: TelemetryPluginSetup; } export class TelemetryOptIn extends React.Component { From c0c4ab1a43378eed04b1962bf94c911fb2b4d062 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 5 Feb 2020 02:45:34 +0200 Subject: [PATCH 28/41] update core snapshots with new api exposed --- .../query_string_input.test.tsx.snap | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap index 2f2332bb06e3c..eebbc63f6f1e4 100644 --- a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap +++ b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap @@ -97,6 +97,17 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA "management": Object {}, "navLinks": Object {}, }, + "currentAppId$": Observable { + "_isScalar": false, + "source": Subject { + "_isScalar": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, "getUrlForApp": [MockFunction], "navigateToApp": [MockFunction], "registerMountContext": [MockFunction], @@ -738,6 +749,17 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA "management": Object {}, "navLinks": Object {}, }, + "currentAppId$": Observable { + "_isScalar": false, + "source": Subject { + "_isScalar": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, "getUrlForApp": [MockFunction], "navigateToApp": [MockFunction], "registerMountContext": [MockFunction], @@ -1361,6 +1383,17 @@ exports[`QueryStringInput Should pass the query language to the language switche "management": Object {}, "navLinks": Object {}, }, + "currentAppId$": Observable { + "_isScalar": false, + "source": Subject { + "_isScalar": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, "getUrlForApp": [MockFunction], "navigateToApp": [MockFunction], "registerMountContext": [MockFunction], @@ -1999,6 +2032,17 @@ exports[`QueryStringInput Should pass the query language to the language switche "management": Object {}, "navLinks": Object {}, }, + "currentAppId$": Observable { + "_isScalar": false, + "source": Subject { + "_isScalar": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, "getUrlForApp": [MockFunction], "navigateToApp": [MockFunction], "registerMountContext": [MockFunction], @@ -2622,6 +2666,17 @@ exports[`QueryStringInput Should render the given query 1`] = ` "management": Object {}, "navLinks": Object {}, }, + "currentAppId$": Observable { + "_isScalar": false, + "source": Subject { + "_isScalar": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, "getUrlForApp": [MockFunction], "navigateToApp": [MockFunction], "registerMountContext": [MockFunction], @@ -3260,6 +3315,17 @@ exports[`QueryStringInput Should render the given query 1`] = ` "management": Object {}, "navLinks": Object {}, }, + "currentAppId$": Observable { + "_isScalar": false, + "source": Subject { + "_isScalar": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, "getUrlForApp": [MockFunction], "navigateToApp": [MockFunction], "registerMountContext": [MockFunction], From bb3055f5f667786bb96261d3bc52612d306de6a0 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 5 Feb 2020 13:35:37 +0200 Subject: [PATCH 29/41] remove debugging logs --- .../license_dashboard/start_trial/start_trial.container.js | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.container.js b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.container.js index 926ba8c260020..7f95ba55db150 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.container.js +++ b/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.container.js @@ -11,7 +11,6 @@ import { loadTrialStatus, startLicenseTrial } from '../../../store/actions/start import { shouldShowStartTrial } from '../../../store/reducers/license_management'; const mapStateToProps = state => { - console.log('state!@#!@', state); return { shouldShowStartTrial: shouldShowStartTrial(state), }; From 7f544ff030909b46b6547b85f815106ef7566d86 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 10 Feb 2020 13:13:30 +0200 Subject: [PATCH 30/41] update home contract --- src/legacy/core_plugins/kibana/public/home/index.ts | 7 +++---- .../core_plugins/kibana/public/home/kibana_services.ts | 4 ++-- src/legacy/core_plugins/kibana/public/home/plugin.ts | 8 ++++---- src/legacy/ui/public/new_platform/new_platform.ts | 3 +++ .../plugins/xpack_main/public/services/xpack_info.js | 6 ------ 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/home/index.ts b/src/legacy/core_plugins/kibana/public/home/index.ts index ecf372bf907b0..2c5c30594578d 100644 --- a/src/legacy/core_plugins/kibana/public/home/index.ts +++ b/src/legacy/core_plugins/kibana/public/home/index.ts @@ -18,17 +18,16 @@ */ import { npSetup, npStart } from 'ui/new_platform'; -import chrome from 'ui/chrome'; import { HomePlugin } from './plugin'; -import { IPrivate } from '../../../../../plugins/kibana_legacy/public'; (async () => { const instance = new HomePlugin(); instance.setup(npSetup.core, { - ...npSetup.plugins, + ...(npSetup.plugins as any), __LEGACY: { metadata: npStart.core.injectedMetadata.getLegacyMetadata(), }, }); - instance.start(npStart.core, npStart.plugins as any); + + instance.start(npStart.core, 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 30cfff1c00070..57696d874cc40 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 { TelemetryPluginSetup } from '../../../../../plugins/telemetry/public'; +import { TelemetryPluginStart } from '../../../../../plugins/telemetry/public'; import { Environment, HomePublicPluginSetup, @@ -67,7 +67,7 @@ export interface HomeKibanaServices { docLinks: DocLinksStart; addBasePath: (url: string) => string; environment: Environment; - telemetry?: TelemetryPluginSetup; + telemetry?: TelemetryPluginStart; } let services: HomeKibanaServices | null = null; diff --git a/src/legacy/core_plugins/kibana/public/home/plugin.ts b/src/legacy/core_plugins/kibana/public/home/plugin.ts index da44a751c2d14..bf86c384bd60c 100644 --- a/src/legacy/core_plugins/kibana/public/home/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/home/plugin.ts @@ -20,7 +20,7 @@ import { CoreSetup, CoreStart, LegacyNavLink, Plugin, UiSettingsState } from 'kibana/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { TelemetryPluginSetup } from 'src/plugins/telemetry/public'; +import { TelemetryPluginStart } from 'src/plugins/telemetry/public'; import { setServices } from './kibana_services'; import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public'; import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public'; @@ -34,6 +34,7 @@ import { export interface HomePluginStartDependencies { data: DataPublicPluginStart; home: HomePublicPluginStart; + telemetry: TelemetryPluginStart; } export interface HomePluginSetupDependencies { @@ -52,7 +53,6 @@ export interface HomePluginSetupDependencies { uiSettings: { defaults: UiSettingsState; user?: UiSettingsState | undefined }; }; }; - telemetry: TelemetryPluginSetup; usageCollection: UsageCollectionSetup; kibanaLegacy: KibanaLegacySetup; home: HomePublicPluginSetup; @@ -61,6 +61,7 @@ export interface HomePluginSetupDependencies { export class HomePlugin implements Plugin { private dataStart: DataPublicPluginStart | null = null; private savedObjectsClient: any = null; + private telemetry: TelemetryPluginStart | null = null; private environment: Environment | null = null; private directories: readonly FeatureCatalogueEntry[] | null = null; @@ -70,7 +71,6 @@ export class HomePlugin implements Plugin { home, kibanaLegacy, usageCollection, - telemetry, __LEGACY: { ...legacyServices }, }: HomePluginSetupDependencies ) { @@ -88,6 +88,7 @@ export class HomePlugin implements Plugin { getInjected: core.injectedMetadata.getInjectedVar, docLinks: contextCore.docLinks, savedObjectsClient: this.savedObjectsClient!, + telemetry: this.telemetry!, chrome: contextCore.chrome, uiSettings: core.uiSettings, addBasePath: core.http.basePath.prepend, @@ -97,7 +98,6 @@ export class HomePlugin implements Plugin { config: kibanaLegacy.config, homeConfig: home.config, directories: this.directories!, - telemetry, }); const { renderApp } = await import('./np_ready/application'); return await renderApp(params.element); diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index e300ce4a0caf8..ff8fc9b07879c 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -39,6 +39,7 @@ import { import { ManagementSetup, ManagementStart } from '../../../../plugins/management/public'; import { BfetchPublicSetup, BfetchPublicStart } from '../../../../plugins/bfetch/public'; import { UsageCollectionSetup } from '../../../../plugins/usage_collection/public'; +import { TelemetryPluginSetup, TelemetryPluginStart } from '../../../../plugins/telemetry/public'; import { NavigationPublicPluginSetup, NavigationPublicPluginStart, @@ -60,6 +61,7 @@ export interface PluginsSetup { usageCollection: UsageCollectionSetup; advancedSettings: AdvancedSettingsSetup; management: ManagementSetup; + telemetry?: TelemetryPluginSetup; } export interface PluginsStart { @@ -77,6 +79,7 @@ export interface PluginsStart { share: SharePluginStart; management: ManagementStart; advancedSettings: AdvancedSettingsStart; + telemetry?: TelemetryPluginStart; } export const npSetup = { diff --git a/x-pack/legacy/plugins/xpack_main/public/services/xpack_info.js b/x-pack/legacy/plugins/xpack_main/public/services/xpack_info.js index 9d285cb276afb..8e71e2f63fa73 100644 --- a/x-pack/legacy/plugins/xpack_main/public/services/xpack_info.js +++ b/x-pack/legacy/plugins/xpack_main/public/services/xpack_info.js @@ -13,7 +13,6 @@ const XPACK_INFO_KEY = 'xpackMain.info'; export class XPackInfo { constructor(initialInfo = {}) { - console.log('oinit!@#'); this.inProgressRefreshPromise = null; this.setAll(initialInfo); } @@ -25,7 +24,6 @@ export class XPackInfo { }; setAll = updatedXPackInfo => { - console.log('updatedXPackInfo!@#!@#'); // The decision to convert kebab-case/snake-case keys to camel-case keys stemmed from an old // convention of using kebabe-case/snake-case in API response bodies but camel-case in JS // objects. See pull #29304 for more info. @@ -36,7 +34,6 @@ export class XPackInfo { }; clear = () => { - console.log('celeansdf!@#'); sessionStorage.removeItem(XPACK_INFO_KEY); }; @@ -51,7 +48,6 @@ export class XPackInfo { this.inProgressRefreshPromise = $http .get(chrome.addBasePath('/api/xpack/v1/info')) .catch(err => { - console.log('ok err!@#', err); // if we are unable to fetch the updated info, we should // prevent reusing stale info this.clear(); @@ -59,7 +55,6 @@ export class XPackInfo { throw err; }) .then(xpackInfoResponse => { - console.log('got response!@#'); this.setAll(xpackInfoResponse.data); xpackInfoSignature.set(xpackInfoResponse.headers('kbn-xpack-sig')); }) @@ -70,7 +65,6 @@ export class XPackInfo { }; getLicense = () => { - console.log('getting license!@#'); return this.get('license', { isActive: false, type: undefined, From 8b7ebbed372278ec3818289ec17f06761a05fc47 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 10 Feb 2020 13:20:43 +0200 Subject: [PATCH 31/41] update home contract --- src/legacy/core_plugins/kibana/public/home/index.ts | 2 +- src/legacy/core_plugins/kibana/public/home/plugin.ts | 6 +++--- test/common/config.js | 2 -- test/functional/config.ie.js | 7 +++++-- test/functional/config.js | 7 +++++-- x-pack/test/functional/config.ie.js | 7 +++++-- 6 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/home/index.ts b/src/legacy/core_plugins/kibana/public/home/index.ts index 2c5c30594578d..768e1a96de935 100644 --- a/src/legacy/core_plugins/kibana/public/home/index.ts +++ b/src/legacy/core_plugins/kibana/public/home/index.ts @@ -23,7 +23,7 @@ import { HomePlugin } from './plugin'; (async () => { const instance = new HomePlugin(); instance.setup(npSetup.core, { - ...(npSetup.plugins as any), + ...npSetup.plugins, __LEGACY: { metadata: npStart.core.injectedMetadata.getLegacyMetadata(), }, diff --git a/src/legacy/core_plugins/kibana/public/home/plugin.ts b/src/legacy/core_plugins/kibana/public/home/plugin.ts index bf86c384bd60c..5cc7c9c11dd2f 100644 --- a/src/legacy/core_plugins/kibana/public/home/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/home/plugin.ts @@ -34,7 +34,7 @@ import { export interface HomePluginStartDependencies { data: DataPublicPluginStart; home: HomePublicPluginStart; - telemetry: TelemetryPluginStart; + telemetry?: TelemetryPluginStart; } export interface HomePluginSetupDependencies { @@ -61,9 +61,9 @@ export interface HomePluginSetupDependencies { export class HomePlugin implements Plugin { private dataStart: DataPublicPluginStart | null = null; private savedObjectsClient: any = null; - private telemetry: TelemetryPluginStart | null = null; private environment: Environment | null = null; private directories: readonly FeatureCatalogueEntry[] | null = null; + private telemetry?: TelemetryPluginStart; setup( core: CoreSetup, @@ -88,7 +88,7 @@ export class HomePlugin implements Plugin { getInjected: core.injectedMetadata.getInjectedVar, docLinks: contextCore.docLinks, savedObjectsClient: this.savedObjectsClient!, - telemetry: this.telemetry!, + telemetry: this.telemetry, chrome: contextCore.chrome, uiSettings: core.uiSettings, addBasePath: core.http.basePath.prepend, diff --git a/test/common/config.js b/test/common/config.js index dc0c51223213b..faf8cef027170 100644 --- a/test/common/config.js +++ b/test/common/config.js @@ -51,12 +51,10 @@ export default function() { '--optimize.watchPrebuild=true', '--status.allowAnonymous=true', '--optimize.enabled=true', - '--usageCollection.uiMetric.enabled=false', `--elasticsearch.hosts=${formatUrl(servers.elasticsearch)}`, `--elasticsearch.username=${kibanaServerTestUser.username}`, `--elasticsearch.password=${kibanaServerTestUser.password}`, `--home.disableWelcomeScreen=true`, - '--telemetry.enabled=false', '--telemetry.banner=false', `--server.maxPayloadBytes=1679958`, // newsfeed mock service diff --git a/test/functional/config.ie.js b/test/functional/config.ie.js index 5e8ea56a848dc..2c32ccb69db03 100644 --- a/test/functional/config.ie.js +++ b/test/functional/config.ie.js @@ -35,7 +35,6 @@ export default async function({ readConfigFile }) { defaults: { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', - 'telemetry:optIn': false, 'state:storeInSessionStorage': true, 'notifications:lifetime:info': 10000, }, @@ -43,7 +42,11 @@ export default async function({ readConfigFile }) { kbnTestServer: { ...defaultConfig.get('kbnTestServer'), - serverArgs: [...defaultConfig.get('kbnTestServer.serverArgs'), '--csp.strict=false'], + serverArgs: [ + ...defaultConfig.get('kbnTestServer.serverArgs'), + '--csp.strict=false', + '--telemetry.optIn=false', + ], }, }; } diff --git a/test/functional/config.js b/test/functional/config.js index 134ddf4e84b2d..7861594bfea8b 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -44,14 +44,17 @@ export default async function({ readConfigFile }) { kbnTestServer: { ...commonConfig.get('kbnTestServer'), - serverArgs: [...commonConfig.get('kbnTestServer.serverArgs'), '--oss'], + serverArgs: [ + ...commonConfig.get('kbnTestServer.serverArgs'), + '--oss', + '--telemetry.optIn: false', + ], }, uiSettings: { defaults: { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', - 'telemetry:optIn': false, }, }, diff --git a/x-pack/test/functional/config.ie.js b/x-pack/test/functional/config.ie.js index 081bab4b80457..bac4547b4aa5c 100644 --- a/x-pack/test/functional/config.ie.js +++ b/x-pack/test/functional/config.ie.js @@ -58,14 +58,17 @@ export default async function({ readConfigFile }) { defaults: { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', - 'telemetry:optIn': false, 'state:storeInSessionStorage': true, }, }, kbnTestServer: { ...defaultConfig.get('kbnTestServer'), - serverArgs: [...defaultConfig.get('kbnTestServer.serverArgs'), '--csp.strict=false'], + serverArgs: [ + ...defaultConfig.get('kbnTestServer.serverArgs'), + '--csp.strict=false', + '--telemetry.optIn=false', + ], }, }; } From 9945f672ce6697d88a61fe1d28ca94cb71470d3f Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 10 Feb 2020 14:21:14 +0200 Subject: [PATCH 32/41] fix test eslint import --- .../kibana/public/home/np_ready/components/welcome.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx index 11aac772d0963..36f1c1be55366 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx @@ -20,6 +20,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { Welcome } from './welcome'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { telemetryPluginMock } from '../../../../../../../plugins/telemetry/public/telemetry.mock'; jest.mock('../../kibana_services', () => ({ getServices: () => ({ From d62567f4ba3ed3694b02969e209a4b8dcc9ee717 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 10 Feb 2020 17:15:29 +0200 Subject: [PATCH 33/41] navigate back to dashboard before start of next case for reporting --- x-pack/test/reporting/functional/reporting.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/test/reporting/functional/reporting.js b/x-pack/test/reporting/functional/reporting.js index 0e1078a2a4c8b..6650256db3bf1 100644 --- a/x-pack/test/reporting/functional/reporting.js +++ b/x-pack/test/reporting/functional/reporting.js @@ -94,8 +94,8 @@ export default function({ getService, getPageObjects }) { // Generating and then comparing reports can take longer than the default 60s timeout because the comparePngs // function is taking about 15 seconds per comparison in jenkins. this.timeout(300000); - - await PageObjects.dashboard.switchToEditMode(); + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.gotoDashboardEditMode('My PDF Dashboard'); await PageObjects.reporting.setTimepickerInDataRange(); const visualizations = PageObjects.dashboard.getTestVisualizationNames(); From 3e0cc95cd2d7179e9d2de425a7424d21113c49c3 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 10 Feb 2020 17:16:48 +0200 Subject: [PATCH 34/41] gitignore reporting failure_debug generated dir --- x-pack/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/.gitignore b/x-pack/.gitignore index 40a52f88dbbba..6bac5e181861d 100644 --- a/x-pack/.gitignore +++ b/x-pack/.gitignore @@ -4,6 +4,7 @@ /test/functional/failure_debug /test/functional/screenshots /test/functional/apps/reporting/reports/session +/test/reporting/configs/failure_debug/ /legacy/plugins/reporting/.chromium/ /legacy/plugins/reporting/.phantom/ /.aws-config.json From cb246273991380a9608f75857381657ed6472534 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 10 Feb 2020 18:43:30 +0200 Subject: [PATCH 35/41] use gotoDashboardEditMode instead of switch --- x-pack/test/reporting/functional/reporting.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/reporting/functional/reporting.js b/x-pack/test/reporting/functional/reporting.js index 6650256db3bf1..012f0922c28cf 100644 --- a/x-pack/test/reporting/functional/reporting.js +++ b/x-pack/test/reporting/functional/reporting.js @@ -135,7 +135,8 @@ export default function({ getService, getPageObjects }) { it('matches baseline report', async function() { this.timeout(300000); - await PageObjects.dashboard.switchToEditMode(); + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.gotoDashboardEditMode('My PNG Dash'); await PageObjects.reporting.setTimepickerInDataRange(); const visualizations = PageObjects.dashboard.getTestVisualizationNames(); From 50f3cca4db5f12b7aea929ca2715d8a304181921 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 10 Feb 2020 19:10:08 +0200 Subject: [PATCH 36/41] = instead of : --- test/functional/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/config.js b/test/functional/config.js index 7861594bfea8b..155e844578c54 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -47,7 +47,7 @@ export default async function({ readConfigFile }) { serverArgs: [ ...commonConfig.get('kbnTestServer.serverArgs'), '--oss', - '--telemetry.optIn: false', + '--telemetry.optIn=false', ], }, From 6b021b07a900d2c3fbfeaca24a7590de2792afb0 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Tue, 11 Feb 2020 18:45:23 +0200 Subject: [PATCH 37/41] merge master --- .../public/components/telemetry_management_section.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/telemetry/public/components/telemetry_management_section.tsx b/src/plugins/telemetry/public/components/telemetry_management_section.tsx index c01d7a2b6ab72..00c7205b36cd0 100644 --- a/src/plugins/telemetry/public/components/telemetry_management_section.tsx +++ b/src/plugins/telemetry/public/components/telemetry_management_section.tsx @@ -33,7 +33,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { PRIVACY_STATEMENT_URL } from '../../common/constants'; import { OptInExampleFlyout } from './opt_in_example_flyout'; -import { Field } from '../../../../../plugins/advanced_settings/public'; +import { Field } from '../../../advanced_settings/public'; import { TelemetryService } from '../services/telemetry_service'; const SEARCH_TERMS = ['telemetry', 'usage', 'data', 'usage data']; From 1f7593c4495cac3e12150f3017c79abb5cfa37e1 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 12 Feb 2020 00:11:59 +0200 Subject: [PATCH 38/41] escape unused forced types in Field --- .../public/components/telemetry_management_section.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/telemetry/public/components/telemetry_management_section.tsx b/src/plugins/telemetry/public/components/telemetry_management_section.tsx index 00c7205b36cd0..93de9edb623ee 100644 --- a/src/plugins/telemetry/public/components/telemetry_management_section.tsx +++ b/src/plugins/telemetry/public/components/telemetry_management_section.tsx @@ -33,6 +33,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { PRIVACY_STATEMENT_URL } from '../../common/constants'; import { OptInExampleFlyout } from './opt_in_example_flyout'; +// @ts-ignore import { Field } from '../../../advanced_settings/public'; import { TelemetryService } from '../services/telemetry_service'; const SEARCH_TERMS = ['telemetry', 'usage', 'data', 'usage data']; @@ -126,6 +127,8 @@ export class TelemetryManagementSection extends Component { }), } as any } + dockLinks={null as any} + toasts={null as any} save={this.toggleOptIn} clear={this.toggleOptIn} enableSaving={this.props.enableSaving} From d1238c4868ed9655da6613bb8df6e73371f90e71 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 12 Feb 2020 12:52:48 +0200 Subject: [PATCH 39/41] rename mock to mocks for eslint --- .../kibana/public/home/np_ready/components/welcome.test.tsx | 4 ++-- src/plugins/telemetry/public/{telemetry.mock.ts => mocks.ts} | 0 .../telemetry_notifications/telemetry_notifications.test.ts | 2 +- .../telemetry/public/services/telemetry_sender.test.ts | 2 +- .../telemetry/public/services/telemetry_service.test.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename src/plugins/telemetry/public/{telemetry.mock.ts => mocks.ts} (100%) diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx index 36f1c1be55366..d9da47a2b43da 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx @@ -20,8 +20,8 @@ import React from 'react'; import { shallow } from 'enzyme'; import { Welcome } from './welcome'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { telemetryPluginMock } from '../../../../../../../plugins/telemetry/public/telemetry.mock'; +import { telemetryPluginMock } from '../../../../../../../plugins/telemetry/public/mocks'; + jest.mock('../../kibana_services', () => ({ getServices: () => ({ addBasePath: (path: string) => `root${path}`, diff --git a/src/plugins/telemetry/public/telemetry.mock.ts b/src/plugins/telemetry/public/mocks.ts similarity index 100% rename from src/plugins/telemetry/public/telemetry.mock.ts rename to src/plugins/telemetry/public/mocks.ts diff --git a/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.test.ts b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.test.ts index 9464dbf49c3ac..f767615d25253 100644 --- a/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_notifications/telemetry_notifications.test.ts @@ -18,7 +18,7 @@ */ /* eslint-disable dot-notation */ -import { mockTelemetryNotifications, mockTelemetryService } from '../../telemetry.mock'; +import { mockTelemetryNotifications, mockTelemetryService } from '../../mocks'; describe('onSetOptInClick', () => { it('sets setting successfully and removes banner', async () => { diff --git a/src/plugins/telemetry/public/services/telemetry_sender.test.ts b/src/plugins/telemetry/public/services/telemetry_sender.test.ts index 9db4ef30d8eed..e9f5765c10412 100644 --- a/src/plugins/telemetry/public/services/telemetry_sender.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_sender.test.ts @@ -19,7 +19,7 @@ /* eslint-disable dot-notation */ import { TelemetrySender } from './telemetry_sender'; -import { mockTelemetryService } from '../telemetry.mock'; +import { mockTelemetryService } from '../mocks'; import { REPORT_INTERVAL_MS, LOCALSTORAGE_KEY } from '../../common/constants'; class LocalStorageMock implements Partial { diff --git a/src/plugins/telemetry/public/services/telemetry_service.test.ts b/src/plugins/telemetry/public/services/telemetry_service.test.ts index c6f44e82a4a2e..0ebcd52f1423c 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.test.ts @@ -18,7 +18,7 @@ */ /* eslint-disable dot-notation */ -import { mockTelemetryService } from '../telemetry.mock'; +import { mockTelemetryService } from '../mocks'; const mockSubtract = jest.fn().mockImplementation(() => { return { From cd70972b7013c132de3669e526fd4513dbc30695 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 12 Feb 2020 15:59:42 +0200 Subject: [PATCH 40/41] Update src/plugins/telemetry/public/components/telemetry_management_section.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Alejandro Fernández Haro --- .../public/components/telemetry_management_section.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/telemetry/public/components/telemetry_management_section.tsx b/src/plugins/telemetry/public/components/telemetry_management_section.tsx index 93de9edb623ee..df60626f45541 100644 --- a/src/plugins/telemetry/public/components/telemetry_management_section.tsx +++ b/src/plugins/telemetry/public/components/telemetry_management_section.tsx @@ -199,7 +199,7 @@ export class TelemetryManagementSection extends Component { ); - toggleOptIn = async (): Promise => { + toggleOptIn = async (): Promise => { const { telemetryService } = this.props; const newOptInValue = !telemetryService.getIsOptedIn(); From aaae6c56a032bcbdc607e23ce2e05306de6c186d Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 12 Feb 2020 16:57:04 +0200 Subject: [PATCH 41/41] fix save/clear type --- .../public/components/telemetry_management_section.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/telemetry/public/components/telemetry_management_section.tsx b/src/plugins/telemetry/public/components/telemetry_management_section.tsx index df60626f45541..20c8873b13272 100644 --- a/src/plugins/telemetry/public/components/telemetry_management_section.tsx +++ b/src/plugins/telemetry/public/components/telemetry_management_section.tsx @@ -199,7 +199,7 @@ export class TelemetryManagementSection extends Component { ); - toggleOptIn = async (): Promise => { + toggleOptIn = async (): Promise => { const { telemetryService } = this.props; const newOptInValue = !telemetryService.getIsOptedIn(); @@ -208,7 +208,7 @@ export class TelemetryManagementSection extends Component { try { await telemetryService.setOptIn(newOptInValue); this.setState({ processing: false }); - resolve(); + resolve(true); } catch (err) { this.setState({ processing: false }); reject(err);