diff --git a/src/legacy/server/usage/classes/collector_set.js b/src/legacy/server/usage/classes/collector_set.js
index 8bcb75e39959f..eb2944df853b6 100644
--- a/src/legacy/server/usage/classes/collector_set.js
+++ b/src/legacy/server/usage/classes/collector_set.js
@@ -107,6 +107,7 @@ export class CollectorSet {
// convert an array of fetched stats results into key/object
toObject(statsData) {
+ if (!statsData) return {};
return statsData.reduce((accumulatedStats, { type, result }) => {
return {
...accumulatedStats,
diff --git a/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/auto_follow_pattern_list.test.js b/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/auto_follow_pattern_list.test.js
index 3e95e457ce5d4..cd0d6d47bc5e6 100644
--- a/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/auto_follow_pattern_list.test.js
+++ b/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/auto_follow_pattern_list.test.js
@@ -37,6 +37,9 @@ describe('', () => {
beforeEach(() => {
server = sinon.fakeServer.create();
server.respondImmediately = true;
+ // We make requests to APIs which don't impact the UX, e.g. UI metric telemetry,
+ // and we can mock them all with a 200 instead of mocking each one individually.
+ server.respondWith([200, {}, '']);
// Register helpers to mock Http Requests
({
diff --git a/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/follower_indices_list.test.js b/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/follower_indices_list.test.js
index 645791da10dba..7baee35e65835 100644
--- a/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/follower_indices_list.test.js
+++ b/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/follower_indices_list.test.js
@@ -34,6 +34,9 @@ describe('', () => {
beforeEach(() => {
server = sinon.fakeServer.create();
server.respondImmediately = true;
+ // We make requests to APIs which don't impact the UX, e.g. UI metric telemetry,
+ // and we can mock them all with a 200 instead of mocking each one individually.
+ server.respondWith([200, {}, '']);
// Register helpers to mock Http Requests
({ setLoadFollowerIndicesResponse } = registerHttpRequestMockHelpers(server));
diff --git a/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/home.test.js b/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/home.test.js
index f99c138b4ea9b..3c8372ffba8dc 100644
--- a/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/home.test.js
+++ b/x-pack/plugins/cross_cluster_replication/__jest__/client_integration/home.test.js
@@ -39,6 +39,9 @@ describe('', () => {
beforeEach(() => {
server = sinon.fakeServer.create();
server.respondImmediately = true;
+ // We make requests to APIs which don't impact the UX, e.g. UI metric telemetry,
+ // and we can mock them all with a 200 instead of mocking each one individually.
+ server.respondWith([200, {}, '']);
// Register helpers to mock Http Requests
const { setLoadFollowerIndicesResponse } = registerHttpRequestMockHelpers(server);
diff --git a/x-pack/plugins/cross_cluster_replication/public/app/constants/index.js b/x-pack/plugins/cross_cluster_replication/public/app/constants/index.js
index 11fd188374b53..ed2ae52ce162f 100644
--- a/x-pack/plugins/cross_cluster_replication/public/app/constants/index.js
+++ b/x-pack/plugins/cross_cluster_replication/public/app/constants/index.js
@@ -6,3 +6,4 @@
export { API_STATUS } from './api';
export { SECTIONS } from './sections';
+export * from './ui_metric';
diff --git a/x-pack/plugins/cross_cluster_replication/public/app/constants/ui_metric.js b/x-pack/plugins/cross_cluster_replication/public/app/constants/ui_metric.js
new file mode 100644
index 0000000000000..d5c9d4dc70cfc
--- /dev/null
+++ b/x-pack/plugins/cross_cluster_replication/public/app/constants/ui_metric.js
@@ -0,0 +1,25 @@
+/*
+ * 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 const UIM_APP_NAME = 'cross_cluster_replication';
+
+export const UIM_FOLLOWER_INDEX_LIST_LOAD = 'follower_index_list_load';
+export const UIM_FOLLOWER_INDEX_CREATE = 'follower_index_create';
+export const UIM_FOLLOWER_INDEX_UPDATE = 'follower_index_update';
+export const UIM_FOLLOWER_INDEX_PAUSE = 'follower_index_pause';
+export const UIM_FOLLOWER_INDEX_PAUSE_MANY = 'follower_index_pause_many';
+export const UIM_FOLLOWER_INDEX_RESUME = 'follower_index_resume';
+export const UIM_FOLLOWER_INDEX_RESUME_MANY = 'follower_index_resume_many';
+export const UIM_FOLLOWER_INDEX_UNFOLLOW = 'follower_index_unfollow';
+export const UIM_FOLLOWER_INDEX_UNFOLLOW_MANY = 'follower_index_unfollow_many';
+export const UIM_FOLLOWER_INDEX_USE_ADVANCED_OPTIONS = 'follower_index_use_advanced_options';
+export const UIM_FOLLOWER_INDEX_SHOW_DETAILS_CLICK = 'follower_index_show_details_click';
+export const UIM_AUTO_FOLLOW_PATTERN_LIST_LOAD = 'auto_follow_patter_list_load';
+export const UIM_AUTO_FOLLOW_PATTERN_CREATE = 'auto_follow_pattern_create';
+export const UIM_AUTO_FOLLOW_PATTERN_UPDATE = 'auto_follow_pattern_update';
+export const UIM_AUTO_FOLLOW_PATTERN_DELETE = 'auto_follow_pattern_delete';
+export const UIM_AUTO_FOLLOW_PATTERN_DELETE_MANY = 'auto_follow_pattern_delete_many';
+export const UIM_AUTO_FOLLOW_PATTERN_SHOW_DETAILS_CLICK = 'auto_follow_pattern_show_details_click';
diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js
index b3551998070b8..80e3b74c522b4 100644
--- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js
+++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js
@@ -19,10 +19,12 @@ import {
import routing from '../../../services/routing';
import { extractQueryParams } from '../../../services/query_params';
-import { API_STATUS } from '../../../constants';
+import { trackUiMetric } from '../../../services/track_ui_metric';
+import { API_STATUS, UIM_AUTO_FOLLOW_PATTERN_LIST_LOAD } from '../../../constants';
import { SectionLoading, SectionError, SectionUnauthorized } from '../../../components';
import { AutoFollowPatternTable, DetailPanel } from './components';
+
const REFRESH_RATE_MS = 30000;
const getQueryParamPattern = ({ location: { search } }) => {
@@ -58,6 +60,7 @@ export class AutoFollowPatternList extends PureComponent {
componentDidMount() {
const { loadAutoFollowPatterns, loadAutoFollowStats, selectAutoFollowPattern, history } = this.props;
+ trackUiMetric(UIM_AUTO_FOLLOW_PATTERN_LIST_LOAD);
loadAutoFollowPatterns();
loadAutoFollowStats();
diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js
index a5b46ff3e17f0..8a18bba64960d 100644
--- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js
+++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js
@@ -17,9 +17,10 @@ import {
EuiToolTip,
EuiOverlayMask,
} from '@elastic/eui';
-import { API_STATUS } from '../../../../../constants';
+import { API_STATUS, UIM_AUTO_FOLLOW_PATTERN_SHOW_DETAILS_CLICK } from '../../../../../constants';
import { AutoFollowPatternDeleteProvider } from '../../../../../components';
import routing from '../../../../../services/routing';
+import { trackUiMetric } from '../../../../../services/track_ui_metric';
export class AutoFollowPatternTable extends PureComponent {
static propTypes = {
@@ -75,7 +76,10 @@ export class AutoFollowPatternTable extends PureComponent {
render: (name) => {
return (
selectAutoFollowPattern(name)}
+ onClick={() => {
+ trackUiMetric(UIM_AUTO_FOLLOW_PATTERN_SHOW_DETAILS_CLICK);
+ selectAutoFollowPattern(name);
+ }}
data-test-subj="ccrAutoFollowPatternListPatternLink"
>
{name}
diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js
index 0f663bc71313a..03151a5672aeb 100644
--- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js
+++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js
@@ -16,13 +16,14 @@ import {
EuiLoadingKibana,
EuiOverlayMask,
} from '@elastic/eui';
-import { API_STATUS } from '../../../../../constants';
+import { API_STATUS, UIM_FOLLOWER_INDEX_SHOW_DETAILS_CLICK } from '../../../../../constants';
import {
FollowerIndexPauseProvider,
FollowerIndexResumeProvider,
FollowerIndexUnfollowProvider
} from '../../../../../components';
import routing from '../../../../../services/routing';
+import { trackUiMetric } from '../../../../../services/track_ui_metric';
import { ContextMenu } from '../context_menu';
export class FollowerIndicesTable extends PureComponent {
@@ -189,7 +190,10 @@ export class FollowerIndicesTable extends PureComponent {
render: (name) => {
return (
selectFollowerIndex(name)}
+ onClick={() => {
+ trackUiMetric(UIM_FOLLOWER_INDEX_SHOW_DETAILS_CLICK);
+ selectFollowerIndex(name);
+ }}
data-test-subj="ccrFollowerIndexListFollowerIndexLink"
>
{name}
diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/follower_indices_list.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/follower_indices_list.js
index 16f6e89ec054d..5ff4e2972fee6 100644
--- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/follower_indices_list.js
+++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/follower_indices_list.js
@@ -19,7 +19,8 @@ import {
import routing from '../../../services/routing';
import { extractQueryParams } from '../../../services/query_params';
-import { API_STATUS } from '../../../constants';
+import { trackUiMetric } from '../../../services/track_ui_metric';
+import { API_STATUS, UIM_FOLLOWER_INDEX_LIST_LOAD } from '../../../constants';
import { SectionLoading, SectionError, SectionUnauthorized } from '../../../components';
import { FollowerIndicesTable, DetailPanel } from './components';
@@ -57,6 +58,7 @@ export class FollowerIndicesList extends PureComponent {
componentDidMount() {
const { loadFollowerIndices, selectFollowerIndex, history } = this.props;
+ trackUiMetric(UIM_FOLLOWER_INDEX_LIST_LOAD);
loadFollowerIndices();
// Select the pattern in the URL query params
diff --git a/x-pack/plugins/cross_cluster_replication/public/app/services/api.js b/x-pack/plugins/cross_cluster_replication/public/app/services/api.js
index 1f81c4b02aad5..cfbfcbb61966c 100644
--- a/x-pack/plugins/cross_cluster_replication/public/app/services/api.js
+++ b/x-pack/plugins/cross_cluster_replication/public/app/services/api.js
@@ -11,6 +11,23 @@ import {
API_INDEX_MANAGEMENT_BASE_PATH,
} from '../../../common/constants';
import { arrify } from '../../../common/services/utils';
+import {
+ UIM_FOLLOWER_INDEX_CREATE,
+ UIM_FOLLOWER_INDEX_UPDATE,
+ UIM_FOLLOWER_INDEX_PAUSE,
+ UIM_FOLLOWER_INDEX_PAUSE_MANY,
+ UIM_FOLLOWER_INDEX_RESUME,
+ UIM_FOLLOWER_INDEX_RESUME_MANY,
+ UIM_FOLLOWER_INDEX_UNFOLLOW,
+ UIM_FOLLOWER_INDEX_UNFOLLOW_MANY,
+ UIM_FOLLOWER_INDEX_USE_ADVANCED_OPTIONS,
+ UIM_AUTO_FOLLOW_PATTERN_CREATE,
+ UIM_AUTO_FOLLOW_PATTERN_UPDATE,
+ UIM_AUTO_FOLLOW_PATTERN_DELETE,
+ UIM_AUTO_FOLLOW_PATTERN_DELETE_MANY,
+} from '../constants';
+import { trackUserRequest } from './track_ui_metric';
+import { areAllSettingsDefault } from './follower_index_default_settings';
const apiPrefix = chrome.addBasePath(API_BASE_PATH);
const apiPrefixRemoteClusters = chrome.addBasePath(API_REMOTE_CLUSTERS_BASE_PATH);
@@ -20,8 +37,8 @@ const apiPrefixIndexManagement = chrome.addBasePath(API_INDEX_MANAGEMENT_BASE_PA
// to access it within our React app.
let httpClient;
-// The deffered AngularJS api allows us to create deferred promise
-// to be resolved later. This allows us to cancel in flight Http Requests
+// The deferred AngularJS api allows us to create a deferred promise
+// to be resolved later. This allows us to cancel in-flight http Requests.
// https://docs.angularjs.org/api/ng/service/$q#the-deferred-api
let $q;
@@ -30,10 +47,16 @@ export function setHttpClient(client, $deffered) {
$q = $deffered;
}
+export const getHttpClient = () => {
+ return httpClient;
+};
+
// ---
const extractData = (response) => response.data;
+const createIdString = (ids) => ids.map(id => encodeURIComponent(id)).join(',');
+
/* Auto Follow Pattern */
export const loadAutoFollowPatterns = () => (
httpClient.get(`${apiPrefix}/auto_follow_patterns`).then(extractData)
@@ -47,18 +70,22 @@ export const loadRemoteClusters = () => (
httpClient.get(apiPrefixRemoteClusters).then(extractData)
);
-export const createAutoFollowPattern = (autoFollowPattern) => (
- httpClient.post(`${apiPrefix}/auto_follow_patterns`, autoFollowPattern).then(extractData)
-);
+export const createAutoFollowPattern = (autoFollowPattern) => {
+ const request = httpClient.post(`${apiPrefix}/auto_follow_patterns`, autoFollowPattern);
+ return trackUserRequest(request, UIM_AUTO_FOLLOW_PATTERN_CREATE).then(extractData);
+};
-export const updateAutoFollowPattern = (id, autoFollowPattern) => (
- httpClient.put(`${apiPrefix}/auto_follow_patterns/${encodeURIComponent(id)}`, autoFollowPattern).then(extractData)
-);
+export const updateAutoFollowPattern = (id, autoFollowPattern) => {
+ const request = httpClient.put(`${apiPrefix}/auto_follow_patterns/${encodeURIComponent(id)}`, autoFollowPattern);
+ return trackUserRequest(request, UIM_AUTO_FOLLOW_PATTERN_UPDATE).then(extractData);
+};
export const deleteAutoFollowPattern = (id) => {
- const ids = arrify(id).map(_id => encodeURIComponent(_id)).join(',');
-
- return httpClient.delete(`${apiPrefix}/auto_follow_patterns/${ids}`).then(extractData);
+ const ids = arrify(id);
+ const idString = ids.map(_id => encodeURIComponent(_id)).join(',');
+ const request = httpClient.delete(`${apiPrefix}/auto_follow_patterns/${idString}`);
+ const uiMetric = ids.length > 1 ? UIM_AUTO_FOLLOW_PATTERN_DELETE_MANY : UIM_AUTO_FOLLOW_PATTERN_DELETE;
+ return trackUserRequest(request, uiMetric).then(extractData);
};
/* Follower Index */
@@ -70,28 +97,49 @@ export const getFollowerIndex = (id) => (
httpClient.get(`${apiPrefix}/follower_indices/${encodeURIComponent(id)}`).then(extractData)
);
-export const createFollowerIndex = (followerIndex) => (
- httpClient.post(`${apiPrefix}/follower_indices`, followerIndex).then(extractData)
-);
+export const createFollowerIndex = (followerIndex) => {
+ const uiMetrics = [UIM_FOLLOWER_INDEX_CREATE];
+ const isUsingAdvancedSettings = !areAllSettingsDefault(followerIndex);
+ if (isUsingAdvancedSettings) {
+ uiMetrics.push(UIM_FOLLOWER_INDEX_USE_ADVANCED_OPTIONS);
+ }
+ const request = httpClient.post(`${apiPrefix}/follower_indices`, followerIndex);
+ return trackUserRequest(request, uiMetrics.join(',')).then(extractData);
+};
export const pauseFollowerIndex = (id) => {
- const ids = arrify(id).map(_id => encodeURIComponent(_id)).join(',');
- return httpClient.put(`${apiPrefix}/follower_indices/${ids}/pause`).then(extractData);
+ const ids = arrify(id);
+ const idString = createIdString(ids);
+ const request = httpClient.put(`${apiPrefix}/follower_indices/${idString}/pause`);
+ const uiMetric = ids.length > 1 ? UIM_FOLLOWER_INDEX_PAUSE_MANY : UIM_FOLLOWER_INDEX_PAUSE;
+ return trackUserRequest(request, uiMetric).then(extractData);
};
export const resumeFollowerIndex = (id) => {
- const ids = arrify(id).map(_id => encodeURIComponent(_id)).join(',');
- return httpClient.put(`${apiPrefix}/follower_indices/${ids}/resume`).then(extractData);
+ const ids = arrify(id);
+ const idString = createIdString(ids);
+ const request = httpClient.put(`${apiPrefix}/follower_indices/${idString}/resume`);
+ const uiMetric = ids.length > 1 ? UIM_FOLLOWER_INDEX_RESUME_MANY : UIM_FOLLOWER_INDEX_RESUME;
+ return trackUserRequest(request, uiMetric).then(extractData);
};
export const unfollowLeaderIndex = (id) => {
- const ids = arrify(id).map(_id => encodeURIComponent(_id)).join(',');
- return httpClient.put(`${apiPrefix}/follower_indices/${ids}/unfollow`).then(extractData);
+ const ids = arrify(id);
+ const idString = createIdString(ids);
+ const request = httpClient.put(`${apiPrefix}/follower_indices/${idString}/unfollow`);
+ const uiMetric = ids.length > 1 ? UIM_FOLLOWER_INDEX_UNFOLLOW_MANY : UIM_FOLLOWER_INDEX_UNFOLLOW;
+ return trackUserRequest(request, uiMetric).then(extractData);
};
-export const updateFollowerIndex = (id, followerIndex) => (
- httpClient.put(`${apiPrefix}/follower_indices/${encodeURIComponent(id)}`, followerIndex).then(extractData)
-);
+export const updateFollowerIndex = (id, followerIndex) => {
+ const uiMetrics = [UIM_FOLLOWER_INDEX_UPDATE];
+ const isUsingAdvancedSettings = !areAllSettingsDefault(followerIndex);
+ if (isUsingAdvancedSettings) {
+ uiMetrics.push(UIM_FOLLOWER_INDEX_USE_ADVANCED_OPTIONS);
+ }
+ const request = httpClient.put(`${apiPrefix}/follower_indices/${encodeURIComponent(id)}`, followerIndex);
+ return trackUserRequest(request, uiMetrics.join(',')).then(extractData);
+};
/* Stats */
export const loadAutoFollowStats = () => (
diff --git a/x-pack/plugins/cross_cluster_replication/public/app/services/track_ui_metric.js b/x-pack/plugins/cross_cluster_replication/public/app/services/track_ui_metric.js
new file mode 100644
index 0000000000000..0cae59fbea5c7
--- /dev/null
+++ b/x-pack/plugins/cross_cluster_replication/public/app/services/track_ui_metric.js
@@ -0,0 +1,28 @@
+/*
+ * 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 { createUiMetricUri } from '../../../../../common/ui_metric';
+import { UIM_APP_NAME } from '../constants';
+import { getHttpClient } from './api';
+
+export function trackUiMetric(actionType) {
+ const uiMetricUri = createUiMetricUri(UIM_APP_NAME, actionType);
+ getHttpClient().post(uiMetricUri);
+}
+
+/**
+ * Transparently return provided request Promise, while allowing us to track
+ * a successful completion of the request.
+ */
+export function trackUserRequest(request, actionType) {
+ // Only track successful actions.
+ return request.then(response => {
+ trackUiMetric(actionType);
+ // We return the response immediately without waiting for the tracking request to resolve,
+ // to avoid adding additional latency.
+ return response;
+ });
+}
diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js
index 27b013cdd200d..dd3044dc491bd 100644
--- a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js
+++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js
@@ -37,6 +37,9 @@ describe('', () => {
beforeEach(() => {
server = sinon.fakeServer.create();
server.respondImmediately = true;
+ // We make requests to APIs which don't impact the UX, e.g. UI metric telemetry,
+ // and we can mock them all with a 200 instead of mocking each one individually.
+ server.respondWith([200, {}, '']);
// Register helpers to mock Http Requests
({ setLoadRemoteClustersResponse, setDeleteRemoteClusterResponse } = registerHttpRequestMockHelpers(server));
diff --git a/x-pack/plugins/remote_clusters/public/app.js b/x-pack/plugins/remote_clusters/public/app.js
index 1a517e860bbbe..4062f6e476b8f 100644
--- a/x-pack/plugins/remote_clusters/public/app.js
+++ b/x-pack/plugins/remote_clusters/public/app.js
@@ -8,8 +8,8 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Switch, Route, Redirect } from 'react-router-dom';
-import { CRUD_APP_BASE_PATH } from './constants';
-import { registerRouter, setUserHasLeftApp } from './services';
+import { CRUD_APP_BASE_PATH, UIM_APP_LOAD } from './constants';
+import { registerRouter, setUserHasLeftApp, trackUiMetric } from './services';
import { RemoteClusterList, RemoteClusterAdd, RemoteClusterEdit } from './sections';
export class App extends Component {
@@ -33,6 +33,10 @@ export class App extends Component {
registerRouter(router);
}
+ componentDidMount() {
+ trackUiMetric(UIM_APP_LOAD);
+ }
+
componentWillUnmount() {
// Set internal flag so we can prevent reacting to route changes internally.
setUserHasLeftApp(true);
diff --git a/x-pack/plugins/remote_clusters/public/constants/index.js b/x-pack/plugins/remote_clusters/public/constants/index.js
index f71725d23b4e0..949ad61e0d17d 100644
--- a/x-pack/plugins/remote_clusters/public/constants/index.js
+++ b/x-pack/plugins/remote_clusters/public/constants/index.js
@@ -7,3 +7,5 @@
export {
CRUD_APP_BASE_PATH,
} from './paths';
+
+export * from './ui_metric';
diff --git a/x-pack/plugins/remote_clusters/public/constants/ui_metric.js b/x-pack/plugins/remote_clusters/public/constants/ui_metric.js
new file mode 100644
index 0000000000000..50e4fb9a19275
--- /dev/null
+++ b/x-pack/plugins/remote_clusters/public/constants/ui_metric.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.
+ */
+
+export const UIM_APP_NAME = 'remote_clusters';
+
+export const UIM_APP_LOAD = 'app_load';
+export const UIM_CLUSTER_ADD = 'cluster_add';
+export const UIM_CLUSTER_UPDATE = 'cluster_update';
+export const UIM_CLUSTER_REMOVE = 'cluster_remove';
+export const UIM_CLUSTER_REMOVE_MANY = 'cluster_remove_many';
+export const UIM_SHOW_DETAILS_CLICK = 'show_details_click';
diff --git a/x-pack/plugins/remote_clusters/public/sections/remote_cluster_edit/remote_cluster_edit.js b/x-pack/plugins/remote_clusters/public/sections/remote_cluster_edit/remote_cluster_edit.js
index c4c40374b42b2..ebe2331c95a9d 100644
--- a/x-pack/plugins/remote_clusters/public/sections/remote_cluster_edit/remote_cluster_edit.js
+++ b/x-pack/plugins/remote_clusters/public/sections/remote_cluster_edit/remote_cluster_edit.js
@@ -23,7 +23,14 @@ import {
} from '@elastic/eui';
import { CRUD_APP_BASE_PATH } from '../../constants';
-import { buildListBreadcrumb, editBreadcrumb, extractQueryParams, getRouter, getRouterLinkProps, redirect } from '../../services';
+import {
+ buildListBreadcrumb,
+ editBreadcrumb,
+ extractQueryParams,
+ getRouter,
+ getRouterLinkProps,
+ redirect,
+} from '../../services';
import { RemoteClusterPageTitle, RemoteClusterForm, ConfiguredByNodeWarning } from '../components';
const disabledFields = {
diff --git a/x-pack/plugins/remote_clusters/public/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js b/x-pack/plugins/remote_clusters/public/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js
index 7b0d6f94c4bd0..194cf7075af60 100644
--- a/x-pack/plugins/remote_clusters/public/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js
+++ b/x-pack/plugins/remote_clusters/public/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js
@@ -20,8 +20,8 @@ import {
EuiToolTip,
} from '@elastic/eui';
-import { CRUD_APP_BASE_PATH } from '../../../constants';
-import { getRouterLinkProps } from '../../../services';
+import { CRUD_APP_BASE_PATH, UIM_SHOW_DETAILS_CLICK } from '../../../constants';
+import { getRouterLinkProps, trackUiMetric } from '../../../services';
import { ConnectionStatus, RemoveClusterButtonProvider } from '../components';
export const RemoteClusterTable = injectI18n(
@@ -93,7 +93,10 @@ export const RemoteClusterTable = injectI18n(
const link = (
openDetailPanel(name)}
+ onClick={() => {
+ trackUiMetric(UIM_SHOW_DETAILS_CLICK);
+ openDetailPanel(name);
+ }}
>
{name}
diff --git a/x-pack/plugins/remote_clusters/public/services/api.js b/x-pack/plugins/remote_clusters/public/services/api.js
index 1d4246b51a414..853c3adf638a8 100644
--- a/x-pack/plugins/remote_clusters/public/services/api.js
+++ b/x-pack/plugins/remote_clusters/public/services/api.js
@@ -5,10 +5,19 @@
*/
import chrome from 'ui/chrome';
+import { UIM_CLUSTER_ADD, UIM_CLUSTER_UPDATE } from '../constants';
+import { trackUserRequest } from './track_ui_metric';
+
let httpClient;
+
export const setHttpClient = (client) => {
httpClient = client;
};
+
+export const getHttpClient = () => {
+ return httpClient;
+};
+
const apiPrefix = chrome.addBasePath('/api/remote_clusters');
export async function loadClusters() {
@@ -17,7 +26,8 @@ export async function loadClusters() {
}
export async function addCluster(cluster) {
- return await httpClient.post(apiPrefix, cluster);
+ const request = httpClient.post(apiPrefix, cluster);
+ return await trackUserRequest(request, UIM_CLUSTER_ADD);
}
export async function editCluster(cluster) {
@@ -26,7 +36,8 @@ export async function editCluster(cluster) {
...rest
} = cluster;
- return await httpClient.put(`${apiPrefix}/${name}`, rest);
+ const request = httpClient.put(`${apiPrefix}/${name}`, rest);
+ return await trackUserRequest(request, UIM_CLUSTER_UPDATE);
}
export function removeClusterRequest(name) {
diff --git a/x-pack/plugins/remote_clusters/public/services/index.js b/x-pack/plugins/remote_clusters/public/services/index.js
index 5eff4f41d0840..bf01f806f3393 100644
--- a/x-pack/plugins/remote_clusters/public/services/index.js
+++ b/x-pack/plugins/remote_clusters/public/services/index.js
@@ -45,3 +45,7 @@ export {
getRouter,
getRouterLinkProps,
} from './routing';
+
+export {
+ trackUiMetric,
+} from './track_ui_metric';
diff --git a/x-pack/plugins/remote_clusters/public/services/track_ui_metric.js b/x-pack/plugins/remote_clusters/public/services/track_ui_metric.js
new file mode 100644
index 0000000000000..d98815e95ff49
--- /dev/null
+++ b/x-pack/plugins/remote_clusters/public/services/track_ui_metric.js
@@ -0,0 +1,28 @@
+/*
+ * 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 { createUiMetricUri } from '../../../../common/ui_metric';
+import { UIM_APP_NAME } from '../constants';
+import { getHttpClient } from './api';
+
+export function trackUiMetric(actionType) {
+ const uiMetricUri = createUiMetricUri(UIM_APP_NAME, actionType);
+ getHttpClient().post(uiMetricUri);
+}
+
+/**
+ * Transparently return provided request Promise, while allowing us to track
+ * a successful completion of the request.
+ */
+export function trackUserRequest(request, actionType) {
+ // Only track successful actions.
+ return request.then(response => {
+ trackUiMetric(actionType);
+ // We return the response immediately without waiting for the tracking request to resolve,
+ // to avoid adding additional latency.
+ return response;
+ });
+}
diff --git a/x-pack/plugins/remote_clusters/public/store/actions/remove_clusters.js b/x-pack/plugins/remote_clusters/public/store/actions/remove_clusters.js
index d72b6a5d1264a..0155070cb3d94 100644
--- a/x-pack/plugins/remote_clusters/public/store/actions/remove_clusters.js
+++ b/x-pack/plugins/remote_clusters/public/store/actions/remove_clusters.js
@@ -7,8 +7,11 @@
import { i18n } from '@kbn/i18n';
import { toastNotifications } from 'ui/notify';
+import { UIM_CLUSTER_REMOVE, UIM_CLUSTER_REMOVE_MANY } from '../../constants';
+
import {
removeClusterRequest as sendRemoveClusterRequest,
+ trackUiMetric,
} from '../../services';
import {
@@ -79,6 +82,9 @@ export const removeClusters = (names) => async (dispatch, getState) => {
}
if (itemsDeleted.length > 0) {
+ // Only track successful requests.
+ trackUiMetric(names.length > 1 ? UIM_CLUSTER_REMOVE_MANY : UIM_CLUSTER_REMOVE);
+
if (itemsDeleted.length === 1) {
toastNotifications.addSuccess(i18n.translate('xpack.remoteClusters.removeAction.successSingleNotificationTitle', {
defaultMessage: `Remote cluster '{name}' was removed`,
diff --git a/x-pack/plugins/rollup/common/ui_metric.js b/x-pack/plugins/rollup/common/ui_metric.js
index 557b925dbbc51..acfd22d83f963 100644
--- a/x-pack/plugins/rollup/common/ui_metric.js
+++ b/x-pack/plugins/rollup/common/ui_metric.js
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export const UIM_APP_NAME = 'rollup_job_wizard';
+export const UIM_APP_NAME = 'rollup_jobs';
export const UIM_APP_LOAD = 'app_load';
export const UIM_JOB_CREATE = 'job_create';