"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.ApplicationUsageTracker = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _metrics = require("./metrics"); /* * 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. */ class ApplicationUsageTracker { constructor(reporter) { (0, _defineProperty2.default)(this, "trackedApplicationViews", {}); (0, _defineProperty2.default)(this, "reporter", void 0); (0, _defineProperty2.default)(this, "currentAppId", void 0); (0, _defineProperty2.default)(this, "currentApplicationKeys", []); (0, _defineProperty2.default)(this, "beforeUnloadListener", void 0); (0, _defineProperty2.default)(this, "onVisiblityChangeListener", void 0); this.reporter = reporter; } createKey(appId, viewId) { return { appId, viewId }; } static serializeKey({ appId, viewId }) { return `${appId}-${viewId}`; } trackApplications(appKeys) { for (const { appId, viewId } of appKeys.filter(Boolean)) { const serializedKey = ApplicationUsageTracker.serializeKey({ appId, viewId }); if (typeof this.trackedApplicationViews[serializedKey] !== 'undefined') { continue; } const metric = (0, _metrics.createApplicationUsageMetric)(appId, viewId); this.trackedApplicationViews[serializedKey] = metric; } } attachListeners() { if (typeof window === 'undefined' || typeof document === 'undefined') { return; } this.beforeUnloadListener = () => { this.flushTrackedViews(); }; this.onVisiblityChangeListener = () => { if (document.visibilityState === 'visible') { this.resumeTrackingAll(); } else if (document.visibilityState === 'hidden') { this.pauseTrackingAll(); } }; // Before leaving the page, make sure we store the current usage window.addEventListener('beforeunload', this.beforeUnloadListener); // Monitoring dashboards might be open in background and we are fine with that // but we don't want to report hours if the user goes to another tab and Kibana is not shown document.addEventListener('visibilitychange', this.onVisiblityChangeListener); } detachListeners() { if (typeof window === 'undefined' || typeof document === 'undefined') { return; } if (this.beforeUnloadListener) { window.removeEventListener('beforeunload', this.beforeUnloadListener); } if (this.onVisiblityChangeListener) { document.removeEventListener('visibilitychange', this.onVisiblityChangeListener); } } sendMetricsToReporter(metrics) { metrics.forEach(metric => { this.reporter.reportApplicationUsage(metric); }); } updateViewClickCounter(viewId) { if (!this.currentAppId) { return; } const appKey = ApplicationUsageTracker.serializeKey({ appId: this.currentAppId, viewId }); if (this.trackedApplicationViews[appKey]) { this.trackedApplicationViews[appKey].numberOfClicks++; } } flushTrackedViews() { const appViewMetrics = Object.values(this.trackedApplicationViews); this.sendMetricsToReporter(appViewMetrics); this.trackedApplicationViews = {}; } start() { this.attachListeners(); } stop() { this.flushTrackedViews(); this.detachListeners(); } setCurrentAppId(appId) { // application change, flush current views first. this.flushTrackedViews(); this.currentAppId = appId; } trackApplicationViewUsage(viewId) { if (!this.currentAppId) { return; } const appKey = this.createKey(this.currentAppId, viewId); this.trackApplications([appKey]); } pauseTrackingAll() { this.currentApplicationKeys = Object.values(this.trackedApplicationViews).map(({ appId, viewId }) => this.createKey(appId, viewId)); this.flushTrackedViews(); } resumeTrackingAll() { this.trackApplications(this.currentApplicationKeys); this.currentApplicationKeys = []; // We also want to send the report now because intervals and timeouts be stalled when too long in the "hidden" state // Note: it might be better to create a separate listener in the reporter for this. this.reporter.sendReports(); } flushTrackedView(viewId) { if (!this.currentAppId) { return; } const appKey = this.createKey(this.currentAppId, viewId); const serializedKey = ApplicationUsageTracker.serializeKey(appKey); const appViewMetric = this.trackedApplicationViews[serializedKey]; if (appViewMetric) { this.sendMetricsToReporter([appViewMetric]); delete this.trackedApplicationViews[serializedKey]; } } } exports.ApplicationUsageTracker = ApplicationUsageTracker;