"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.TelemetryService = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _i18n = require("@kbn/i18n"); var _routes = require("../../common/routes"); var _telemetry_config = require("../../common/telemetry_config"); var _constants = require("../../common/constants"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ /** * Handles caching telemetry config in the user's session and requests the * backend to fetch telemetry payload requests or notify about config changes. */ class TelemetryService { /** Current version of Kibana */ constructor({ config, http, isScreenshotMode, notifications, currentKibanaVersion, reportOptInStatusChange = true }) { (0, _defineProperty2.default)(this, "http", void 0); (0, _defineProperty2.default)(this, "reportOptInStatusChange", void 0); (0, _defineProperty2.default)(this, "notifications", void 0); (0, _defineProperty2.default)(this, "defaultConfig", void 0); (0, _defineProperty2.default)(this, "isScreenshotMode", void 0); (0, _defineProperty2.default)(this, "updatedConfig", void 0); (0, _defineProperty2.default)(this, "currentKibanaVersion", void 0); /** Is the cluster allowed to change the opt-in/out status **/ (0, _defineProperty2.default)(this, "getCanChangeOptInStatus", () => { return this.config.allowChangingOptInStatus; }); /** Retrieve the opt-in/out notification URL **/ (0, _defineProperty2.default)(this, "getOptInStatusUrl", () => { const { appendServerlessChannelsSuffix, sendUsageTo } = this.config; return (0, _telemetry_config.getTelemetryChannelEndpoint)({ channelName: 'optInStatus', env: sendUsageTo, appendServerlessChannelsSuffix }); }); /** Retrieve the URL to report telemetry **/ (0, _defineProperty2.default)(this, "getTelemetryUrl", () => { const { appendServerlessChannelsSuffix, sendUsageTo } = this.config; return (0, _telemetry_config.getTelemetryChannelEndpoint)({ channelName: 'snapshot', env: sendUsageTo, appendServerlessChannelsSuffix }); }); /** Is the cluster opted-in to telemetry **/ (0, _defineProperty2.default)(this, "getIsOptedIn", () => { return this.isOptedIn; }); /** Are there any blockers for sending telemetry */ (0, _defineProperty2.default)(this, "canSendTelemetry", () => { return !this.isScreenshotMode && this.getIsOptedIn(); }); (0, _defineProperty2.default)(this, "fetchLastReported", async () => { const response = await this.http.get(_routes.LastReportedRoute, _routes.INTERNAL_VERSION); return response === null || response === void 0 ? void 0 : response.lastReported; }); (0, _defineProperty2.default)(this, "updateLastReported", async () => { return this.http.put(_routes.LastReportedRoute); }); /** Fetches an unencrypted telemetry payload, so we can show it to the user **/ (0, _defineProperty2.default)(this, "fetchExample", async () => { return await this.fetchTelemetry({ unencrypted: true, refreshCache: true }); }); /** * Fetches telemetry payload * @param unencrypted Default `false`. Whether the returned payload should be encrypted or not. * @param refreshCache Default `false`. Set to `true` to force the regeneration of the telemetry report. */ (0, _defineProperty2.default)(this, "fetchTelemetry", async ({ unencrypted = false, refreshCache = false } = {}) => { return this.http.post(_routes.FetchSnapshotTelemetry, { ..._routes.INTERNAL_VERSION, body: JSON.stringify({ unencrypted, refreshCache }) }); }); /** * Overwrite the opt-in status. * It will send a final request to the remote telemetry cluster to report about the opt-in/out change. * @param optedIn Whether the user is opting-in (`true`) or out (`false`). */ (0, _defineProperty2.default)(this, "setOptIn", async optedIn => { const canChangeOptInStatus = this.getCanChangeOptInStatus(); if (!canChangeOptInStatus) { return false; } try { // Report the option to the Kibana server to store the settings. // It returns the encrypted update to send to the telemetry cluster [{cluster_uuid, opt_in_status}] const optInStatusPayload = await this.http.post(_routes.OptInRoute, { ..._routes.INTERNAL_VERSION, body: JSON.stringify({ enabled: optedIn }) }); if (this.reportOptInStatusChange) { // Use the response to report about the change to the remote telemetry cluster. // If it's opt-out, this will be the last communication to the remote service. await this.reportOptInStatus(optInStatusPayload); } this.isOptedIn = optedIn; } catch (err) { this.notifications.toasts.addError(err, { title: _i18n.i18n.translate('telemetry.optInErrorToastTitle', { defaultMessage: 'Error' }), toastMessage: _i18n.i18n.translate('telemetry.optInErrorToastText', { defaultMessage: 'An error occurred while trying to set the usage statistics preference.' }) }); return false; } return true; }); /** * Discards the notice about usage collection and stores it so we don't bother any other users. */ (0, _defineProperty2.default)(this, "setUserHasSeenNotice", async () => { try { await this.http.put(_routes.UserHasSeenNoticeRoute, _routes.INTERNAL_VERSION); this.userHasSeenOptedInNotice = true; } catch (error) { this.notifications.toasts.addError(error, { title: _i18n.i18n.translate('telemetry.optInNoticeSeenErrorTitle', { defaultMessage: 'Error' }), toastMessage: _i18n.i18n.translate('telemetry.optInNoticeSeenErrorToastText', { defaultMessage: 'An error occurred dismissing the notice' }) }); this.userHasSeenOptedInNotice = false; } }); /** * Pushes the encrypted payload [{cluster_uuid, opt_in_status}] to the remote telemetry service * @param optInStatusPayload [{cluster_uuid, opt_in_status}] encrypted by the server into an array of strings */ (0, _defineProperty2.default)(this, "reportOptInStatus", async optInStatusPayload => { const telemetryOptInStatusUrl = this.getOptInStatusUrl(); try { await Promise.all(optInStatusPayload.map(async ({ clusterUuid, stats }) => { return await fetch(telemetryOptInStatusUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Elastic-Stack-Version': this.currentKibanaVersion, 'X-Elastic-Cluster-ID': clusterUuid, 'X-Elastic-Content-Encoding': _constants.PAYLOAD_CONTENT_ENCODING }, body: stats }); })); } catch (err) { // Sending the ping is best-effort. Telemetry tries to send the ping once and discards it immediately if sending fails. // swallow any errors } }); this.defaultConfig = config; this.isScreenshotMode = isScreenshotMode; this.reportOptInStatusChange = reportOptInStatusChange; this.notifications = notifications; this.currentKibanaVersion = currentKibanaVersion; this.http = http; } /** * Config setter to locally persist the updated configuration. * Useful for caching the configuration throughout the users' session, * so they don't need to refresh the page. * @param updatedConfig */ set config(updatedConfig) { this.updatedConfig = updatedConfig; } /** Returns the latest configuration **/ get config() { return { ...this.defaultConfig, ...this.updatedConfig }; } /** Is the cluster opted-in to telemetry **/ get isOptedIn() { return Boolean(this.config.optIn); } /** Changes the opt-in status **/ set isOptedIn(optIn) { this.config = { ...this.config, optIn }; } /** true if the user has already seen the opt-in/out notice **/ get userHasSeenOptedInNotice() { return this.config.telemetryNotifyUserAboutOptInDefault; } /** Changes the notice visibility options **/ set userHasSeenOptedInNotice(telemetryNotifyUserAboutOptInDefault) { this.config = { ...this.config, telemetryNotifyUserAboutOptInDefault }; } /** * Returns whether a user should be shown the notice about Opt-In/Out telemetry. * The decision is made based on: * 1. The config hidePrivacyStatement is unset * 2. The user has enough privileges to change the settings * 3. At least one of the following: * * It is opted-in, and the user has already been notified at any given point in the deployment's life. * * It is opted-out, and the user has been notified for this version (excluding patch updates) */ getUserShouldSeeOptInNotice() { var _ref; return (_ref = !this.config.hidePrivacyStatement && this.config.userCanChangeSettings && (this.config.telemetryNotifyUserAboutOptInDefault || this.config.optIn === null)) !== null && _ref !== void 0 ? _ref : false; } /** Is the user allowed to change the opt-in/out status **/ get userCanChangeSettings() { var _this$config$userCanC; return (_this$config$userCanC = this.config.userCanChangeSettings) !== null && _this$config$userCanC !== void 0 ? _this$config$userCanC : false; } /** Change the user's permissions to change the opt-in/out status **/ set userCanChangeSettings(userCanChangeSettings) { this.config = { ...this.config, userCanChangeSettings }; } } exports.TelemetryService = TelemetryService;