From 837be314a418920d2cad5b1115c9cd7cfd6c105e Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 22 Aug 2018 10:39:35 -0400 Subject: [PATCH 01/18] add mappings for new telemetry document --- x-pack/plugins/xpack_main/index.js | 3 ++- x-pack/plugins/xpack_main/mappings.json | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/xpack_main/mappings.json diff --git a/x-pack/plugins/xpack_main/index.js b/x-pack/plugins/xpack_main/index.js index 1fe2393f61d364..e7ee46b283f1d5 100644 --- a/x-pack/plugins/xpack_main/index.js +++ b/x-pack/plugins/xpack_main/index.js @@ -22,6 +22,7 @@ import { CONFIG_TELEMETRY_DESC, } from './common/constants'; import { settingsRoute } from './server/routes/api/v1/settings'; +import mappings from './mappings'; export { callClusterFactory } from './server/lib/call_cluster_factory'; @@ -48,7 +49,7 @@ export const xpackMain = (kibana) => { configPrefix: 'xpack.xpack_main', publicDir: resolve(__dirname, 'public'), require: ['elasticsearch'], - + mappings, config(Joi) { return Joi.object({ enabled: Joi.boolean().default(true), diff --git a/x-pack/plugins/xpack_main/mappings.json b/x-pack/plugins/xpack_main/mappings.json new file mode 100644 index 00000000000000..d83f7f59676306 --- /dev/null +++ b/x-pack/plugins/xpack_main/mappings.json @@ -0,0 +1,9 @@ +{ + "telemetry": { + "properties": { + "enabled": { + "type": "boolean" + } + } + } +} From ca4699089b7f8b355b7ecc77ac92c059687ce9d9 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 22 Aug 2018 15:15:35 -0400 Subject: [PATCH 02/18] use new telemetry document, and expose management page to set this --- x-pack/plugins/xpack_main/index.js | 10 +- .../xpack_main/public/hacks/telemetry.js | 11 +- .../hacks/welcome_banner/click_banner.js | 17 +-- .../welcome_banner/handle_old_settings.js | 29 +++-- .../hacks/welcome_banner/inject_banner.js | 8 +- .../hacks/welcome_banner/render_banner.js | 6 +- .../welcome_banner/should_show_banner.js | 7 +- .../public/services/telemetry_opt_in.js | 32 +++++ .../public/views/management/index.js | 7 ++ .../public/views/management/management.js | 24 ++++ .../views/management/telemetry/index.js | 7 ++ .../telemetry/manage_telemetry_page.js | 111 ++++++++++++++++++ .../telemetry/manage_telemetry_page.less | 4 + .../views/management/telemetry/telemetry.html | 3 + .../views/management/telemetry/telemetry.js | 35 ++++++ .../server/lib/get_telemetry_opt_in.js | 19 +++ .../server/lib/replace_injected_vars.js | 11 +- .../routes/api/v1/telemetry/telemetry.js | 38 +++++- 18 files changed, 336 insertions(+), 43 deletions(-) create mode 100644 x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js create mode 100644 x-pack/plugins/xpack_main/public/views/management/index.js create mode 100644 x-pack/plugins/xpack_main/public/views/management/management.js create mode 100644 x-pack/plugins/xpack_main/public/views/management/telemetry/index.js create mode 100644 x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js create mode 100644 x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less create mode 100644 x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.html create mode 100644 x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.js create mode 100644 x-pack/plugins/xpack_main/server/lib/get_telemetry_opt_in.js diff --git a/x-pack/plugins/xpack_main/index.js b/x-pack/plugins/xpack_main/index.js index e7ee46b283f1d5..6e6ed25893ced1 100644 --- a/x-pack/plugins/xpack_main/index.js +++ b/x-pack/plugins/xpack_main/index.js @@ -22,7 +22,7 @@ import { CONFIG_TELEMETRY_DESC, } from './common/constants'; import { settingsRoute } from './server/routes/api/v1/settings'; -import mappings from './mappings'; +import mappings from './mappings.json'; export { callClusterFactory } from './server/lib/call_cluster_factory'; @@ -49,7 +49,7 @@ export const xpackMain = (kibana) => { configPrefix: 'xpack.xpack_main', publicDir: resolve(__dirname, 'public'), require: ['elasticsearch'], - mappings, + config(Joi) { return Joi.object({ enabled: Joi.boolean().default(true), @@ -66,11 +66,13 @@ export const xpackMain = (kibana) => { }, uiExports: { + managementSections: ['plugins/xpack_main/views/management'], uiSettingDefaults: { [CONFIG_TELEMETRY]: { name: 'Telemetry opt-in', description: CONFIG_TELEMETRY_DESC, - value: false + value: false, + readonly: true, }, [XPACK_DEFAULT_ADMIN_EMAIL_UI_SETTING]: { name: 'Admin email', @@ -85,6 +87,7 @@ export const xpackMain = (kibana) => { return { telemetryUrl: config.get('xpack.xpack_main.telemetry.url'), telemetryEnabled: isTelemetryEnabled(config), + telemetryOptedIn: null, }; }, hacks: [ @@ -102,6 +105,7 @@ export const xpackMain = (kibana) => { raw: true, }); }, + mappings, }, init(server) { diff --git a/x-pack/plugins/xpack_main/public/hacks/telemetry.js b/x-pack/plugins/xpack_main/public/hacks/telemetry.js index 66abbdbaee9a0b..4b03f1c951118b 100644 --- a/x-pack/plugins/xpack_main/public/hacks/telemetry.js +++ b/x-pack/plugins/xpack_main/public/hacks/telemetry.js @@ -6,7 +6,6 @@ import Promise from 'bluebird'; import { - CONFIG_TELEMETRY, REPORT_INTERVAL_MS, LOCALSTORAGE_KEY, } from '../../common/constants'; @@ -19,9 +18,9 @@ export class Telemetry { */ constructor($injector, fetchTelemetry) { this._storage = $injector.get('localStorage'); - this._config = $injector.get('config'); this._$http = $injector.get('$http'); this._telemetryUrl = $injector.get('telemetryUrl'); + this._telemetryOptedIn = $injector.get('telemetryOptedIn'); this._attributes = this._storage.get(LOCALSTORAGE_KEY) || {}; this._fetchTelemetry = fetchTelemetry; } @@ -42,8 +41,8 @@ export class Telemetry { * Check time interval passage */ _checkReportStatus() { - // check if opt-in for telemetry is enabled in config - if (this._config.get(CONFIG_TELEMETRY, false)) { + // check if opt-in for telemetry is enabled + if (this._telemetryOptedIn) { // If the last report is empty it means we've never sent telemetry and // now is the time to send it. if (!this._get('lastReport')) { @@ -78,13 +77,13 @@ export class Telemetry { }); }) .then(response => { - // we sent a report, so we need to record and store the current time stamp + // we sent a report, so we need to record and store the current time stamp this._set('lastReport', Date.now()); this._saveToBrowser(); return response; }) .catch(() => { - // no ajaxErrorHandlers for telemetry + // no ajaxErrorHandlers for telemetry return Promise.resolve(null); }); } diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/click_banner.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/click_banner.js index ce0f2a98b416c9..efc07f49c58648 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/click_banner.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/click_banner.js @@ -12,22 +12,25 @@ import { } from 'ui/notify'; import { EuiText } from '@elastic/eui'; -import { CONFIG_TELEMETRY } from '../../../common/constants'; - /** * Handle clicks from the user on the opt-in banner. * * @param {String} bannerId Banner ID to close upon success. - * @param {Object} config Advanced settings configuration to set opt-in. + * @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(bannerId, config, optIn, { _banners = banners, _toastNotifications = toastNotifications } = { }) { +export async function clickBanner( + bannerId, + telemetryOptInProvider, + optIn, + { _banners = banners, _toastNotifications = toastNotifications } = {}) { + let set = false; try { - set = await config.set(CONFIG_TELEMETRY, Boolean(optIn)); + set = await telemetryOptInProvider.setOptIn(optIn); } catch (err) { // set is already false console.log('Unexpected error while trying to save setting.', err); @@ -37,10 +40,10 @@ export async function clickBanner(bannerId, config, optIn, { _banners = banners, _banners.remove(bannerId); } else { _toastNotifications.addDanger({ - title: 'Advanced Setting Error', + title: 'Telemetry Error', text: ( -

Unable to save advanced setting.

+

Unable to save telemetry preference.

Check that Kibana and Elasticsearch are still running, then try again. diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/handle_old_settings.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/handle_old_settings.js index fba5a22d5b16ff..1c95901d84514e 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/handle_old_settings.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/handle_old_settings.js @@ -14,17 +14,28 @@ 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. */ -export async function handleOldSettings(config) { +export async function handleOldSettings(config, telemetryOptInProvider) { const CONFIG_ALLOW_REPORT = 'xPackMonitoring:allowReport'; const CONFIG_SHOW_BANNER = 'xPackMonitoring:showBanner'; - const oldSetting = config.get(CONFIG_ALLOW_REPORT, null); + const oldAllowReportSetting = config.get(CONFIG_ALLOW_REPORT, null); + const oldTelemetrySetting = config.get(CONFIG_TELEMETRY, null); - if (oldSetting !== null) { - if (await config.set(CONFIG_TELEMETRY, Boolean(oldSetting))) { - // 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); - } + let legacyOptInValue = null; + + if (oldTelemetrySetting !== null) { + legacyOptInValue = oldTelemetrySetting; + } else if (oldAllowReportSetting !== null) { + legacyOptInValue = oldAllowReportSetting; + } + + console.log({ legacyOptInValue, oldAllowReportSetting, oldTelemetrySetting }); + if (legacyOptInValue !== null) { + 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); return false; } @@ -36,4 +47,4 @@ export async function handleOldSettings(config) { } return true; -} \ No newline at end of file +} diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/inject_banner.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/inject_banner.js index 7fd8a5027d4074..d3142fd3e2dac3 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/inject_banner.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/inject_banner.js @@ -9,6 +9,7 @@ import { PathProvider } from 'plugins/xpack_main/services/path'; import { fetchTelemetry } from '../fetch_telemetry'; import { renderBanner } from './render_banner'; import { shouldShowBanner } from './should_show_banner'; +import { TelemetryOptInProvider } from '../../services/telemetry_opt_in'; /** * Add the Telemetry opt-in banner if the user has not already made a decision. @@ -21,6 +22,7 @@ import { shouldShowBanner } from './should_show_banner'; async function asyncInjectBanner($injector) { const telemetryEnabled = $injector.get('telemetryEnabled'); const Private = $injector.get('Private'); + const telemetryOptInProvider = Private(TelemetryOptInProvider); const config = $injector.get('config'); // no banner if the server config has telemetry disabled @@ -39,10 +41,10 @@ async function asyncInjectBanner($injector) { } // determine if the banner should be displayed - if (await shouldShowBanner(config)) { - const $http = $injector.get('$http'); + if (await shouldShowBanner(telemetryOptInProvider, config)) { + const $http = $injector.get("$http"); - renderBanner(config, () => fetchTelemetry($http)); + renderBanner(telemetryOptInProvider, () => fetchTelemetry($http)); } } diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/render_banner.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/render_banner.js index 59ccced7ac7144..1fa1287cc2d98f 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/render_banner.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/render_banner.js @@ -14,15 +14,15 @@ import { OptInBanner } from './opt_in_banner_component'; /** * Render the Telemetry Opt-in banner. * - * @param {Object} config The advanced settings config. + * @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(config, fetchTelemetry, { _banners = banners } = { }) { +export function renderBanner(telemetryOptInProvider, fetchTelemetry, { _banners = banners } = {}) { const bannerId = _banners.add({ component: ( clickBanner(bannerId, config, optIn)} + optInClick={optIn => clickBanner(bannerId, telemetryOptInProvider, optIn)} fetchTelemetry={fetchTelemetry} /> ), diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/should_show_banner.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/should_show_banner.js index 4e224173997b69..5685132a95061f 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/should_show_banner.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/should_show_banner.js @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CONFIG_TELEMETRY } from '../../../common/constants'; import { handleOldSettings } from './handle_old_settings'; /** @@ -16,6 +15,6 @@ import { handleOldSettings } from './handle_old_settings'; * @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(config, { _handleOldSettings = handleOldSettings } = { }) { - return config.get(CONFIG_TELEMETRY, null) === null && await _handleOldSettings(config); -} \ No newline at end of file +export async function shouldShowBanner(telemetryOptInProvider, config, { _handleOldSettings = handleOldSettings } = {}) { + return telemetryOptInProvider.getOptIn() === null && await _handleOldSettings(config, telemetryOptInProvider); +} diff --git a/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js b/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js new file mode 100644 index 00000000000000..58dbe5f2ac5c94 --- /dev/null +++ b/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js @@ -0,0 +1,32 @@ +/* + * 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 chrome from 'ui/chrome'; +import moment from 'moment'; + +export function TelemetryOptInProvider($injector) { + + let currentOptInStatus = $injector.get('telemetryOptedIn'); + + return { + getOptIn: () => currentOptInStatus, + setOptIn: async (enabled) => { + const $http = $injector.get('$http'); + await $http.post(chrome.addBasePath('/api/telemetry/v1/optIn'), { enabled }); + currentOptInStatus = enabled; + + return true; + }, + fetchExample: async () => { + const $http = $injector.get('$http'); + return $http.post(chrome.addBasePath(`/api/telemetry/v1/clusters/_stats`), { + timeRange: { + min: moment().subtract(20, 'minutes').toISOString(), + max: moment().toISOString() + } + }); + } + }; +} diff --git a/x-pack/plugins/xpack_main/public/views/management/index.js b/x-pack/plugins/xpack_main/public/views/management/index.js new file mode 100644 index 00000000000000..0ed6fe09ef80a4 --- /dev/null +++ b/x-pack/plugins/xpack_main/public/views/management/index.js @@ -0,0 +1,7 @@ +/* + * 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 './management'; diff --git a/x-pack/plugins/xpack_main/public/views/management/management.js b/x-pack/plugins/xpack_main/public/views/management/management.js new file mode 100644 index 00000000000000..551f2573359859 --- /dev/null +++ b/x-pack/plugins/xpack_main/public/views/management/management.js @@ -0,0 +1,24 @@ +/* + * 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 'plugins/xpack_main/views/management/telemetry'; +import routes from 'ui/routes'; + +import { management } from 'ui/management'; + +routes.defaults(/\/management/, { + resolve: { + telemetryManagementSection: function () { + const kibanaManagementSection = management.getSection('kibana'); + + kibanaManagementSection.register('telemetry', { + order: 25, + display: 'Telemetry', + url: '#/management/kibana/telemetry' + }); + } + } +}); diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/index.js b/x-pack/plugins/xpack_main/public/views/management/telemetry/index.js new file mode 100644 index 00000000000000..e78cb86f65d637 --- /dev/null +++ b/x-pack/plugins/xpack_main/public/views/management/telemetry/index.js @@ -0,0 +1,7 @@ +/* + * 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 './telemetry'; diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js new file mode 100644 index 00000000000000..5ca0a154b80456 --- /dev/null +++ b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js @@ -0,0 +1,111 @@ +/* + * 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, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { + EuiPage, + EuiPageBody, + EuiPanel, + EuiForm, + EuiFormRow, + EuiDescribedFormGroup, + EuiSwitch, + EuiSpacer, + EuiTitle, + EuiLink, +} from '@elastic/eui'; +import { CONFIG_TELEMETRY_DESC } from '../../../../common/constants'; +import { OptInExampleFlyout } from '../../../hacks/welcome_banner/opt_in_details_component'; +import './manage_telemetry_page.less'; + +export class ManageTelemetryPage extends Component { + static propTypes = { + telemetryOptInProvider: PropTypes.object.isRequired, + } + + constructor(props) { + super(props); + this.state = { + enabled: props.telemetryOptInProvider.getOptIn(), + processing: false, + showExample: false, + }; + } + + render() { + const { + telemetryOptInProvider, + } = this.props; + + const { + showExample, + } = this.state; + + return ( + + +

Manage Telemetry

+ + + + {showExample && + telemetryOptInProvider.fetchExample()} onClose={this.toggleExample} /> + } + + + + Opt-In} + description={ + + {CONFIG_TELEMETRY_DESC} + + } + > + + + + + See an example of what we collect + + + + + +
+
+ ); + } + + toggleOptIn = async () => { + const newOptInValue = !this.state.enabled; + + this.setState({ + enabled: newOptInValue, + processing: true + }, () => { + this.props.telemetryOptInProvider.setOptIn(newOptInValue).then(() => { + this.setState({ processing: false }); + }, () => { + // something went wrong + this.setState({ processing: false, enabled: !newOptInValue }); + }); + }); + } + + toggleExample = () => { + this.setState({ + showExample: !this.state.showExample + }); + } +} diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less new file mode 100644 index 00000000000000..1c6f1e85f0962f --- /dev/null +++ b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less @@ -0,0 +1,4 @@ +.manageTelemetryPage { + background-color: #f5f5f5; + min-height: 100vh; +} diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.html b/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.html new file mode 100644 index 00000000000000..cab1f23d9744aa --- /dev/null +++ b/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.html @@ -0,0 +1,3 @@ + +
+ diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.js b/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.js new file mode 100644 index 00000000000000..052d9f22ee82bb --- /dev/null +++ b/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.js @@ -0,0 +1,35 @@ +/* + * 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 { render, unmountComponentAtNode } from 'react-dom'; + +import routes from 'ui/routes'; +import template from 'plugins/xpack_main/views/management/telemetry/telemetry.html'; + +import { TelemetryOptInProvider } from '../../../services/telemetry_opt_in'; +import { ManageTelemetryPage } from './manage_telemetry_page'; + +routes.when('/management/kibana/telemetry', { + template, + controllerAs: 'telemetryCtrl', + controller($scope, $route, kbnUrl, Private) { + + $scope.$$postDigest(() => { + const domNode = document.getElementById('telemetryReactRoot'); + + const telemetryOptInProvider = Private(TelemetryOptInProvider); + + render(, domNode); + + // unmount react on controller destroy + $scope.$on('$destroy', () => { + unmountComponentAtNode(domNode); + }); + }); + } + +}); \ No newline at end of file diff --git a/x-pack/plugins/xpack_main/server/lib/get_telemetry_opt_in.js b/x-pack/plugins/xpack_main/server/lib/get_telemetry_opt_in.js new file mode 100644 index 00000000000000..1f9d6c5849e2f4 --- /dev/null +++ b/x-pack/plugins/xpack_main/server/lib/get_telemetry_opt_in.js @@ -0,0 +1,19 @@ +/* + * 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 async function getTelemetryOptIn(request) { + const savedObjectsClient = request.getSavedObjectsClient(); + + try { + const { attributes } = await savedObjectsClient.get('telemetry', 'telemetry'); + return attributes.enabled; + } catch (error) { + if (savedObjectsClient.errors.isNotFoundError(error)) { + return null; + } + throw error; + } +} diff --git a/x-pack/plugins/xpack_main/server/lib/replace_injected_vars.js b/x-pack/plugins/xpack_main/server/lib/replace_injected_vars.js index 178795fd88ab6b..990e4e1a7d53a5 100644 --- a/x-pack/plugins/xpack_main/server/lib/replace_injected_vars.js +++ b/x-pack/plugins/xpack_main/server/lib/replace_injected_vars.js @@ -4,16 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ +import { getTelemetryOptIn } from "./get_telemetry_opt_in"; + export async function replaceInjectedVars(originalInjectedVars, request, server) { const xpackInfo = server.plugins.xpack_main.info; - const withXpackInfo = () => ({ + const withXpackInfo = async () => ({ ...originalInjectedVars, + telemetryOptedIn: await getTelemetryOptIn(request), xpackInitialInfo: xpackInfo.isAvailable() ? xpackInfo.toJSON() : undefined }); // security feature is disabled if (!server.plugins.security) { - return withXpackInfo(); + return await withXpackInfo(); } // not enough license info to make decision one way or another @@ -23,7 +26,7 @@ export async function replaceInjectedVars(originalInjectedVars, request, server) // authentication is not a thing you can do if (xpackInfo.license.isOneOf('basic')) { - return withXpackInfo(); + return await withXpackInfo(); } // request is not authenticated @@ -32,5 +35,5 @@ export async function replaceInjectedVars(originalInjectedVars, request, server) } // plugin enabled, license is appropriate, request is authenticated - return withXpackInfo(); + return await withXpackInfo(); } diff --git a/x-pack/plugins/xpack_main/server/routes/api/v1/telemetry/telemetry.js b/x-pack/plugins/xpack_main/server/routes/api/v1/telemetry/telemetry.js index 00f1695fa789ba..9700b527698f3a 100644 --- a/x-pack/plugins/xpack_main/server/routes/api/v1/telemetry/telemetry.js +++ b/x-pack/plugins/xpack_main/server/routes/api/v1/telemetry/telemetry.js @@ -17,7 +17,7 @@ import { getAllStats, getLocalStats } from '../../../../lib/telemetry'; * @param {String} end The end time of the request. * @return {Promise} An array of telemetry objects. */ -export async function getTelemetry(req, config, start, end, { _getAllStats = getAllStats, _getLocalStats = getLocalStats } = { }) { +export async function getTelemetry(req, config, start, end, { _getAllStats = getAllStats, _getLocalStats = getLocalStats } = {}) { let response = []; if (config.get('xpack.monitoring.enabled')) { @@ -26,13 +26,43 @@ export async function getTelemetry(req, config, start, end, { _getAllStats = get if (!Array.isArray(response) || response.length === 0) { // return it as an array for a consistent API response - response = [ await _getLocalStats(req) ]; + response = [await _getLocalStats(req)]; } return response; } export function telemetryRoute(server) { + /** + * Change Telemetry Opt-In preference. + */ + server.route({ + method: 'POST', + path: '/api/telemetry/v1/optIn', + config: { + validate: { + payload: Joi.object({ + enabled: Joi.bool().required() + }) + } + }, + handler: async (req, reply) => { + const savedObjectsClient = req.getSavedObjectsClient(); + try { + await savedObjectsClient.create('telemetry', { + enabled: req.payload.enabled + }, { + id: 'telemetry', + overwrite: true, + }); + } catch (err) { + return reply(wrap(err)); + } + reply({}).code(200); + } + }); + + /** * Telemetry Data * @@ -61,10 +91,10 @@ export function telemetryRoute(server) { reply(await getTelemetry(req, config, start, end)); } catch (err) { if (config.get('env.dev')) { - // don't ignore errors when running in dev mode + // don't ignore errors when running in dev mode reply(wrap(err)); } else { - // ignore errors, return empty set and a 200 + // ignore errors, return empty set and a 200 reply([]).code(200); } } From 0aa1090476971ddc7d3aa4a5d65c8786e41e746e Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 22 Aug 2018 16:33:45 -0400 Subject: [PATCH 03/18] fixed and added tests for handle_old_settings --- .../__tests__/handle_old_settings.js | 133 ++++++++++++++---- .../welcome_banner/handle_old_settings.js | 23 +-- .../public/services/telemetry_opt_in.js | 4 +- 3 files changed, 119 insertions(+), 41 deletions(-) diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js index 178bfb9cdfa2a5..8af977f2b9fea7 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js @@ -9,67 +9,138 @@ 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 + }; + + const $injector = { + get: (key) => { + if (key === '$http') { + return $http; + } + if (key === 'telemetryOptedIn') { + return enabled; + } + throw new Error(`unexpected injector usage for ${key}`); + } + }; + + return new TelemetryOptInProvider($injector, chrome); +}; describe('handle_old_settings', () => { - it('re-uses old setting and stays opted in', async () => { + 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()).to.be(null); + config.get.withArgs('xPackMonitoring:allowReport', null).returns(true); config.set.withArgs(CONFIG_TELEMETRY, true).returns(Promise.resolve(true)); - expect(await handleOldSettings(config)).to.be(false); + expect(await handleOldSettings(config, telemetryOptInProvider)).to.be(false); + + expect(config.get.calledTwice).to.be(true); + expect(config.set.called).to.be(false); - expect(config.get.calledOnce).to.be(true); - expect(config.set.calledOnce).to.be(true); - expect(config.set.getCall(0).args).to.eql([ CONFIG_TELEMETRY, true ]); - expect(config.remove.calledTwice).to.be(true); + expect(config.remove.calledThrice).to.be(true); expect(config.remove.getCall(0).args[0]).to.be('xPackMonitoring:allowReport'); expect(config.remove.getCall(1).args[0]).to.be('xPackMonitoring:showBanner'); + expect(config.remove.getCall(2).args[0]).to.be(CONFIG_TELEMETRY); + + expect(telemetryOptInProvider.getOptIn()).to.be(true); }); - it('re-uses old setting and stays opted out', async () => { + 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()).to.be(null); + config.get.withArgs('xPackMonitoring:allowReport', null).returns(false); - config.set.withArgs(CONFIG_TELEMETRY, false).returns(Promise.resolve(true)); + config.get.withArgs(CONFIG_TELEMETRY, null).returns(true); + + expect(await handleOldSettings(config, telemetryOptInProvider)).to.be(false); - expect(await handleOldSettings(config)).to.be(false); + expect(config.get.calledTwice).to.be(true); + expect(config.set.called).to.be(false); - expect(config.get.calledOnce).to.be(true); - expect(config.set.calledOnce).to.be(true); - expect(config.set.getCall(0).args).to.eql([ CONFIG_TELEMETRY, false ]); - expect(config.remove.calledTwice).to.be(true); + expect(config.remove.calledThrice).to.be(true); expect(config.remove.getCall(0).args[0]).to.be('xPackMonitoring:allowReport'); expect(config.remove.getCall(1).args[0]).to.be('xPackMonitoring:showBanner'); + expect(config.remove.getCall(2).args[0]).to.be(CONFIG_TELEMETRY); + + expect(telemetryOptInProvider.getOptIn()).to.be(true); }); - it('re-uses old setting and stays opted out', async () => { + 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()).to.be(null); + config.get.withArgs('xPackMonitoring:allowReport', null).returns(false); config.set.withArgs(CONFIG_TELEMETRY, false).returns(Promise.resolve(true)); - expect(await handleOldSettings(config)).to.be(false); + expect(await handleOldSettings(config, telemetryOptInProvider)).to.be(false); + + expect(config.get.calledTwice).to.be(true); + expect(config.set.called).to.be(false); + expect(config.remove.calledThrice).to.be(true); + expect(config.remove.getCall(0).args[0]).to.be('xPackMonitoring:allowReport'); + expect(config.remove.getCall(1).args[0]).to.be('xPackMonitoring:showBanner'); + expect(config.remove.getCall(2).args[0]).to.be(CONFIG_TELEMETRY); + + expect(telemetryOptInProvider.getOptIn()).to.be(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)).to.be(false); - expect(config.get.calledOnce).to.be(true); - expect(config.set.calledOnce).to.be(true); - expect(config.set.getCall(0).args).to.eql([ CONFIG_TELEMETRY, false ]); - expect(config.remove.calledTwice).to.be(true); + expect(config.get.calledTwice).to.be(true); + expect(config.set.called).to.be(false); + expect(config.remove.calledThrice).to.be(true); expect(config.remove.getCall(0).args[0]).to.be('xPackMonitoring:allowReport'); expect(config.remove.getCall(1).args[0]).to.be('xPackMonitoring:showBanner'); + expect(config.remove.getCall(2).args[0]).to.be(CONFIG_TELEMETRY); + + expect(telemetryOptInProvider.getOptIn()).to.be(false); }); it('acknowledges users old setting even if re-setting fails', async () => { @@ -78,15 +149,17 @@ describe('handle_old_settings', () => { 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)).to.be(false); + expect(await handleOldSettings(config, telemetryOptInProvider)).to.be(false); - expect(config.get.calledOnce).to.be(true); - expect(config.set.calledOnce).to.be(true); - expect(config.set.getCall(0).args).to.eql([ CONFIG_TELEMETRY, false ]); + expect(config.get.calledTwice).to.be(true); + expect(config.set.called).to.be(false); }); it('removes show banner setting and presents user with choice', async () => { @@ -95,12 +168,14 @@ describe('handle_old_settings', () => { 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)).to.be(true); + expect(await handleOldSettings(config, telemetryOptInProvider)).to.be(true); - expect(config.get.calledTwice).to.be(true); + expect(config.get.calledThrice).to.be(true); expect(config.remove.calledOnce).to.be(true); expect(config.remove.getCall(0).args[0]).to.be('xPackMonitoring:showBanner'); }); @@ -110,12 +185,14 @@ describe('handle_old_settings', () => { 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)).to.be(true); + expect(await handleOldSettings(config, telemetryOptInProvider)).to.be(true); - expect(config.get.calledTwice).to.be(true); + expect(config.get.calledThrice).to.be(true); }); -}); \ No newline at end of file +}); diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/handle_old_settings.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/handle_old_settings.js index 1c95901d84514e..4188676bad1e0e 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/handle_old_settings.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/handle_old_settings.js @@ -22,22 +22,23 @@ export async function handleOldSettings(config, telemetryOptInProvider) { let legacyOptInValue = null; - if (oldTelemetrySetting !== null) { + if (typeof oldTelemetrySetting === 'boolean') { legacyOptInValue = oldTelemetrySetting; - } else if (oldAllowReportSetting !== null) { + } else if (typeof oldAllowReportSetting === 'boolean') { legacyOptInValue = oldAllowReportSetting; } - console.log({ legacyOptInValue, oldAllowReportSetting, oldTelemetrySetting }); if (legacyOptInValue !== null) { - 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); - - return false; + 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); diff --git a/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js b/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js index 58dbe5f2ac5c94..7292ef2f875dc9 100644 --- a/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js +++ b/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js @@ -3,10 +3,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import chrome from 'ui/chrome'; + import moment from 'moment'; -export function TelemetryOptInProvider($injector) { +export function TelemetryOptInProvider($injector, chrome) { let currentOptInStatus = $injector.get('telemetryOptedIn'); From 13520daebb6c500982e0962ee26dc696c648bb7d Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 23 Aug 2018 08:16:47 -0400 Subject: [PATCH 04/18] fix and add replace_injected_vars tests --- .../lib/__tests__/replace_injected_vars.js | 74 +++++++++++++++++-- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/xpack_main/server/lib/__tests__/replace_injected_vars.js b/x-pack/plugins/xpack_main/server/lib/__tests__/replace_injected_vars.js index 283f3e09b9d60b..f75ec5678f1c6b 100644 --- a/x-pack/plugins/xpack_main/server/lib/__tests__/replace_injected_vars.js +++ b/x-pack/plugins/xpack_main/server/lib/__tests__/replace_injected_vars.js @@ -9,15 +9,40 @@ import expect from 'expect.js'; import { replaceInjectedVars } from '../replace_injected_vars'; +const buildRequest = (telemetryOptedIn = null) => { + const get = sinon.stub(); + if (telemetryOptedIn === null) { + get.withArgs('telemetry', 'telemetry').returns(Promise.reject(new Error('not found exception'))); + } else { + get.withArgs('telemetry', 'telemetry').returns(Promise.resolve({ attributes: { enabled: telemetryOptedIn } })); + } + + return { + getSavedObjectsClient: () => { + return { + get, + create: sinon.stub(), + + errors: { + isNotFoundError: (error) => { + return error.message === 'not found exception'; + } + } + }; + } + }; +}; + describe('replaceInjectedVars uiExport', () => { it('sends xpack info if request is authenticated and license is not basic', async () => { const originalInjectedVars = { a: 1 }; - const request = {}; + const request = buildRequest(); const server = mockServer(); const newVars = await replaceInjectedVars(originalInjectedVars, request, server); expect(newVars).to.eql({ a: 1, + telemetryOptedIn: null, xpackInitialInfo: { b: 1 } @@ -29,13 +54,14 @@ describe('replaceInjectedVars uiExport', () => { it('sends the xpack info if security plugin is disabled', async () => { const originalInjectedVars = { a: 1 }; - const request = {}; + const request = buildRequest(); const server = mockServer(); delete server.plugins.security; const newVars = await replaceInjectedVars(originalInjectedVars, request, server); expect(newVars).to.eql({ a: 1, + telemetryOptedIn: null, xpackInitialInfo: { b: 1 } @@ -44,13 +70,46 @@ describe('replaceInjectedVars uiExport', () => { it('sends the xpack info if xpack license is basic', async () => { const originalInjectedVars = { a: 1 }; - const request = {}; + const request = buildRequest(); + const server = mockServer(); + server.plugins.xpack_main.info.license.isOneOf.returns(true); + + const newVars = await replaceInjectedVars(originalInjectedVars, request, server); + expect(newVars).to.eql({ + a: 1, + telemetryOptedIn: null, + xpackInitialInfo: { + b: 1 + } + }); + }); + + it('respects the telemetry opt-in document when opted-out', async () => { + const originalInjectedVars = { a: 1 }; + const request = buildRequest(false); + const server = mockServer(); + server.plugins.xpack_main.info.license.isOneOf.returns(true); + + const newVars = await replaceInjectedVars(originalInjectedVars, request, server); + expect(newVars).to.eql({ + a: 1, + telemetryOptedIn: false, + xpackInitialInfo: { + b: 1 + } + }); + }); + + it('respects the telemetry opt-in document when opted-in', async () => { + const originalInjectedVars = { a: 1 }; + const request = buildRequest(true); const server = mockServer(); server.plugins.xpack_main.info.license.isOneOf.returns(true); const newVars = await replaceInjectedVars(originalInjectedVars, request, server); expect(newVars).to.eql({ a: 1, + telemetryOptedIn: true, xpackInitialInfo: { b: 1 } @@ -59,7 +118,7 @@ describe('replaceInjectedVars uiExport', () => { it('sends the originalInjectedVars if not authenticated', async () => { const originalInjectedVars = { a: 1 }; - const request = {}; + const request = buildRequest(); const server = mockServer(); server.plugins.security.isAuthenticated.returns(false); @@ -69,7 +128,7 @@ describe('replaceInjectedVars uiExport', () => { it('sends the originalInjectedVars if xpack info is unavailable', async () => { const originalInjectedVars = { a: 1 }; - const request = {}; + const request = buildRequest(); const server = mockServer(); server.plugins.xpack_main.info.isAvailable.returns(false); @@ -79,7 +138,7 @@ describe('replaceInjectedVars uiExport', () => { it('sends the originalInjectedVars (with xpackInitialInfo = undefined) if security is disabled, xpack info is unavailable', async () => { const originalInjectedVars = { a: 1 }; - const request = {}; + const request = buildRequest(); const server = mockServer(); delete server.plugins.security; server.plugins.xpack_main.info.isAvailable.returns(false); @@ -87,13 +146,14 @@ describe('replaceInjectedVars uiExport', () => { const newVars = await replaceInjectedVars(originalInjectedVars, request, server); expect(newVars).to.eql({ a: 1, + telemetryOptedIn: null, xpackInitialInfo: undefined }); }); it('sends the originalInjectedVars if the license check result is not available', async () => { const originalInjectedVars = { a: 1 }; - const request = {}; + const request = buildRequest(); const server = mockServer(); server.plugins.xpack_main.info.feature().getLicenseCheckResults.returns(undefined); From 5db484a45bf44f3af2fb046a6cdaec02f3843f29 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 23 Aug 2018 09:05:39 -0400 Subject: [PATCH 05/18] fix screen registry --- .../plugins/xpack_main/public/views/management/management.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/xpack_main/public/views/management/management.js b/x-pack/plugins/xpack_main/public/views/management/management.js index 551f2573359859..8383704db512af 100644 --- a/x-pack/plugins/xpack_main/public/views/management/management.js +++ b/x-pack/plugins/xpack_main/public/views/management/management.js @@ -14,6 +14,10 @@ routes.defaults(/\/management/, { telemetryManagementSection: function () { const kibanaManagementSection = management.getSection('kibana'); + if (kibanaManagementSection.hasItem('telemetry')) { + kibanaManagementSection.deregister('telemetry'); + } + kibanaManagementSection.register('telemetry', { order: 25, display: 'Telemetry', From 9bc393f504aa4276c3b30037abff5eb1ce38de07 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 23 Aug 2018 11:37:08 -0400 Subject: [PATCH 06/18] update telemetry class tests --- .../plugins/xpack_main/public/hacks/__tests__/telemetry.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/xpack_main/public/hacks/__tests__/telemetry.js b/x-pack/plugins/xpack_main/public/hacks/__tests__/telemetry.js index e027530cc08fec..20227b40359aec 100644 --- a/x-pack/plugins/xpack_main/public/hacks/__tests__/telemetry.js +++ b/x-pack/plugins/xpack_main/public/hacks/__tests__/telemetry.js @@ -13,6 +13,7 @@ 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'); const getMockInjector = ({ allowReport, lastReport }) => { @@ -21,9 +22,7 @@ const getMockInjector = ({ allowReport, lastReport }) => { get: sinon.stub().returns({ lastReport: lastReport }), set: sinon.stub() }); - get.withArgs('config').returns({ - get: () => allowReport - }); + get.withArgs('telemetryOptedIn').returns(allowReport); const mockHttp = (req) => { return req; }; From a0f9a4e42bc5fb81dd8180379c9438df8ff61752 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 23 Aug 2018 14:13:59 -0400 Subject: [PATCH 07/18] address PR design feedback --- .../hacks/welcome_banner/opt_in_details_component.js | 8 ++++---- .../management/telemetry/manage_telemetry_page.js | 10 ++++------ .../management/telemetry/manage_telemetry_page.less | 3 +-- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_details_component.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_details_component.js index f980a10604fbd4..ba9582241c2406 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_details_component.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_details_component.js @@ -58,7 +58,7 @@ export class OptInExampleFlyout extends Component { return ( - + ); @@ -79,7 +79,7 @@ export class OptInExampleFlyout extends Component { return ( - { JSON.stringify(data, null, 2) } + {JSON.stringify(data, null, 2)} ); } @@ -90,7 +90,7 @@ export class OptInExampleFlyout extends Component { @@ -104,7 +104,7 @@ export class OptInExampleFlyout extends Component { - { this.renderBody(this.state) } + {this.renderBody(this.state)} diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js index 5ca0a154b80456..e850f344f362e2 100644 --- a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js +++ b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js @@ -47,8 +47,8 @@ export class ManageTelemetryPage extends Component { return ( - -

Manage Telemetry

+ +

Manage telemetry

@@ -63,7 +63,8 @@ export class ManageTelemetryPage extends Component { title={

Opt-In

} description={ - {CONFIG_TELEMETRY_DESC} +

{CONFIG_TELEMETRY_DESC}

+

See an example of what we collect

} > @@ -75,9 +76,6 @@ export class ManageTelemetryPage extends Component { disabled={this.state.processing} /> - - See an example of what we collect - diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less index 1c6f1e85f0962f..a62044e061e968 100644 --- a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less +++ b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less @@ -1,4 +1,3 @@ .manageTelemetryPage { - background-color: #f5f5f5; - min-height: 100vh; + min-height: calc(~"100vh - 70px"); } From 5a86f9d0daed181886911ddc7a0c06aeb56c798c Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 23 Aug 2018 16:14:04 -0400 Subject: [PATCH 08/18] rename telemetry => usage data, and add link to privacy statement --- x-pack/plugins/xpack_main/common/constants.js | 5 +++++ .../hacks/welcome_banner/opt_in_banner_component.js | 10 +++++----- .../xpack_main/public/views/management/management.js | 4 ++-- .../management/telemetry/manage_telemetry_page.js | 11 ++++++++--- .../public/views/management/telemetry/telemetry.js | 2 +- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/xpack_main/common/constants.js b/x-pack/plugins/xpack_main/common/constants.js index d939038a3986b1..5b1247edd40b84 100644 --- a/x-pack/plugins/xpack_main/common/constants.js +++ b/x-pack/plugins/xpack_main/common/constants.js @@ -53,3 +53,8 @@ export const REPORT_INTERVAL_MS = 86400000; * Key for the localStorage service */ export const LOCALSTORAGE_KEY = 'xpack.data'; + +/** + * Link to the Elastic Telemetry privacy statement. + */ +export const PRIVACY_STATEMENT_URL = `https://www.elastic.co/legal/telemetry-privacy-statement`; diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_banner_component.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_banner_component.js index 805a9541191f56..53183aa4f09903 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_banner_component.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_banner_component.js @@ -17,7 +17,7 @@ import { EuiText, } from '@elastic/eui'; -import { CONFIG_TELEMETRY_DESC } from '../../../common/constants'; +import { CONFIG_TELEMETRY_DESC, PRIVACY_STATEMENT_URL } from '../../../common/constants'; import { OptInExampleFlyout } from './opt_in_details_component'; /** @@ -63,7 +63,7 @@ export class OptInBanner extends Component { )} or read our {( telemetry privacy statement @@ -84,7 +84,7 @@ export class OptInBanner extends Component { } else { title = ( - { CONFIG_TELEMETRY_DESC } {( + {CONFIG_TELEMETRY_DESC} {( this.setState({ showDetails: true })}> Read more @@ -95,8 +95,8 @@ export class OptInBanner extends Component { return ( - { details } - { flyoutDetails } + {details} + {flyoutDetails} diff --git a/x-pack/plugins/xpack_main/public/views/management/management.js b/x-pack/plugins/xpack_main/public/views/management/management.js index 8383704db512af..17c21bc90df42e 100644 --- a/x-pack/plugins/xpack_main/public/views/management/management.js +++ b/x-pack/plugins/xpack_main/public/views/management/management.js @@ -20,8 +20,8 @@ routes.defaults(/\/management/, { kibanaManagementSection.register('telemetry', { order: 25, - display: 'Telemetry', - url: '#/management/kibana/telemetry' + display: 'Usage Data', + url: '#/management/kibana/usageData' }); } } diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js index e850f344f362e2..12a054e9df0ad3 100644 --- a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js +++ b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js @@ -18,7 +18,7 @@ import { EuiTitle, EuiLink, } from '@elastic/eui'; -import { CONFIG_TELEMETRY_DESC } from '../../../../common/constants'; +import { CONFIG_TELEMETRY_DESC, PRIVACY_STATEMENT_URL } from '../../../../common/constants'; import { OptInExampleFlyout } from '../../../hacks/welcome_banner/opt_in_details_component'; import './manage_telemetry_page.less'; @@ -48,7 +48,7 @@ export class ManageTelemetryPage extends Component { return ( -

Manage telemetry

+

Manage usage data

@@ -65,12 +65,17 @@ export class ManageTelemetryPage extends Component {

{CONFIG_TELEMETRY_DESC}

See an example of what we collect

+

+ + Read our usage data privacy statement + +

} > Date: Fri, 24 Aug 2018 08:15:46 -0400 Subject: [PATCH 09/18] copy edits for usage data --- x-pack/plugins/xpack_main/common/constants.js | 2 +- .../hacks/welcome_banner/opt_in_details_component.js | 7 ++++--- .../views/management/telemetry/manage_telemetry_page.js | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/xpack_main/common/constants.js b/x-pack/plugins/xpack_main/common/constants.js index 5b1247edd40b84..d59a4ab9e5a4aa 100644 --- a/x-pack/plugins/xpack_main/common/constants.js +++ b/x-pack/plugins/xpack_main/common/constants.js @@ -14,7 +14,7 @@ export const CONFIG_TELEMETRY = 'telemetry:optIn'; * @type {string} */ export const CONFIG_TELEMETRY_DESC = ( - 'Help us improve the Elastic Stack by providing basic feature usage statistics? We will never share this data outside of Elastic.' + 'Help us improve the Elastic Stack by providing usage statistics for basic features. We will not share this data outside of Elastic.' ); /** diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_details_component.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_details_component.js index ba9582241c2406..678b30d2614661 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_details_component.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_details_component.js @@ -94,12 +94,13 @@ export class OptInExampleFlyout extends Component { > -

Cluster Statistics

+

Cluster statistics

- This is an example of the basic cluster statistics we’ll gather, which includes number of indexes, - number of shards, number of nodes, and high-level usage statistics, such as whether monitoring is enabled. + This is an example of the basic cluster statistics that we’ll collect. + It includes the number of indices, shards, and nodes. + It also includes high-level usage statistics, such as whether monitoring is turned on.
diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js index 12a054e9df0ad3..7dab148283b642 100644 --- a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js +++ b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js @@ -60,7 +60,7 @@ export class ManageTelemetryPage extends Component { Opt-In} + title={
} description={

{CONFIG_TELEMETRY_DESC}

@@ -75,7 +75,7 @@ export class ManageTelemetryPage extends Component { > Date: Fri, 24 Aug 2018 08:19:18 -0400 Subject: [PATCH 10/18] move shared component into common location --- x-pack/plugins/xpack_main/public/components/index.js | 2 ++ .../welcome_banner => components}/opt_in_details_component.js | 0 .../public/hacks/welcome_banner/opt_in_banner_component.js | 2 +- .../public/views/management/telemetry/manage_telemetry_page.js | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) rename x-pack/plugins/xpack_main/public/{hacks/welcome_banner => components}/opt_in_details_component.js (100%) diff --git a/x-pack/plugins/xpack_main/public/components/index.js b/x-pack/plugins/xpack_main/public/components/index.js index c18cf4665f4ac9..cdad9586186fba 100644 --- a/x-pack/plugins/xpack_main/public/components/index.js +++ b/x-pack/plugins/xpack_main/public/components/index.js @@ -11,3 +11,5 @@ export { AddLicense } from '../../../license_management/public/sections/license_ * For to link to management */ export { BASE_PATH as MANAGEMENT_BASE_PATH } from '../../../license_management/common/constants'; + +export { OptInExampleFlyout } from './opt_in_details_component'; \ No newline at end of file diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_details_component.js b/x-pack/plugins/xpack_main/public/components/opt_in_details_component.js similarity index 100% rename from x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_details_component.js rename to x-pack/plugins/xpack_main/public/components/opt_in_details_component.js diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_banner_component.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_banner_component.js index 53183aa4f09903..30d19f4c32478b 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_banner_component.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/opt_in_banner_component.js @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import { CONFIG_TELEMETRY_DESC, PRIVACY_STATEMENT_URL } from '../../../common/constants'; -import { OptInExampleFlyout } from './opt_in_details_component'; +import { OptInExampleFlyout } from '../../components'; /** * React component for displaying the Telemetry opt-in banner. diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js index 7dab148283b642..e7c3fcbafa1cb5 100644 --- a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js +++ b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js @@ -19,7 +19,7 @@ import { EuiLink, } from '@elastic/eui'; import { CONFIG_TELEMETRY_DESC, PRIVACY_STATEMENT_URL } from '../../../../common/constants'; -import { OptInExampleFlyout } from '../../../hacks/welcome_banner/opt_in_details_component'; +import { OptInExampleFlyout } from '../../../components'; import './manage_telemetry_page.less'; export class ManageTelemetryPage extends Component { From bbe426d2b8da1b27e6c1ca9362482ca5681c288f Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 24 Aug 2018 09:49:48 -0400 Subject: [PATCH 11/18] updated tests & fixed error handling --- .../welcome_banner/__tests__/click_banner.js | 77 +++++++++++++------ .../__tests__/handle_old_settings.js | 7 +- .../__tests__/should_show_banner.js | 40 ++++++++-- .../public/services/telemetry_opt_in.js | 12 ++- 4 files changed, 104 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/click_banner.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/click_banner.js index 21b3ad9ccafe35..aba5e7ee71e0f2 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/click_banner.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/click_banner.js @@ -6,11 +6,54 @@ import expect from 'expect.js'; 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('notifier', function mockNotifier() { this.notify = sinon.stub(); }) + .constant('telemetryOptedIn', null); -import { CONFIG_TELEMETRY } from '../../../../common/constants'; import { clickBanner, } from '../click_banner'; +import { TelemetryOptInProvider } from '../../../services/telemetry_opt_in'; + +const getMockInjector = ({ simulateFailure }) => { + const get = sinon.stub(); + + get.withArgs('telemetryOptedIn').returns(null); + get.withArgs('notifier').returns(function mockNotifier() { this.notify = 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); + + if (simulateError) { + provider.setOptIn = () => Promise.reject('unhandled error'); + } + + return provider; +}; describe('click_banner', () => { @@ -18,17 +61,15 @@ describe('click_banner', () => { const banners = { remove: sinon.spy() }; - const config = { - set: sinon.stub() - }; + + const telemetryOptInProvider = getTelemetryOptInProvider(); + const bannerId = 'bruce-banner'; const optIn = true; - config.set.withArgs(CONFIG_TELEMETRY, true).returns(Promise.resolve(true)); - - await clickBanner(bannerId, config, optIn, { _banners: banners }); + await clickBanner(bannerId, telemetryOptInProvider, optIn, { _banners: banners }); - expect(config.set.calledOnce).to.be(true); + expect(telemetryOptInProvider.getOptIn()).to.be(optIn); expect(banners.remove.calledOnce).to.be(true); expect(banners.remove.calledWith(bannerId)).to.be(true); }); @@ -40,17 +81,13 @@ describe('click_banner', () => { const banners = { remove: sinon.spy() }; - const config = { - set: sinon.stub() - }; + const telemetryOptInProvider = getTelemetryOptInProvider({ simulateFailure: true }); const bannerId = 'bruce-banner'; const optIn = true; - config.set.withArgs(CONFIG_TELEMETRY, true).returns(Promise.resolve(false)); - - await clickBanner(bannerId, config, optIn, { _banners: banners, _toastNotifications: toastNotifications }); + await clickBanner(bannerId, telemetryOptInProvider, optIn, { _banners: banners, _toastNotifications: toastNotifications }); - expect(config.set.calledOnce).to.be(true); + expect(telemetryOptInProvider.getOptIn()).to.be(null); expect(toastNotifications.addDanger.calledOnce).to.be(true); expect(banners.remove.notCalled).to.be(true); }); @@ -62,17 +99,13 @@ describe('click_banner', () => { const banners = { remove: sinon.spy() }; - const config = { - set: sinon.stub() - }; + const telemetryOptInProvider = getTelemetryOptInProvider({ simulateError: true }); const bannerId = 'bruce-banner'; const optIn = false; - config.set.withArgs(CONFIG_TELEMETRY, false).returns(Promise.reject()); - - await clickBanner(bannerId, config, optIn, { _banners: banners, _toastNotifications: toastNotifications }); + await clickBanner(bannerId, telemetryOptInProvider, optIn, { _banners: banners, _toastNotifications: toastNotifications }); - expect(config.set.calledOnce).to.be(true); + expect(telemetryOptInProvider.getOptIn()).to.be(null); expect(toastNotifications.addDanger.calledOnce).to.be(true); expect(banners.remove.notCalled).to.be(true); }); diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js index 8af977f2b9fea7..567bcc016868c4 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js @@ -33,7 +33,12 @@ const getTelemetryOptInProvider = (enabled, { simulateFailure = false } = {}) => if (key === 'telemetryOptedIn') { return enabled; } - throw new Error(`unexpected injector usage for ${key}`); + if (key === 'notifier') { + return function mockNotifier() { + this.notify = sinon.stub(); + }; + } + throw new Error(`unexpected mock injector usage for ${key}`); } }; diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/should_show_banner.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/should_show_banner.js index a215d21e7cdbf8..79f55d25acbab2 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/should_show_banner.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/should_show_banner.js @@ -9,11 +9,37 @@ import sinon from 'sinon'; import { CONFIG_TELEMETRY } from '../../../../common/constants'; import { shouldShowBanner } from '../should_show_banner'; +import { TelemetryOptInProvider } from '../../../services/telemetry_opt_in'; + +const getMockInjector = ({ telemetryEnabled }) => { + const get = sinon.stub(); + + get.withArgs('telemetryOptedIn').returns(telemetryEnabled); + get.withArgs('notifier').returns(function mockNotifier() { this.notify = sinon.stub(); }); + + const mockHttp = { + post: sinon.stub() + }; + + get.withArgs('$http').returns(mockHttp); + + return { get }; +}; + +const getTelemetryOptInProvider = ({ telemetryEnabled = null } = {}) => { + const injector = getMockInjector({ telemetryEnabled }); + 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(); @@ -21,13 +47,13 @@ describe('should_show_banner', () => { handleOldSettingsTrue.returns(Promise.resolve(true)); handleOldSettingsFalse.returns(Promise.resolve(false)); - const showBannerTrue = await shouldShowBanner(config, { _handleOldSettings: handleOldSettingsTrue }); - const showBannerFalse = await shouldShowBanner(config, { _handleOldSettings: handleOldSettingsFalse }); + const showBannerTrue = await shouldShowBanner(telemetryOptInProvider, config, { _handleOldSettings: handleOldSettingsTrue }); + const showBannerFalse = await shouldShowBanner(telemetryOptInProvider, config, { _handleOldSettings: handleOldSettingsFalse }); expect(showBannerTrue).to.be(true); expect(showBannerFalse).to.be(false); - expect(config.get.calledTwice).to.be(true); + expect(config.get.callCount).to.be(0); expect(handleOldSettingsTrue.calledOnce).to.be(true); expect(handleOldSettingsFalse.calledOnce).to.be(true); }); @@ -35,17 +61,17 @@ describe('should_show_banner', () => { it('returns false if telemetry opt-in setting is set to true', async () => { const config = { get: sinon.stub() }; - config.get.withArgs(CONFIG_TELEMETRY, null).returns(true); + const telemetryOptInProvider = getTelemetryOptInProvider({ telemetryEnabled: true }); - expect(await shouldShowBanner(config)).to.be(false); + expect(await shouldShowBanner(telemetryOptInProvider, config)).to.be(false); }); it('returns false if telemetry opt-in setting is set to false', async () => { const config = { get: sinon.stub() }; - config.get.withArgs(CONFIG_TELEMETRY, null).returns(false); + const telemetryOptInProvider = getTelemetryOptInProvider({ telemetryEnabled: false }); - expect(await shouldShowBanner(config)).to.be(false); + expect(await shouldShowBanner(telemetryOptInProvider, config)).to.be(false); }); }); \ No newline at end of file diff --git a/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js b/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js index 7292ef2f875dc9..6e1ae502cb81e2 100644 --- a/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js +++ b/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js @@ -8,14 +8,22 @@ import moment from 'moment'; export function TelemetryOptInProvider($injector, chrome) { + const Notifier = $injector.get('notifier'); + const notify = new Notifier(); let currentOptInStatus = $injector.get('telemetryOptedIn'); return { getOptIn: () => currentOptInStatus, setOptIn: async (enabled) => { const $http = $injector.get('$http'); - await $http.post(chrome.addBasePath('/api/telemetry/v1/optIn'), { enabled }); - currentOptInStatus = enabled; + + try { + await $http.post(chrome.addBasePath('/api/telemetry/v1/optIn'), { enabled }); + currentOptInStatus = enabled; + } catch (error) { + notify.error(error); + return false; + } return true; }, From 059cb2438075d31863503789a657126130f5ae83 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Mon, 27 Aug 2018 10:42:52 -0400 Subject: [PATCH 12/18] move telemetry into advanced settings ui --- .../sections/settings/advanced_settings.js | 4 +- .../components/default_component_registry.js | 3 + .../__snapshots__/page_footer.test.js.snap | 3 + .../settings/components/page_footer/index.js | 20 +++ .../components/page_footer/page_footer.js | 20 +++ .../page_footer/page_footer.test.js | 28 +++++ src/ui/public/management/index.js | 5 + .../xpack_main/public/components/index.js | 3 +- .../__snapshots__/telemetry_form.test.js.snap | 72 +++++++++++ .../opt_in_details_component.js | 0 .../components/telemetry/telemetry_form.js | 101 ++++++++++++++++ .../components/telemetry/telemetry_form.less | 3 + .../telemetry/telemetry_form.test.js | 45 +++++++ .../public/services/telemetry_opt_in.js | 2 +- .../public/views/management/management.js | 21 ++-- .../telemetry/manage_telemetry_page.js | 114 ------------------ .../telemetry/manage_telemetry_page.less | 3 - .../views/management/telemetry/telemetry.html | 3 - .../views/management/telemetry/telemetry.js | 35 ------ 19 files changed, 314 insertions(+), 171 deletions(-) create mode 100644 src/core_plugins/kibana/public/management/sections/settings/components/page_footer/__snapshots__/page_footer.test.js.snap create mode 100644 src/core_plugins/kibana/public/management/sections/settings/components/page_footer/index.js create mode 100644 src/core_plugins/kibana/public/management/sections/settings/components/page_footer/page_footer.js create mode 100644 src/core_plugins/kibana/public/management/sections/settings/components/page_footer/page_footer.test.js create mode 100644 x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/telemetry_form.test.js.snap rename x-pack/plugins/xpack_main/public/components/{ => telemetry}/opt_in_details_component.js (100%) create mode 100644 x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js create mode 100644 x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.less create mode 100644 x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.test.js delete mode 100644 x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js delete mode 100644 x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less delete mode 100644 x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.html delete mode 100644 x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.js diff --git a/src/core_plugins/kibana/public/management/sections/settings/advanced_settings.js b/src/core_plugins/kibana/public/management/sections/settings/advanced_settings.js index 2c67da99b6bf79..9b78b649cbba5a 100644 --- a/src/core_plugins/kibana/public/management/sections/settings/advanced_settings.js +++ b/src/core_plugins/kibana/public/management/sections/settings/advanced_settings.js @@ -35,7 +35,7 @@ import { Form } from './components/form'; import { getAriaName, toEditableConfig, DEFAULT_CATEGORY } from './lib'; import './advanced_settings.less'; -import { registerDefaultComponents, PAGE_TITLE_COMPONENT } from './components/default_component_registry'; +import { registerDefaultComponents, PAGE_TITLE_COMPONENT, PAGE_FOOTER_COMPONENT } from './components/default_component_registry'; import { getSettingsComponent } from './components/component_registry'; export class AdvancedSettings extends Component { @@ -137,6 +137,7 @@ export class AdvancedSettings extends Component { const { filteredSettings, query } = this.state; const PageTitle = getSettingsComponent(PAGE_TITLE_COMPONENT); + const PageFooter = getSettingsComponent(PAGE_FOOTER_COMPONENT); return (
@@ -163,6 +164,7 @@ export class AdvancedSettings extends Component { save={this.saveConfig} clear={this.clearConfig} /> +
); } diff --git a/src/core_plugins/kibana/public/management/sections/settings/components/default_component_registry.js b/src/core_plugins/kibana/public/management/sections/settings/components/default_component_registry.js index b88fb11d63bbcf..221f8c2f82bf8c 100644 --- a/src/core_plugins/kibana/public/management/sections/settings/components/default_component_registry.js +++ b/src/core_plugins/kibana/public/management/sections/settings/components/default_component_registry.js @@ -19,9 +19,12 @@ import { tryRegisterSettingsComponent } from './component_registry'; import { PageTitle } from './page_title'; +import { PageFooter } from './page_footer'; export const PAGE_TITLE_COMPONENT = 'advanced_settings_page_title'; +export const PAGE_FOOTER_COMPONENT = 'advanced_settings_page_footer'; export function registerDefaultComponents() { tryRegisterSettingsComponent(PAGE_TITLE_COMPONENT, PageTitle); + tryRegisterSettingsComponent(PAGE_FOOTER_COMPONENT, PageFooter); } \ No newline at end of file diff --git a/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/__snapshots__/page_footer.test.js.snap b/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/__snapshots__/page_footer.test.js.snap new file mode 100644 index 00000000000000..eea1003c8eb95b --- /dev/null +++ b/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/__snapshots__/page_footer.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PageFooter should render normally 1`] = `""`; diff --git a/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/index.js b/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/index.js new file mode 100644 index 00000000000000..2fae89ceb0380a --- /dev/null +++ b/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/index.js @@ -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 { PageFooter } from './page_footer'; \ No newline at end of file diff --git a/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/page_footer.js b/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/page_footer.js new file mode 100644 index 00000000000000..e55fbbae3b5f83 --- /dev/null +++ b/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/page_footer.js @@ -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 const PageFooter = () => null; \ No newline at end of file diff --git a/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/page_footer.test.js b/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/page_footer.test.js new file mode 100644 index 00000000000000..e4ac6af0a88fef --- /dev/null +++ b/src/core_plugins/kibana/public/management/sections/settings/components/page_footer/page_footer.test.js @@ -0,0 +1,28 @@ +/* + * 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 { shallow } from 'enzyme'; + +import { PageFooter } from './page_footer'; + +describe('PageFooter', () => { + it('should render normally', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); \ No newline at end of file diff --git a/src/ui/public/management/index.js b/src/ui/public/management/index.js index 616600f1ac1dec..4dfae1f9228c1e 100644 --- a/src/ui/public/management/index.js +++ b/src/ui/public/management/index.js @@ -19,6 +19,11 @@ import { ManagementSection } from './section'; +export { + PAGE_TITLE_COMPONENT, + PAGE_FOOTER_COMPONENT, +} from '../../../core_plugins/kibana/public/management/sections/settings/components/default_component_registry'; + export { registerSettingsComponent } from '../../../core_plugins/kibana/public/management/sections/settings/components/component_registry'; export const management = new ManagementSection('management', { diff --git a/x-pack/plugins/xpack_main/public/components/index.js b/x-pack/plugins/xpack_main/public/components/index.js index cdad9586186fba..c8dc260717da03 100644 --- a/x-pack/plugins/xpack_main/public/components/index.js +++ b/x-pack/plugins/xpack_main/public/components/index.js @@ -12,4 +12,5 @@ export { AddLicense } from '../../../license_management/public/sections/license_ */ export { BASE_PATH as MANAGEMENT_BASE_PATH } from '../../../license_management/common/constants'; -export { OptInExampleFlyout } from './opt_in_details_component'; \ No newline at end of file +export { TelemetryForm } from './telemetry/telemetry_form'; +export { OptInExampleFlyout } from './telemetry/opt_in_details_component'; \ No newline at end of file diff --git a/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/telemetry_form.test.js.snap b/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/telemetry_form.test.js.snap new file mode 100644 index 00000000000000..6e9bf4d2fc8504 --- /dev/null +++ b/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/telemetry_form.test.js.snap @@ -0,0 +1,72 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TelemetryForm renders as expected 1`] = ` + + + +

+ Usage Data +

+
+ + +

+ Help us improve the Elastic Stack by providing usage statistics for basic features. We will not share this data outside of Elastic. +

+

+ + See an example of what we collect + +

+

+ + Read our usage data privacy statement + +

+ + } + fullWidth={false} + gutterSize="l" + idAria="manage-telemetry-aria" + title={
} + titleSize="xs" + > + + + + + + +`; diff --git a/x-pack/plugins/xpack_main/public/components/opt_in_details_component.js b/x-pack/plugins/xpack_main/public/components/telemetry/opt_in_details_component.js similarity index 100% rename from x-pack/plugins/xpack_main/public/components/opt_in_details_component.js rename to x-pack/plugins/xpack_main/public/components/telemetry/opt_in_details_component.js diff --git a/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js new file mode 100644 index 00000000000000..b1b8637eaea207 --- /dev/null +++ b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js @@ -0,0 +1,101 @@ +/* + * 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, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { + EuiPanel, + EuiDescribedFormGroup, + EuiLink, + EuiFormRow, + EuiSwitch, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { CONFIG_TELEMETRY_DESC, PRIVACY_STATEMENT_URL } from '../../../common/constants'; +import { OptInExampleFlyout } from './opt_in_details_component'; +import './telemetry_form.less'; + +export class TelemetryForm extends Component { + static propTypes = { + telemetryOptInProvider: PropTypes.object.isRequired, + }; + + state = { + processing: false, + showExample: false, + } + + render() { + const { + telemetryOptInProvider + } = this.props; + + const { + showExample + } = this.state; + + return ( + + {showExample && + telemetryOptInProvider.fetchExample()} onClose={this.toggleExample} /> + } + +
+

Usage Data

+ + } + description={ + +

{CONFIG_TELEMETRY_DESC}

+

See an example of what we collect

+

+ + Read our usage data privacy statement + +

+
+ } + > + + + +
+
+
+
+ ); + } + + toggleOptIn = async () => { + const newOptInValue = !this.props.telemetryOptInProvider.getOptIn(); + + this.setState({ + enabled: newOptInValue, + processing: true + }, () => { + this.props.telemetryOptInProvider.setOptIn(newOptInValue).then(() => { + this.setState({ processing: false }); + }, () => { + // something went wrong + this.setState({ processing: false }); + }); + }); + } + + toggleExample = () => { + this.setState({ + showExample: !this.state.showExample + }); + } +} \ No newline at end of file diff --git a/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.less b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.less new file mode 100644 index 00000000000000..041b0c9461e238 --- /dev/null +++ b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.less @@ -0,0 +1,3 @@ +.telemetryForm { + margin: 10px 6px 6px 6px; +} \ No newline at end of file diff --git a/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.test.js b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.test.js new file mode 100644 index 00000000000000..b3e8fdb5ed04c7 --- /dev/null +++ b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.test.js @@ -0,0 +1,45 @@ +/* + * 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 { shallow } from 'enzyme'; +import { TelemetryForm } from './telemetry_form'; +import { TelemetryOptInProvider } from '../../services/telemetry_opt_in'; + +const buildTelemetryOptInProvider = () => { + const mockHttp = { + post: jest.fn() + }; + + function mockNotifier() { + this.notify = jest.fn(); + } + + const mockInjector = { + get: (key) => { + switch (key) { + case '$http': + return mockHttp; + case 'Notifier': + return mockNotifier; + default: + return null; + } + } + }; + + const chrome = { + addBasePath: (url) => url + }; + + return new TelemetryOptInProvider(mockInjector, chrome); +}; + +describe('TelemetryForm', () => { + it('renders as expected', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); \ No newline at end of file diff --git a/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js b/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js index 6e1ae502cb81e2..b4070e4587f87d 100644 --- a/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js +++ b/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.js @@ -8,7 +8,7 @@ import moment from 'moment'; export function TelemetryOptInProvider($injector, chrome) { - const Notifier = $injector.get('notifier'); + const Notifier = $injector.get('Notifier'); const notify = new Notifier(); let currentOptInStatus = $injector.get('telemetryOptedIn'); diff --git a/x-pack/plugins/xpack_main/public/views/management/management.js b/x-pack/plugins/xpack_main/public/views/management/management.js index 17c21bc90df42e..09285ee9f24294 100644 --- a/x-pack/plugins/xpack_main/public/views/management/management.js +++ b/x-pack/plugins/xpack_main/public/views/management/management.js @@ -3,26 +3,21 @@ * 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 'plugins/xpack_main/views/management/telemetry'; import routes from 'ui/routes'; -import { management } from 'ui/management'; +import { registerSettingsComponent, PAGE_FOOTER_COMPONENT } from 'ui/management'; +import { TelemetryOptInProvider } from '../../services/telemetry_opt_in'; +import { TelemetryForm } from '../../components'; routes.defaults(/\/management/, { resolve: { - telemetryManagementSection: function () { - const kibanaManagementSection = management.getSection('kibana'); - - if (kibanaManagementSection.hasItem('telemetry')) { - kibanaManagementSection.deregister('telemetry'); - } + telemetryManagementSection: function (Private) { + const telemetryOptInProvider = Private(TelemetryOptInProvider); + const Component = () => ; - kibanaManagementSection.register('telemetry', { - order: 25, - display: 'Usage Data', - url: '#/management/kibana/usageData' - }); + registerSettingsComponent(PAGE_FOOTER_COMPONENT, Component, true); } } }); diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js deleted file mode 100644 index e7c3fcbafa1cb5..00000000000000 --- a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.js +++ /dev/null @@ -1,114 +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, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; -import { - EuiPage, - EuiPageBody, - EuiPanel, - EuiForm, - EuiFormRow, - EuiDescribedFormGroup, - EuiSwitch, - EuiSpacer, - EuiTitle, - EuiLink, -} from '@elastic/eui'; -import { CONFIG_TELEMETRY_DESC, PRIVACY_STATEMENT_URL } from '../../../../common/constants'; -import { OptInExampleFlyout } from '../../../components'; -import './manage_telemetry_page.less'; - -export class ManageTelemetryPage extends Component { - static propTypes = { - telemetryOptInProvider: PropTypes.object.isRequired, - } - - constructor(props) { - super(props); - this.state = { - enabled: props.telemetryOptInProvider.getOptIn(), - processing: false, - showExample: false, - }; - } - - render() { - const { - telemetryOptInProvider, - } = this.props; - - const { - showExample, - } = this.state; - - return ( - - -

Manage usage data

- - - - {showExample && - telemetryOptInProvider.fetchExample()} onClose={this.toggleExample} /> - } - - - - } - description={ - -

{CONFIG_TELEMETRY_DESC}

-

See an example of what we collect

-

- - Read our usage data privacy statement - -

-
- } - > - - - -
-
-
- -
-
- ); - } - - toggleOptIn = async () => { - const newOptInValue = !this.state.enabled; - - this.setState({ - enabled: newOptInValue, - processing: true - }, () => { - this.props.telemetryOptInProvider.setOptIn(newOptInValue).then(() => { - this.setState({ processing: false }); - }, () => { - // something went wrong - this.setState({ processing: false, enabled: !newOptInValue }); - }); - }); - } - - toggleExample = () => { - this.setState({ - showExample: !this.state.showExample - }); - } -} diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less b/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less deleted file mode 100644 index a62044e061e968..00000000000000 --- a/x-pack/plugins/xpack_main/public/views/management/telemetry/manage_telemetry_page.less +++ /dev/null @@ -1,3 +0,0 @@ -.manageTelemetryPage { - min-height: calc(~"100vh - 70px"); -} diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.html b/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.html deleted file mode 100644 index cab1f23d9744aa..00000000000000 --- a/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.html +++ /dev/null @@ -1,3 +0,0 @@ - -
- diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.js b/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.js deleted file mode 100644 index faf746305c2dc2..00000000000000 --- a/x-pack/plugins/xpack_main/public/views/management/telemetry/telemetry.js +++ /dev/null @@ -1,35 +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 { render, unmountComponentAtNode } from 'react-dom'; - -import routes from 'ui/routes'; -import template from 'plugins/xpack_main/views/management/telemetry/telemetry.html'; - -import { TelemetryOptInProvider } from '../../../services/telemetry_opt_in'; -import { ManageTelemetryPage } from './manage_telemetry_page'; - -routes.when('/management/kibana/usageData', { - template, - controllerAs: 'telemetryCtrl', - controller($scope, $route, kbnUrl, Private) { - - $scope.$$postDigest(() => { - const domNode = document.getElementById('telemetryReactRoot'); - - const telemetryOptInProvider = Private(TelemetryOptInProvider); - - render(, domNode); - - // unmount react on controller destroy - $scope.$on('$destroy', () => { - unmountComponentAtNode(domNode); - }); - }); - } - -}); \ No newline at end of file From 448adc8113f37022c95c97e8fa148a0e77620395 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 28 Aug 2018 14:29:06 -0400 Subject: [PATCH 13/18] shim usage data into advanced settings search results --- .../advanced_settings.test.js.snap | 60 +++++++++++++++++++ .../sections/settings/advanced_settings.js | 13 +++- .../form/__snapshots__/form.test.js.snap | 2 + .../sections/settings/components/form/form.js | 22 ++++--- .../settings/components/form/form.test.js | 24 +++++++- .../components/telemetry/telemetry_form.js | 31 +++++++++- .../telemetry/telemetry_form.test.js | 4 +- .../public/views/management/management.js | 3 +- .../views/management/telemetry/index.js | 7 --- 9 files changed, 142 insertions(+), 24 deletions(-) delete mode 100644 x-pack/plugins/xpack_main/public/views/management/telemetry/index.js diff --git a/src/core_plugins/kibana/public/management/sections/settings/__snapshots__/advanced_settings.test.js.snap b/src/core_plugins/kibana/public/management/sections/settings/__snapshots__/advanced_settings.test.js.snap index f219c199b1d870..85abe0571d1167 100644 --- a/src/core_plugins/kibana/public/management/sections/settings/__snapshots__/advanced_settings.test.js.snap +++ b/src/core_plugins/kibana/public/management/sections/settings/__snapshots__/advanced_settings.test.js.snap @@ -302,6 +302,27 @@ exports[`AdvancedSettings should render normally 1`] = ` ], } } + showNoResultsMessage={true} + /> +
`; @@ -420,6 +441,45 @@ exports[`AdvancedSettings should render specific setting if given setting key 1` ], } } + showNoResultsMessage={true} + /> +
`; diff --git a/src/core_plugins/kibana/public/management/sections/settings/advanced_settings.js b/src/core_plugins/kibana/public/management/sections/settings/advanced_settings.js index 9b78b649cbba5a..7ce4341f59ed8e 100644 --- a/src/core_plugins/kibana/public/management/sections/settings/advanced_settings.js +++ b/src/core_plugins/kibana/public/management/sections/settings/advanced_settings.js @@ -51,6 +51,7 @@ export class AdvancedSettings extends Component { this.init(config); this.state = { query: parsedQuery, + footerQueryMatched: false, filteredSettings: this.mapSettings(Query.execute(parsedQuery, this.settings)), }; @@ -129,12 +130,19 @@ export class AdvancedSettings extends Component { clearQuery = () => { this.setState({ query: Query.parse(''), + footerQueryMatched: false, filteredSettings: this.groupedSettings, }); } + onFooterQueryMatchChange = (matched) => { + this.setState({ + footerQueryMatched: matched + }); + } + render() { - const { filteredSettings, query } = this.state; + const { filteredSettings, query, footerQueryMatched } = this.state; const PageTitle = getSettingsComponent(PAGE_TITLE_COMPONENT); const PageFooter = getSettingsComponent(PAGE_FOOTER_COMPONENT); @@ -163,8 +171,9 @@ export class AdvancedSettings extends Component { clearQuery={this.clearQuery} save={this.saveConfig} clear={this.clearConfig} + showNoResultsMessage={!footerQueryMatched} /> - +
); } diff --git a/src/core_plugins/kibana/public/management/sections/settings/components/form/__snapshots__/form.test.js.snap b/src/core_plugins/kibana/public/management/sections/settings/components/form/__snapshots__/form.test.js.snap index 627f5d864d1d2a..7d51699e975e74 100644 --- a/src/core_plugins/kibana/public/management/sections/settings/components/form/__snapshots__/form.test.js.snap +++ b/src/core_plugins/kibana/public/management/sections/settings/components/form/__snapshots__/form.test.js.snap @@ -1,5 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Form should not render no settings message when instructed not to 1`] = ``; + exports[`Form should render no settings message when there are no settings 1`] = ` @@ -95,12 +96,23 @@ export class Form extends PureComponent { ); } + maybeRenderNoSettings(clearQuery) { + if (this.props.showNoResultsMessage) { + return ( + + No settings found (Clear search) + + ); + } + return null; + } + render() { const { settings, categories, categoryCounts, clearQuery } = this.props; const currentCategories = []; categories.forEach(category => { - if(settings[category] && settings[category].length) { + if (settings[category] && settings[category].length) { currentCategories.push(category); } }); @@ -112,11 +124,7 @@ export class Form extends PureComponent { return ( this.renderCategory(category, settings[category], categoryCounts[category]) // fix this ); - }) : ( - - No settings found (Clear search) - - ) + }) : this.maybeRenderNoSettings(clearQuery) }
); diff --git a/src/core_plugins/kibana/public/management/sections/settings/components/form/form.test.js b/src/core_plugins/kibana/public/management/sections/settings/components/form/form.test.js index cd3b3f2db5fb3e..fddaae79ec44e2 100644 --- a/src/core_plugins/kibana/public/management/sections/settings/components/form/form.test.js +++ b/src/core_plugins/kibana/public/management/sections/settings/components/form/form.test.js @@ -69,9 +69,9 @@ const categoryCounts = { dashboard: 1, 'x-pack': 10, }; -const save = () => {}; -const clear = () => {}; -const clearQuery = () => {}; +const save = () => { }; +const clear = () => { }; +const clearQuery = () => { }; describe('Form', () => { it('should render normally', async () => { @@ -83,6 +83,7 @@ describe('Form', () => { save={save} clear={clear} clearQuery={clearQuery} + showNoResultsMessage={true} /> ); @@ -98,6 +99,23 @@ describe('Form', () => { save={save} clear={clear} clearQuery={clearQuery} + showNoResultsMessage={true} + /> + ); + + expect(component).toMatchSnapshot(); + }); + + it('should not render no settings message when instructed not to', async () => { + const component = shallow( +
); diff --git a/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js index b1b8637eaea207..7fbe4373c1d267 100644 --- a/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js +++ b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js @@ -19,25 +19,52 @@ import { CONFIG_TELEMETRY_DESC, PRIVACY_STATEMENT_URL } from '../../../common/co import { OptInExampleFlyout } from './opt_in_details_component'; import './telemetry_form.less'; +const SEARCH_TERMS = ['telemetry', 'usage', 'data', 'usage data']; + export class TelemetryForm extends Component { static propTypes = { telemetryOptInProvider: PropTypes.object.isRequired, + query: PropTypes.object, + onQueryMatchChange: PropTypes.func.isRequired, }; state = { processing: false, showExample: false, + queryMatches: null, + } + + componentWillReceiveProps(nextProps) { + const { + query + } = nextProps; + + const searchTerm = (query.text || '').toLowerCase(); + const searchTermMatches = SEARCH_TERMS.some(term => term.indexOf(searchTerm) >= 0); + + if (searchTermMatches !== this.state.queryMatches) { + this.setState({ + queryMatches: searchTermMatches + }, () => { + this.props.onQueryMatchChange(searchTermMatches); + }); + } } render() { const { - telemetryOptInProvider + telemetryOptInProvider, } = this.props; const { - showExample + showExample, + queryMatches, } = this.state; + if (queryMatches !== null && !queryMatches) { + return null; + } + return ( {showExample && diff --git a/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.test.js b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.test.js index b3e8fdb5ed04c7..53717aa0b15a25 100644 --- a/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.test.js +++ b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.test.js @@ -40,6 +40,8 @@ const buildTelemetryOptInProvider = () => { describe('TelemetryForm', () => { it('renders as expected', () => { - expect(shallow()).toMatchSnapshot(); + expect(shallow( + ) + ).toMatchSnapshot(); }); }); \ No newline at end of file diff --git a/x-pack/plugins/xpack_main/public/views/management/management.js b/x-pack/plugins/xpack_main/public/views/management/management.js index 09285ee9f24294..8c244f8ae933fd 100644 --- a/x-pack/plugins/xpack_main/public/views/management/management.js +++ b/x-pack/plugins/xpack_main/public/views/management/management.js @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import 'plugins/xpack_main/views/management/telemetry'; import routes from 'ui/routes'; import { registerSettingsComponent, PAGE_FOOTER_COMPONENT } from 'ui/management'; @@ -15,7 +14,7 @@ routes.defaults(/\/management/, { resolve: { telemetryManagementSection: function (Private) { const telemetryOptInProvider = Private(TelemetryOptInProvider); - const Component = () => ; + const Component = (props) => ; registerSettingsComponent(PAGE_FOOTER_COMPONENT, Component, true); } diff --git a/x-pack/plugins/xpack_main/public/views/management/telemetry/index.js b/x-pack/plugins/xpack_main/public/views/management/telemetry/index.js deleted file mode 100644 index e78cb86f65d637..00000000000000 --- a/x-pack/plugins/xpack_main/public/views/management/telemetry/index.js +++ /dev/null @@ -1,7 +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 './telemetry'; From 524c3b0fbd71f80896fc8a09455101e9a257e971 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 28 Aug 2018 17:01:22 -0400 Subject: [PATCH 14/18] fix tests --- .../public/hacks/welcome_banner/__tests__/click_banner.js | 4 ++-- .../hacks/welcome_banner/__tests__/handle_old_settings.js | 2 +- .../hacks/welcome_banner/__tests__/should_show_banner.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/click_banner.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/click_banner.js index aba5e7ee71e0f2..3ef092bde18ec1 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/click_banner.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/click_banner.js @@ -11,7 +11,7 @@ import { uiModules } from 'ui/modules'; uiModules.get('kibana') // disable stat reporting while running tests, // MockInjector used in these tests is not impacted - .constant('notifier', function mockNotifier() { this.notify = sinon.stub(); }) + .constant('Notifier', function mockNotifier() { this.notify = sinon.stub(); }) .constant('telemetryOptedIn', null); import { @@ -23,7 +23,7 @@ const getMockInjector = ({ simulateFailure }) => { const get = sinon.stub(); get.withArgs('telemetryOptedIn').returns(null); - get.withArgs('notifier').returns(function mockNotifier() { this.notify = sinon.stub(); }); + get.withArgs('Notifier').returns(function mockNotifier() { this.notify = sinon.stub(); }); const mockHttp = { post: sinon.stub() diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js index 567bcc016868c4..3ceb31cb2eb309 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/handle_old_settings.js @@ -33,7 +33,7 @@ const getTelemetryOptInProvider = (enabled, { simulateFailure = false } = {}) => if (key === 'telemetryOptedIn') { return enabled; } - if (key === 'notifier') { + if (key === 'Notifier') { return function mockNotifier() { this.notify = sinon.stub(); }; diff --git a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/should_show_banner.js b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/should_show_banner.js index 79f55d25acbab2..33fde83241f8a7 100644 --- a/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/should_show_banner.js +++ b/x-pack/plugins/xpack_main/public/hacks/welcome_banner/__tests__/should_show_banner.js @@ -15,7 +15,7 @@ const getMockInjector = ({ telemetryEnabled }) => { const get = sinon.stub(); get.withArgs('telemetryOptedIn').returns(telemetryEnabled); - get.withArgs('notifier').returns(function mockNotifier() { this.notify = sinon.stub(); }); + get.withArgs('Notifier').returns(function mockNotifier() { this.notify = sinon.stub(); }); const mockHttp = { post: sinon.stub() From 8125923d3a33b96e72a2fcfd34c722c6574db83b Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 29 Aug 2018 07:20:45 -0400 Subject: [PATCH 15/18] fix snapshot --- .../__snapshots__/telemetry_form.test.js.snap | 116 +++++++++--------- 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/telemetry_form.test.js.snap b/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/telemetry_form.test.js.snap index 6e9bf4d2fc8504..441f5ba2a0dce7 100644 --- a/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/telemetry_form.test.js.snap +++ b/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/telemetry_form.test.js.snap @@ -7,66 +7,70 @@ exports[`TelemetryForm renders as expected 1`] = ` hasShadow={false} paddingSize="m" > - -

- Usage Data -

-
- - -

- Help us improve the Elastic Stack by providing usage statistics for basic features. We will not share this data outside of Elastic. -

-

- - See an example of what we collect - -

-

- - Read our usage data privacy statement - -

- - } - fullWidth={false} - gutterSize="l" - idAria="manage-telemetry-aria" - title={
} - titleSize="xs" - > - +

+ Usage Data +

+ + + +

+ Help us improve the Elastic Stack by providing usage statistics for basic features. We will not share this data outside of Elastic. +

+

+ + See an example of what we collect + +

+

+ + Read our usage data privacy statement + +

+ } fullWidth={false} - hasEmptyLabelSpace={true} + gutterSize="l" + idAria="manage-telemetry-aria" + title={
} + titleSize="xs" > - - - + + + + +
`; From 36e0ac0064fcfd16776eec904fbc6f8b5311af3e Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 29 Aug 2018 09:07:59 -0400 Subject: [PATCH 16/18] update expected privilege/action mapping --- x-pack/test/rbac_api_integration/apis/privileges/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/test/rbac_api_integration/apis/privileges/index.js b/x-pack/test/rbac_api_integration/apis/privileges/index.js index 9563a1b65540f0..12ca92f3fe33c3 100644 --- a/x-pack/test/rbac_api_integration/apis/privileges/index.js +++ b/x-pack/test/rbac_api_integration/apis/privileges/index.js @@ -35,6 +35,9 @@ export default function ({ getService }) { 'action:saved_objects/timelion-sheet/get', 'action:saved_objects/timelion-sheet/bulk_get', 'action:saved_objects/timelion-sheet/find', + 'action:saved_objects/telemetry/get', + 'action:saved_objects/telemetry/bulk_get', + 'action:saved_objects/telemetry/find', 'action:saved_objects/graph-workspace/get', 'action:saved_objects/graph-workspace/bulk_get', 'action:saved_objects/graph-workspace/find', From b26d4301221b5480a088d8972fe653143cb7d590 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 30 Aug 2018 14:35:22 -0400 Subject: [PATCH 17/18] additional UI tests --- .../opt_in_details_component.test.js.snap | 56 ++++++++++++ .../opt_in_details_component.test.js | 14 +++ .../public/services/telemetry_opt_in.test.js | 89 +++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/opt_in_details_component.test.js.snap create mode 100644 x-pack/plugins/xpack_main/public/components/telemetry/opt_in_details_component.test.js create mode 100644 x-pack/plugins/xpack_main/public/services/telemetry_opt_in.test.js diff --git a/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/opt_in_details_component.test.js.snap b/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/opt_in_details_component.test.js.snap new file mode 100644 index 00000000000000..6c0bdc798b593d --- /dev/null +++ b/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/opt_in_details_component.test.js.snap @@ -0,0 +1,56 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`OptInDetailsComponent should render normally 1`] = ` + + + + +

+ Cluster statistics +

+
+ + + This is an example of the basic cluster statistics that we’ll collect. It includes the number of indices, shards, and nodes. It also includes high-level usage statistics, such as whether monitoring is turned on. + + +
+ + + + + + + +
+
+`; diff --git a/x-pack/plugins/xpack_main/public/components/telemetry/opt_in_details_component.test.js b/x-pack/plugins/xpack_main/public/components/telemetry/opt_in_details_component.test.js new file mode 100644 index 00000000000000..a649f932e025fa --- /dev/null +++ b/x-pack/plugins/xpack_main/public/components/telemetry/opt_in_details_component.test.js @@ -0,0 +1,14 @@ +/* + * 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 { shallow } from 'enzyme'; +import { OptInExampleFlyout } from './opt_in_details_component'; + +describe('OptInDetailsComponent', () => { + it('renders as expected', () => { + expect(shallow( ({ data: [] }))} onClose={jest.fn()} />)).toMatchSnapshot(); + }); +}); \ No newline at end of file diff --git a/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.test.js b/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.test.js new file mode 100644 index 00000000000000..60c08e5a338e56 --- /dev/null +++ b/x-pack/plugins/xpack_main/public/services/telemetry_opt_in.test.js @@ -0,0 +1,89 @@ +/* + * 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 { TelemetryOptInProvider } from "./telemetry_opt_in"; + +describe('TelemetryOptInProvider', () => { + const setup = ({ optedIn, simulatePostError }) => { + const mockHttp = { + post: jest.fn(async () => { + if (simulatePostError) { + return Promise.reject("Something happened"); + } + }) + }; + + const mockChrome = { + addBasePath: (url) => url + }; + + class MockNotifier { + constructor() { + this.error = jest.fn(); + } + } + + const mockInjector = { + get: (key) => { + switch (key) { + case 'telemetryOptedIn': { + return optedIn; + } + case 'Notifier': { + return MockNotifier; + } + case '$http': { + return mockHttp; + } + default: + throw new Error('unexpected injector request: ' + key); + } + } + }; + + const provider = new TelemetryOptInProvider(mockInjector, mockChrome); + 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/v1/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/v1/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/v1/optIn`, { enabled: true }); + + // opt-in change should not be reflected + expect(provider.getOptIn()).toEqual(false); + }); +}); \ No newline at end of file From 19625a0ae11cd3de955af0a513dbeb963347c0c9 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 30 Aug 2018 16:51:43 -0400 Subject: [PATCH 18/18] render telemetry setting using common Field component --- .../settings/components/field/field.js | 60 +++++---- src/ui/public/management/index.js | 2 + .../opt_in_details_component.test.js.snap | 2 +- .../__snapshots__/telemetry_form.test.js.snap | 114 +++++++++--------- .../components/telemetry/telemetry_form.js | 90 ++++++++------ 5 files changed, 144 insertions(+), 124 deletions(-) diff --git a/src/core_plugins/kibana/public/management/sections/settings/components/field/field.js b/src/core_plugins/kibana/public/management/sections/settings/components/field/field.js index 16ea544028c83b..a4ed24873f4d9d 100644 --- a/src/core_plugins/kibana/public/management/sections/settings/components/field/field.js +++ b/src/core_plugins/kibana/public/management/sections/settings/components/field/field.js @@ -87,7 +87,7 @@ export class Field extends PureComponent { getEditableValue(type, value, defVal) { const val = (value === null || value === undefined) ? defVal : value; - switch(type) { + switch (type) { case 'array': return val.join(', '); case 'boolean': @@ -102,10 +102,10 @@ export class Field extends PureComponent { } getDisplayedDefaultValue(type, defVal) { - if(defVal === undefined || defVal === null || defVal === '') { + if (defVal === undefined || defVal === null || defVal === '') { return 'null'; } - switch(type) { + switch (type) { case 'array': return defVal.join(', '); default: @@ -193,7 +193,7 @@ export class Field extends PureComponent { } onImageChange = async (files) => { - if(!files.length) { + if (!files.length) { this.clearError(); this.setState({ unsavedValue: null, @@ -212,18 +212,18 @@ export class Field extends PureComponent { changeImage: true, unsavedValue: base64Image, }); - } catch(err) { + } catch (err) { toastNotifications.addDanger('Image could not be saved'); this.cancelChangeImage(); } } getImageAsBase64(file) { - if(!file instanceof File) { + if (!file instanceof File) { return null; } - const reader = new FileReader(); + const reader = new FileReader(); reader.readAsDataURL(file); return new Promise((resolve, reject) => { @@ -245,7 +245,7 @@ export class Field extends PureComponent { cancelChangeImage = () => { const { savedValue } = this.state; - if(this.changeImageForm) { + if (this.changeImageForm) { this.changeImageForm.fileInput.value = null; this.changeImageForm.handleChange(); } @@ -268,14 +268,14 @@ export class Field extends PureComponent { const { name, defVal, type } = this.props.setting; const { changeImage, savedValue, unsavedValue, isJsonArray } = this.state; - if(savedValue === unsavedValue) { + if (savedValue === unsavedValue) { return; } let valueToSave = unsavedValue; let isSameValue = false; - switch(type) { + switch (type) { case 'array': valueToSave = valueToSave.split(',').map(val => val.trim()); isSameValue = valueToSave.join(',') === defVal.join(','); @@ -295,10 +295,10 @@ export class Field extends PureComponent { await this.props.save(name, valueToSave); } - if(changeImage) { + if (changeImage) { this.cancelChangeImage(); } - } catch(e) { + } catch (e) { toastNotifications.addDanger(`Unable to save ${name}`); } this.setLoading(false); @@ -311,7 +311,7 @@ export class Field extends PureComponent { await this.props.clear(name); this.cancelChangeImage(); this.clearError(); - } catch(e) { + } catch (e) { toastNotifications.addDanger(`Unable to reset ${name}`); } this.setLoading(false); @@ -321,7 +321,7 @@ export class Field extends PureComponent { const { loading, changeImage, unsavedValue } = this.state; const { name, value, type, options, isOverridden } = setting; - switch(type) { + switch (type) { case 'boolean': return ( ); case 'image': - if(!isDefaultValue(setting) && !changeImage) { + if (!isDefaultValue(setting) && !changeImage) { return ( {setting.name} @@ -438,7 +438,7 @@ export class Field extends PureComponent { const defaultLink = this.renderResetToDefaultLink(setting); const imageLink = this.renderChangeImageLink(setting); - if(defaultLink || imageLink) { + if (defaultLink || imageLink) { return ( {defaultLink} @@ -462,8 +462,12 @@ export class Field extends PureComponent { } renderDescription(setting) { - return ( - + let description; + + if (React.isValidElement(setting.description)) { + description = setting.description; + } else { + description = (
+ ); + } + + return ( + + {description} {this.renderDefaultValue(setting)} ); @@ -478,14 +488,14 @@ export class Field extends PureComponent { renderDefaultValue(setting) { const { type, defVal } = setting; - if(isDefaultValue(setting)) { + if (isDefaultValue(setting)) { return; } return ( - { type === 'json' ? ( + {type === 'json' ? ( Default: ) : ( - Default: {this.getDisplayedDefaultValue(type, defVal)} + Default: {this.getDisplayedDefaultValue(type, defVal)} - ) } + )} ); @@ -508,7 +518,7 @@ export class Field extends PureComponent { renderResetToDefaultLink(setting) { const { ariaName, name } = setting; - if(isDefaultValue(setting)) { + if (isDefaultValue(setting)) { return; } return ( @@ -528,7 +538,7 @@ export class Field extends PureComponent { renderChangeImageLink(setting) { const { changeImage } = this.state; const { type, value, ariaName, name } = setting; - if(type !== 'image' || !value || changeImage) { + if (type !== 'image' || !value || changeImage) { return; } return ( diff --git a/src/ui/public/management/index.js b/src/ui/public/management/index.js index 4dfae1f9228c1e..62f9850c839f5e 100644 --- a/src/ui/public/management/index.js +++ b/src/ui/public/management/index.js @@ -26,6 +26,8 @@ export { export { registerSettingsComponent } from '../../../core_plugins/kibana/public/management/sections/settings/components/component_registry'; +export { Field } from '../../../core_plugins/kibana/public/management/sections/settings/components/field/field'; + export const management = new ManagementSection('management', { display: 'Management' }); diff --git a/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/opt_in_details_component.test.js.snap b/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/opt_in_details_component.test.js.snap index 6c0bdc798b593d..19a9bbb169b350 100644 --- a/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/opt_in_details_component.test.js.snap +++ b/x-pack/plugins/xpack_main/public/components/telemetry/__snapshots__/opt_in_details_component.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`OptInDetailsComponent should render normally 1`] = ` +exports[`OptInDetailsComponent renders as expected 1`] = ` -
+ -

- Usage Data -

+ + +

+ Usage Data +

+
+
- -

- Help us improve the Elastic Stack by providing usage statistics for basic features. We will not share this data outside of Elastic. -

-

- - See an example of what we collect - -

-

- - Read our usage data privacy statement - -

- - } - fullWidth={false} - gutterSize="l" - idAria="manage-telemetry-aria" - title={
} - titleSize="xs" - > - +

+ Help us improve the Elastic Stack by providing usage statistics for basic features. We will not share this data outside of Elastic. +

+

+ + See an example of what we collect + +

+

+ + Read our usage data privacy statement + +

+ , + "type": "boolean", + "value": false, } - fullWidth={false} - hasEmptyLabelSpace={true} - > - -
- -
+ } + /> +
`; diff --git a/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js index 7fbe4373c1d267..1a7826f56d0d2f 100644 --- a/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js +++ b/x-pack/plugins/xpack_main/public/components/telemetry/telemetry_form.js @@ -8,16 +8,17 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { EuiPanel, - EuiDescribedFormGroup, + EuiForm, + EuiFlexGroup, + EuiFlexItem, EuiLink, - EuiFormRow, - EuiSwitch, EuiSpacer, EuiText, } from '@elastic/eui'; import { CONFIG_TELEMETRY_DESC, PRIVACY_STATEMENT_URL } from '../../../common/constants'; import { OptInExampleFlyout } from './opt_in_details_component'; import './telemetry_form.less'; +import { Field } from 'ui/management'; const SEARCH_TERMS = ['telemetry', 'usage', 'data', 'usage data']; @@ -70,54 +71,63 @@ export class TelemetryForm extends Component { {showExample && telemetryOptInProvider.fetchExample()} onClose={this.toggleExample} /> } - -
-

Usage Data

- - } - description={ - -

{CONFIG_TELEMETRY_DESC}

-

See an example of what we collect

-

- - Read our usage data privacy statement - -

-
- } - > - - - -
-
+ + + + + +

Usage Data

+
+
+
+ + +
); } + renderDescription = () => ( + +

{CONFIG_TELEMETRY_DESC}

+

See an example of what we collect

+

+ + Read our usage data privacy statement + +

+
+ ) + toggleOptIn = async () => { const newOptInValue = !this.props.telemetryOptInProvider.getOptIn(); - this.setState({ - enabled: newOptInValue, - processing: true - }, () => { - this.props.telemetryOptInProvider.setOptIn(newOptInValue).then(() => { - this.setState({ processing: false }); + return new Promise((resolve, reject) => { + this.setState({ + enabled: newOptInValue, + processing: true }, () => { - // something went wrong - this.setState({ processing: false }); + this.props.telemetryOptInProvider.setOptIn(newOptInValue).then(() => { + this.setState({ processing: false }); + resolve(); + }, (e) => { + // something went wrong + this.setState({ processing: false }); + reject(e); + }); }); }); + } toggleExample = () => {