Revert "[Upgrade Assistant] Refactor telemetry (#112177)" (#113665)

This reverts commit 991d24bad2.
This commit is contained in:
CJ Cenizal 2021-10-01 15:17:16 -07:00 committed by GitHub
parent ac0ba881f6
commit c385d49887
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 809 additions and 207 deletions

View file

@ -7324,6 +7324,44 @@
}
}
}
},
"ui_open": {
"properties": {
"elasticsearch": {
"type": "long",
"_meta": {
"description": "Number of times a user viewed the list of Elasticsearch deprecations."
}
},
"overview": {
"type": "long",
"_meta": {
"description": "Number of times a user viewed the Overview page."
}
},
"kibana": {
"type": "long",
"_meta": {
"description": "Number of times a user viewed the list of Kibana deprecations"
}
}
}
},
"ui_reindex": {
"properties": {
"close": {
"type": "long"
},
"open": {
"type": "long"
},
"start": {
"type": "long"
},
"stop": {
"type": "long"
}
}
}
}
},

View file

@ -226,29 +226,4 @@ This is a non-exhaustive list of different error scenarios in Upgrade Assistant.
- **Error updating deprecation logging status.** Mock a `404` status code to `PUT /api/upgrade_assistant/deprecation_logging`. Alternatively, edit [this line](https://github.com/elastic/kibana/blob/545c1420c285af8f5eee56f414bd6eca735aea11/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts#L77) locally and replace `deprecation_logging` with `fake_deprecation_logging`.
- **Unauthorized error fetching ES deprecations.** Mock a `403` status code to `GET /api/upgrade_assistant/es_deprecations` with the response payload: `{ "statusCode": 403 }`
- **Partially upgraded error fetching ES deprecations.** Mock a `426` status code to `GET /api/upgrade_assistant/es_deprecations` with the response payload: `{ "statusCode": 426, "attributes": { "allNodesUpgraded": false } }`
- **Upgraded error fetching ES deprecations.** Mock a `426` status code to `GET /api/upgrade_assistant/es_deprecations` with the response payload: `{ "statusCode": 426, "attributes": { "allNodesUpgraded": true } }`
### Telemetry
The Upgrade Assistant tracks several triggered events in the UI, using Kibana Usage Collection service's [UI counters](https://github.com/elastic/kibana/blob/master/src/plugins/usage_collection/README.mdx#ui-counters).
**Overview page**
- Component loaded
- Click event for "Create snapshot" button
- Click event for "View deprecation logs in Observability" link
- Click event for "Analyze logs in Discover" link
- Click event for "Reset counter" button
**ES deprecations page**
- Component loaded
- Click events for starting and stopping reindex tasks
- Click events for upgrading or deleting a Machine Learning snapshot
- Click event for deleting a deprecated index setting
**Kibana deprecations page**
- Component loaded
- Click event for "Quick resolve" button
In addition to UI counters, the Upgrade Assistant has a [custom usage collector](https://github.com/elastic/kibana/blob/master/src/plugins/usage_collection/README.mdx#custom-collector). It currently is only responsible for tracking whether the user has deprecation logging enabled or not.
For testing instructions, refer to the [Kibana Usage Collection service README](https://github.com/elastic/kibana/blob/master/src/plugins/usage_collection/README.mdx#testing).
- **Upgraded error fetching ES deprecations.** Mock a `426` status code to `GET /api/upgrade_assistant/es_deprecations` with the response payload: `{ "statusCode": 426, "attributes": { "allNodesUpgraded": true } }`

View file

@ -141,7 +141,32 @@ export interface UIReindex {
stop: boolean;
}
export interface UpgradeAssistantTelemetrySavedObject {
ui_open: {
overview: number;
elasticsearch: number;
kibana: number;
};
ui_reindex: {
close: number;
open: number;
start: number;
stop: number;
};
}
export interface UpgradeAssistantTelemetry {
ui_open: {
overview: number;
elasticsearch: number;
kibana: number;
};
ui_reindex: {
close: number;
open: number;
start: number;
stop: number;
};
features: {
deprecation_logging: {
enabled: boolean;
@ -149,6 +174,10 @@ export interface UpgradeAssistantTelemetry {
};
}
export interface UpgradeAssistantTelemetrySavedObjectAttributes {
[key: string]: any;
}
export type MIGRATION_DEPRECATION_LEVEL = 'none' | 'info' | 'warning' | 'critical';
export interface DeprecationInfo {
level: MIGRATION_DEPRECATION_LEVEL;

View file

@ -5,9 +5,8 @@
* 2.0.
*/
import React, { useCallback } from 'react';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { METRIC_TYPE } from '@kbn/analytics';
import {
EuiButton,
EuiButtonEmpty,
@ -26,7 +25,6 @@ import {
} from '@elastic/eui';
import { EnrichedDeprecationInfo, IndexSettingAction } from '../../../../../../common/types';
import type { ResponseError } from '../../../../lib/api';
import { uiMetricService, UIM_INDEX_SETTINGS_DELETE_CLICK } from '../../../../lib/ui_metric';
import type { Status } from '../../../types';
import { DeprecationBadge } from '../../../shared';
@ -109,11 +107,6 @@ export const RemoveIndexSettingsFlyout = ({
// Flag used to hide certain parts of the UI if the deprecation has been resolved or is in progress
const isResolvable = ['idle', 'error'].includes(statusType);
const onRemoveSettings = useCallback(() => {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_INDEX_SETTINGS_DELETE_CLICK);
removeIndexSettings(index!, (correctiveAction as IndexSettingAction).deprecatedSettings);
}, [correctiveAction, index, removeIndexSettings]);
return (
<>
<EuiFlyoutHeader hasBorder>
@ -197,7 +190,12 @@ export const RemoveIndexSettingsFlyout = ({
fill
data-test-subj="deleteSettingsButton"
color="danger"
onClick={onRemoveSettings}
onClick={() =>
removeIndexSettings(
index!,
(correctiveAction as IndexSettingAction).deprecatedSettings
)
}
>
{statusType === 'error'
? i18nTexts.retryRemoveButtonLabel

View file

@ -8,7 +8,6 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { METRIC_TYPE } from '@kbn/analytics';
import {
EuiButton,
@ -26,11 +25,6 @@ import {
} from '@elastic/eui';
import { EnrichedDeprecationInfo } from '../../../../../../common/types';
import {
uiMetricService,
UIM_ML_SNAPSHOT_UPGRADE_CLICK,
UIM_ML_SNAPSHOT_DELETE_CLICK,
} from '../../../../lib/ui_metric';
import { DeprecationBadge } from '../../../shared';
import { MlSnapshotContext } from './context';
import { useAppContext } from '../../../../app_context';
@ -179,13 +173,11 @@ export const FixSnapshotsFlyout = ({
const isResolved = snapshotState.status === 'complete';
const onUpgradeSnapshot = () => {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_ML_SNAPSHOT_UPGRADE_CLICK);
upgradeSnapshot();
closeFlyout();
};
const onDeleteSnapshot = () => {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_ML_SNAPSHOT_DELETE_CLICK);
deleteSnapshot();
closeFlyout();
};

View file

@ -42,7 +42,7 @@ exports[`ChecklistFlyout renders 1`] = `
</h3>
</EuiTitle>
<ReindexProgress
cancelReindex={[Function]}
cancelReindex={[MockFunction]}
reindexState={
Object {
"errorMessage": null,
@ -84,7 +84,7 @@ exports[`ChecklistFlyout renders 1`] = `
disabled={false}
fill={true}
isLoading={false}
onClick={[Function]}
onClick={[MockFunction]}
>
<FormattedMessage
defaultMessage="Run reindex"

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { Fragment, useCallback } from 'react';
import React, { Fragment } from 'react';
import {
EuiButton,
@ -19,14 +19,8 @@ import {
EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { METRIC_TYPE } from '@kbn/analytics';
import { ReindexStatus } from '../../../../../../../common/types';
import {
uiMetricService,
UIM_REINDEX_START_CLICK,
UIM_REINDEX_STOP_CLICK,
} from '../../../../../lib/ui_metric';
import { LoadingState } from '../../../../types';
import type { ReindexState } from '../use_reindex_state';
import { ReindexProgress } from './progress';
@ -80,16 +74,6 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
const hasFetchFailed = status === ReindexStatus.fetchFailed;
const hasReindexingFailed = status === ReindexStatus.failed;
const onStartReindex = useCallback(() => {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_START_CLICK);
startReindex();
}, [startReindex]);
const onStopReindex = useCallback(() => {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_STOP_CLICK);
cancelReindex();
}, [cancelReindex]);
return (
<Fragment>
<EuiFlyoutBody>
@ -168,7 +152,7 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
/>
</h3>
</EuiTitle>
<ReindexProgress reindexState={reindexState} cancelReindex={onStopReindex} />
<ReindexProgress reindexState={reindexState} cancelReindex={cancelReindex} />
</EuiFlyoutBody>
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="spaceBetween">
@ -186,7 +170,7 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
fill
color={status === ReindexStatus.paused ? 'warning' : 'primary'}
iconType={status === ReindexStatus.paused ? 'play' : undefined}
onClick={onStartReindex}
onClick={startReindex}
isLoading={loading}
disabled={loading || !hasRequiredPrivileges}
data-test-subj="startReindexingButton"

View file

@ -7,15 +7,9 @@
import React, { useState, useEffect, useCallback } from 'react';
import { EuiTableRowCell } from '@elastic/eui';
import { METRIC_TYPE } from '@kbn/analytics';
import { EnrichedDeprecationInfo } from '../../../../../../common/types';
import { GlobalFlyout } from '../../../../../shared_imports';
import { useAppContext } from '../../../../app_context';
import {
uiMetricService,
UIM_REINDEX_CLOSE_FLYOUT_CLICK,
UIM_REINDEX_OPEN_FLYOUT_CLICK,
} from '../../../../lib/ui_metric';
import { DeprecationTableColumns } from '../../../types';
import { EsDeprecationsTableCells } from '../../es_deprecations_table_cells';
import { ReindexResolutionCell } from './resolution_table_cell';
@ -35,6 +29,9 @@ const ReindexTableRowCells: React.FunctionComponent<TableRowProps> = ({
}) => {
const [showFlyout, setShowFlyout] = useState(false);
const reindexState = useReindexContext();
const {
services: { api },
} = useAppContext();
const { addContent: addContentToGlobalFlyout, removeContent: removeContentFromGlobalFlyout } =
useGlobalFlyout();
@ -42,8 +39,8 @@ const ReindexTableRowCells: React.FunctionComponent<TableRowProps> = ({
const closeFlyout = useCallback(async () => {
removeContentFromGlobalFlyout('reindexFlyout');
setShowFlyout(false);
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_CLOSE_FLYOUT_CLICK);
}, [removeContentFromGlobalFlyout]);
await api.sendReindexTelemetryData({ close: true });
}, [api, removeContentFromGlobalFlyout]);
useEffect(() => {
if (showFlyout) {
@ -67,9 +64,13 @@ const ReindexTableRowCells: React.FunctionComponent<TableRowProps> = ({
useEffect(() => {
if (showFlyout) {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_OPEN_FLYOUT_CLICK);
async function sendTelemetry() {
await api.sendReindexTelemetryData({ open: true });
}
sendTelemetry();
}
}, [showFlyout]);
}, [showFlyout, api]);
return (
<>

View file

@ -132,6 +132,8 @@ export const useReindexStatus = ({ indexName, api }: { indexName: string; api: A
cancelLoadingState: undefined,
});
api.sendReindexTelemetryData({ start: true });
const { data, error } = await api.startReindexTask(indexName);
if (error) {
@ -149,6 +151,8 @@ export const useReindexStatus = ({ indexName, api }: { indexName: string; api: A
}, [api, indexName, reindexState, updateStatus]);
const cancelReindex = useCallback(async () => {
api.sendReindexTelemetryData({ stop: true });
const { error } = await api.cancelReindexTask(indexName);
setReindexState({

View file

@ -12,12 +12,10 @@ import { EuiPageHeader, EuiSpacer, EuiPageContent, EuiLink } from '@elastic/eui'
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { DocLinksStart } from 'kibana/public';
import { METRIC_TYPE } from '@kbn/analytics';
import { EnrichedDeprecationInfo } from '../../../../common/types';
import { SectionLoading } from '../../../shared_imports';
import { useAppContext } from '../../app_context';
import { uiMetricService, UIM_ES_DEPRECATIONS_PAGE_LOAD } from '../../lib/ui_metric';
import { getEsDeprecationError } from '../../lib/get_es_deprecation_error';
import { DeprecationsPageLoadingError, NoDeprecationsPrompt, DeprecationCount } from '../shared';
import { EsDeprecationsTable } from './es_deprecations_table';
@ -84,7 +82,13 @@ export const EsDeprecations = withRouter(({ history }: RouteComponentProps) => {
},
} = useAppContext();
const { data: esDeprecations, isLoading, error, resendRequest } = api.useLoadEsDeprecations();
const {
data: esDeprecations,
isLoading,
error,
resendRequest,
isInitialRequest,
} = api.useLoadEsDeprecations();
const deprecationsCountByLevel: {
warningDeprecations: number;
@ -99,8 +103,16 @@ export const EsDeprecations = withRouter(({ history }: RouteComponentProps) => {
}, [breadcrumbs]);
useEffect(() => {
uiMetricService.trackUiMetric(METRIC_TYPE.LOADED, UIM_ES_DEPRECATIONS_PAGE_LOAD);
}, []);
if (isLoading === false && isInitialRequest) {
async function sendTelemetryData() {
await api.sendPageTelemetryData({
elasticsearch: true,
});
}
sendTelemetryData();
}
}, [api, isLoading, isInitialRequest]);
if (error) {
return (

View file

@ -5,10 +5,9 @@
* 2.0.
*/
import React, { useCallback } from 'react';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { METRIC_TYPE } from '@kbn/analytics';
import {
EuiButtonEmpty,
@ -25,7 +24,6 @@ import {
EuiSpacer,
} from '@elastic/eui';
import { uiMetricService, UIM_KIBANA_QUICK_RESOLVE_CLICK } from '../../lib/ui_metric';
import type { DeprecationResolutionState, KibanaDeprecationDetails } from './kibana_deprecations';
import { DeprecationBadge } from '../shared';
@ -136,11 +134,6 @@ export const DeprecationDetailsFlyout = ({
const isCurrent = deprecationResolutionState?.id === deprecation.id;
const isResolved = isCurrent && deprecationResolutionState?.resolveDeprecationStatus === 'ok';
const onResolveDeprecation = useCallback(() => {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_KIBANA_QUICK_RESOLVE_CLICK);
resolveDeprecation(deprecation);
}, [deprecation, resolveDeprecation]);
return (
<>
<EuiFlyoutHeader hasBorder>
@ -242,7 +235,7 @@ export const DeprecationDetailsFlyout = ({
<EuiButton
fill
data-test-subj="resolveButton"
onClick={onResolveDeprecation}
onClick={() => resolveDeprecation(deprecation)}
isLoading={Boolean(
deprecationResolutionState?.resolveDeprecationStatus === 'in_progress'
)}

View file

@ -11,12 +11,10 @@ import { withRouter, RouteComponentProps } from 'react-router-dom';
import { EuiPageContent, EuiPageHeader, EuiSpacer, EuiCallOut } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { METRIC_TYPE } from '@kbn/analytics';
import type { DomainDeprecationDetails } from 'kibana/public';
import { SectionLoading, GlobalFlyout } from '../../../shared_imports';
import { useAppContext } from '../../app_context';
import { uiMetricService, UIM_KIBANA_DEPRECATIONS_PAGE_LOAD } from '../../lib/ui_metric';
import { DeprecationsPageLoadingError, NoDeprecationsPrompt, DeprecationCount } from '../shared';
import { KibanaDeprecationsTable } from './kibana_deprecations_table';
import {
@ -118,6 +116,7 @@ export const KibanaDeprecations = withRouter(({ history }: RouteComponentProps)
services: {
core: { deprecations },
breadcrumbs,
api,
},
} = useAppContext();
@ -226,8 +225,14 @@ export const KibanaDeprecations = withRouter(({ history }: RouteComponentProps)
]);
useEffect(() => {
uiMetricService.trackUiMetric(METRIC_TYPE.LOADED, UIM_KIBANA_DEPRECATIONS_PAGE_LOAD);
}, []);
async function sendTelemetryData() {
await api.sendPageTelemetryData({
kibana: true,
});
}
sendTelemetryData();
}, [api]);
useEffect(() => {
breadcrumbs.setBreadcrumbs('kibanaDeprecations');

View file

@ -9,7 +9,6 @@ import React, { useEffect } from 'react';
import moment from 'moment-timezone';
import { FormattedDate, FormattedTime, FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { METRIC_TYPE } from '@kbn/analytics';
import {
EuiLoadingContent,
EuiFlexGroup,
@ -22,7 +21,6 @@ import {
} from '@elastic/eui';
import { useAppContext } from '../../../app_context';
import { uiMetricService, UIM_BACKUP_DATA_CLOUD_CLICK } from '../../../lib/ui_metric';
interface Props {
cloudSnapshotsUrl: string;
@ -130,13 +128,11 @@ export const CloudBackup: React.FunctionComponent<Props> = ({
return (
<>
{statusMessage}
<EuiSpacer size="s" />
{/* eslint-disable-next-line @elastic/eui/href-or-on-click */}
<EuiButton
href={cloudSnapshotsUrl}
onClick={() => {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_BACKUP_DATA_CLOUD_CLICK);
}}
data-test-subj="cloudSnapshotsLink"
target="_blank"
iconType="popout"

View file

@ -8,11 +8,9 @@
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { METRIC_TYPE } from '@kbn/analytics';
import { EuiText, EuiButton, EuiSpacer } from '@elastic/eui';
import { useAppContext } from '../../../app_context';
import { uiMetricService, UIM_BACKUP_DATA_ON_PREM_CLICK } from '../../../lib/ui_metric';
const SnapshotRestoreAppLink: React.FunctionComponent = () => {
const {
@ -24,14 +22,7 @@ const SnapshotRestoreAppLink: React.FunctionComponent = () => {
?.useUrl({ page: 'snapshots' });
return (
// eslint-disable-next-line @elastic/eui/href-or-on-click
<EuiButton
href={snapshotRestoreUrl}
onClick={() => {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_BACKUP_DATA_ON_PREM_CLICK);
}}
data-test-subj="snapshotRestoreLink"
>
<EuiButton href={snapshotRestoreUrl} data-test-subj="snapshotRestoreLink">
<FormattedMessage
id="xpack.upgradeAssistant.overview.snapshotRestoreLink"
defaultMessage="Create snapshot"

View file

@ -8,12 +8,10 @@
import React, { FunctionComponent, useEffect } from 'react';
import moment from 'moment-timezone';
import { FormattedDate, FormattedTime, FormattedMessage } from '@kbn/i18n/react';
import { METRIC_TYPE } from '@kbn/analytics';
import { i18n } from '@kbn/i18n';
import { EuiCallOut, EuiButton, EuiLoadingContent } from '@elastic/eui';
import { useAppContext } from '../../../../app_context';
import { uiMetricService, UIM_RESET_LOGS_COUNTER_CLICK } from '../../../../lib/ui_metric';
const i18nTexts = {
calloutTitle: (warningsCount: number, previousCheck: string) => (
@ -73,7 +71,6 @@ export const DeprecationsCountCheckpoint: FunctionComponent<Props> = ({
const onResetClick = () => {
const now = moment().toISOString();
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_RESET_LOGS_COUNTER_CLICK);
setCheckpoint(now);
};

View file

@ -9,17 +9,10 @@ import { encode } from 'rison-node';
import React, { FunctionComponent, useState, useEffect } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { METRIC_TYPE } from '@kbn/analytics';
import { EuiLink, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiPanel, EuiText } from '@elastic/eui';
import { DataPublicPluginStart } from '../../../../shared_imports';
import { useAppContext } from '../../../app_context';
import {
uiMetricService,
UIM_OBSERVABILITY_CLICK,
UIM_DISCOVER_CLICK,
} from '../../../lib/ui_metric';
import { DataPublicPluginStart } from '../../../../shared_imports';
import {
DEPRECATION_LOGS_INDEX_PATTERN,
DEPRECATION_LOGS_SOURCE_ID,
@ -80,14 +73,7 @@ const DiscoverAppLink: FunctionComponent<Props> = ({ checkpoint }) => {
}, [dataService, checkpoint, share.url.locators]);
return (
// eslint-disable-next-line @elastic/eui/href-or-on-click
<EuiLink
href={discoveryUrl}
onClick={() => {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_DISCOVER_CLICK);
}}
data-test-subj="viewDiscoverLogs"
>
<EuiLink href={discoveryUrl} data-test-subj="viewDiscoverLogs">
<FormattedMessage
id="xpack.upgradeAssistant.overview.viewDiscoverResultsAction"
defaultMessage="Analyze logs in Discover"
@ -109,14 +95,7 @@ const ObservabilityAppLink: FunctionComponent<Props> = ({ checkpoint }) => {
);
return (
// eslint-disable-next-line @elastic/eui/href-or-on-click
<EuiLink
href={logStreamUrl}
onClick={() => {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_OBSERVABILITY_CLICK);
}}
data-test-subj="viewObserveLogs"
>
<EuiLink href={logStreamUrl} data-test-subj="viewObserveLogs">
<FormattedMessage
id="xpack.upgradeAssistant.overview.viewObservabilityResultsAction"
defaultMessage="View deprecation logs in Observability"

View file

@ -18,11 +18,9 @@ import {
EuiPageContent,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { METRIC_TYPE } from '@kbn/analytics';
import { FormattedMessage } from '@kbn/i18n/react';
import { useAppContext } from '../../app_context';
import { uiMetricService, UIM_OVERVIEW_PAGE_LOAD } from '../../lib/ui_metric';
import { getBackupStep } from './backup_step';
import { getFixIssuesStep } from './fix_issues_step';
import { getFixLogsStep } from './fix_logs_step';
@ -35,14 +33,21 @@ export const Overview: FunctionComponent = () => {
kibanaVersionInfo: { nextMajor },
services: {
breadcrumbs,
api,
core: { docLinks },
},
plugins: { cloud },
} = useAppContext();
useEffect(() => {
uiMetricService.trackUiMetric(METRIC_TYPE.LOADED, UIM_OVERVIEW_PAGE_LOAD);
}, []);
async function sendTelemetryData() {
await api.sendPageTelemetryData({
overview: true,
});
}
sendTelemetryData();
}, [api]);
useEffect(() => {
breadcrumbs.setBreadcrumbs('overview');

View file

@ -65,6 +65,16 @@ export class ApiService {
});
}
public async sendPageTelemetryData(telemetryData: { [tabName: string]: boolean }) {
const result = await this.sendRequest({
path: `${API_BASE_PATH}/stats/ui_open`,
method: 'put',
body: JSON.stringify(telemetryData),
});
return result;
}
public useLoadDeprecationLogging() {
return this.useRequest<{
isDeprecationLogIndexingEnabled: boolean;
@ -140,6 +150,16 @@ export class ApiService {
});
}
public async sendReindexTelemetryData(telemetryData: { [key: string]: boolean }) {
const result = await this.sendRequest({
path: `${API_BASE_PATH}/stats/ui_reindex`,
method: 'put',
body: JSON.stringify(telemetryData),
});
return result;
}
public async getReindexStatus(indexName: string) {
return await this.sendRequest({
path: `${API_BASE_PATH}/reindex/${indexName}`,

View file

@ -1,49 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { UiCounterMetricType } from '@kbn/analytics';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/public';
export const UIM_APP_NAME = 'upgrade_assistant';
export const UIM_ES_DEPRECATIONS_PAGE_LOAD = 'es_deprecations_page_load';
export const UIM_KIBANA_DEPRECATIONS_PAGE_LOAD = 'kibana_deprecations_page_load';
export const UIM_OVERVIEW_PAGE_LOAD = 'overview_page_load';
export const UIM_REINDEX_OPEN_FLYOUT_CLICK = 'reindex_open_flyout_click';
export const UIM_REINDEX_CLOSE_FLYOUT_CLICK = 'reindex_close_flyout_click';
export const UIM_REINDEX_START_CLICK = 'reindex_start_click';
export const UIM_REINDEX_STOP_CLICK = 'reindex_stop_click';
export const UIM_BACKUP_DATA_CLOUD_CLICK = 'backup_data_cloud_click';
export const UIM_BACKUP_DATA_ON_PREM_CLICK = 'backup_data_on_prem_click';
export const UIM_RESET_LOGS_COUNTER_CLICK = 'reset_logs_counter_click';
export const UIM_OBSERVABILITY_CLICK = 'observability_click';
export const UIM_DISCOVER_CLICK = 'discover_click';
export const UIM_ML_SNAPSHOT_UPGRADE_CLICK = 'ml_snapshot_upgrade_click';
export const UIM_ML_SNAPSHOT_DELETE_CLICK = 'ml_snapshot_delete_click';
export const UIM_INDEX_SETTINGS_DELETE_CLICK = 'index_settings_delete_click';
export const UIM_KIBANA_QUICK_RESOLVE_CLICK = 'kibana_quick_resolve_click';
export class UiMetricService {
private usageCollection: UsageCollectionSetup | undefined;
public setup(usageCollection: UsageCollectionSetup) {
this.usageCollection = usageCollection;
}
private track(metricType: UiCounterMetricType, eventName: string | string[]) {
if (!this.usageCollection) {
// Usage collection might be disabled in Kibana config.
return;
}
return this.usageCollection.reportUiCounter(UIM_APP_NAME, metricType, eventName);
}
public trackUiMetric(metricType: UiCounterMetricType, eventName: string | string[]) {
return this.track(metricType, eventName);
}
}
export const uiMetricService = new UiMetricService();

View file

@ -11,7 +11,6 @@ import { Plugin, CoreSetup, PluginInitializerContext } from 'src/core/public';
import { apiService } from './application/lib/api';
import { breadcrumbService } from './application/lib/breadcrumbs';
import { uiMetricService } from './application/lib/ui_metric';
import { SetupDependencies, StartDependencies, AppDependencies } from './types';
import { Config } from '../common/config';
@ -19,10 +18,7 @@ export class UpgradeAssistantUIPlugin
implements Plugin<void, void, SetupDependencies, StartDependencies>
{
constructor(private ctx: PluginInitializerContext) {}
setup(
coreSetup: CoreSetup<StartDependencies>,
{ management, cloud, share, usageCollection }: SetupDependencies
) {
setup(coreSetup: CoreSetup<StartDependencies>, { management, cloud, share }: SetupDependencies) {
const { readonly } = this.ctx.config.get<Config>();
const appRegistrar = management.sections.section.stack;
@ -38,10 +34,6 @@ export class UpgradeAssistantUIPlugin
defaultMessage: 'Upgrade Assistant',
});
if (usageCollection) {
uiMetricService.setup(usageCollection);
}
appRegistrar.registerApp({
id: 'upgrade_assistant',
title: pluginName,

View file

@ -10,7 +10,6 @@ import { ManagementSetup } from 'src/plugins/management/public';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import { SharePluginSetup } from 'src/plugins/share/public';
import { CoreStart } from 'src/core/public';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/public';
import { CloudSetup } from '../../cloud/public';
import { LicensingPluginStart } from '../../licensing/public';
import { BreadcrumbService } from './application/lib/breadcrumbs';
@ -26,7 +25,6 @@ export interface SetupDependencies {
management: ManagementSetup;
share: SharePluginSetup;
cloud?: CloudSetup;
usageCollection?: UsageCollectionSetup;
}
export interface StartDependencies {

View file

@ -0,0 +1,48 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { savedObjectsRepositoryMock } from 'src/core/server/mocks';
import { UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE } from '../../../common/types';
import { upsertUIOpenOption } from './es_ui_open_apis';
/**
* Since these route callbacks are so thin, these serve simply as integration tests
* to ensure they're wired up to the lib functions correctly. Business logic is tested
* more thoroughly in the lib/telemetry tests.
*/
describe('Upgrade Assistant Telemetry SavedObject UIOpen', () => {
describe('Upsert UIOpen Option', () => {
it('call saved objects internal repository with the correct info', async () => {
const internalRepo = savedObjectsRepositoryMock.create();
await upsertUIOpenOption({
overview: true,
elasticsearch: true,
kibana: true,
savedObjects: { createInternalRepository: () => internalRepo } as any,
});
expect(internalRepo.incrementCounter).toHaveBeenCalledTimes(3);
expect(internalRepo.incrementCounter).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
['ui_open.overview']
);
expect(internalRepo.incrementCounter).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
['ui_open.elasticsearch']
);
expect(internalRepo.incrementCounter).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
['ui_open.kibana']
);
});
});
});

View file

@ -0,0 +1,57 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { SavedObjectsServiceStart } from 'src/core/server';
import {
UIOpen,
UIOpenOption,
UPGRADE_ASSISTANT_DOC_ID,
UPGRADE_ASSISTANT_TYPE,
} from '../../../common/types';
interface IncrementUIOpenDependencies {
uiOpenOptionCounter: UIOpenOption;
savedObjects: SavedObjectsServiceStart;
}
async function incrementUIOpenOptionCounter({
savedObjects,
uiOpenOptionCounter,
}: IncrementUIOpenDependencies) {
const internalRepository = savedObjects.createInternalRepository();
await internalRepository.incrementCounter(UPGRADE_ASSISTANT_TYPE, UPGRADE_ASSISTANT_DOC_ID, [
`ui_open.${uiOpenOptionCounter}`,
]);
}
type UpsertUIOpenOptionDependencies = UIOpen & { savedObjects: SavedObjectsServiceStart };
export async function upsertUIOpenOption({
overview,
elasticsearch,
savedObjects,
kibana,
}: UpsertUIOpenOptionDependencies): Promise<UIOpen> {
if (overview) {
await incrementUIOpenOptionCounter({ savedObjects, uiOpenOptionCounter: 'overview' });
}
if (elasticsearch) {
await incrementUIOpenOptionCounter({ savedObjects, uiOpenOptionCounter: 'elasticsearch' });
}
if (kibana) {
await incrementUIOpenOptionCounter({ savedObjects, uiOpenOptionCounter: 'kibana' });
}
return {
overview,
elasticsearch,
kibana,
};
}

View file

@ -0,0 +1,52 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { savedObjectsRepositoryMock } from 'src/core/server/mocks';
import { UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE } from '../../../common/types';
import { upsertUIReindexOption } from './es_ui_reindex_apis';
/**
* Since these route callbacks are so thin, these serve simply as integration tests
* to ensure they're wired up to the lib functions correctly. Business logic is tested
* more thoroughly in the lib/telemetry tests.
*/
describe('Upgrade Assistant Telemetry SavedObject UIReindex', () => {
describe('Upsert UIReindex Option', () => {
it('call saved objects internal repository with the correct info', async () => {
const internalRepo = savedObjectsRepositoryMock.create();
await upsertUIReindexOption({
close: true,
open: true,
start: true,
stop: true,
savedObjects: { createInternalRepository: () => internalRepo } as any,
});
expect(internalRepo.incrementCounter).toHaveBeenCalledTimes(4);
expect(internalRepo.incrementCounter).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
[`ui_reindex.close`]
);
expect(internalRepo.incrementCounter).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
[`ui_reindex.open`]
);
expect(internalRepo.incrementCounter).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
[`ui_reindex.start`]
);
expect(internalRepo.incrementCounter).toHaveBeenCalledWith(
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID,
[`ui_reindex.stop`]
);
});
});
});

View file

@ -0,0 +1,63 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { SavedObjectsServiceStart } from 'src/core/server';
import {
UIReindex,
UIReindexOption,
UPGRADE_ASSISTANT_DOC_ID,
UPGRADE_ASSISTANT_TYPE,
} from '../../../common/types';
interface IncrementUIReindexOptionDependencies {
uiReindexOptionCounter: UIReindexOption;
savedObjects: SavedObjectsServiceStart;
}
async function incrementUIReindexOptionCounter({
savedObjects,
uiReindexOptionCounter,
}: IncrementUIReindexOptionDependencies) {
const internalRepository = savedObjects.createInternalRepository();
await internalRepository.incrementCounter(UPGRADE_ASSISTANT_TYPE, UPGRADE_ASSISTANT_DOC_ID, [
`ui_reindex.${uiReindexOptionCounter}`,
]);
}
type UpsertUIReindexOptionDepencies = UIReindex & { savedObjects: SavedObjectsServiceStart };
export async function upsertUIReindexOption({
start,
close,
open,
stop,
savedObjects,
}: UpsertUIReindexOptionDepencies): Promise<UIReindex> {
if (close) {
await incrementUIReindexOptionCounter({ savedObjects, uiReindexOptionCounter: 'close' });
}
if (open) {
await incrementUIReindexOptionCounter({ savedObjects, uiReindexOptionCounter: 'open' });
}
if (start) {
await incrementUIReindexOptionCounter({ savedObjects, uiReindexOptionCounter: 'start' });
}
if (stop) {
await incrementUIReindexOptionCounter({ savedObjects, uiReindexOptionCounter: 'stop' });
}
return {
close,
open,
start,
stop,
};
}

View file

@ -47,6 +47,26 @@ describe('Upgrade Assistant Usage Collector', () => {
};
dependencies = {
usageCollection,
savedObjects: {
createInternalRepository: jest.fn().mockImplementation(() => {
return {
get: () => {
return {
attributes: {
'ui_open.overview': 10,
'ui_open.elasticsearch': 20,
'ui_open.kibana': 15,
'ui_reindex.close': 1,
'ui_reindex.open': 4,
'ui_reindex.start': 2,
'ui_reindex.stop': 1,
'ui_reindex.not_defined': 1,
},
};
},
};
}),
},
elasticsearch: {
client: clusterClient,
},
@ -71,6 +91,17 @@ describe('Upgrade Assistant Usage Collector', () => {
callClusterStub
);
expect(upgradeAssistantStats).toEqual({
ui_open: {
overview: 10,
elasticsearch: 20,
kibana: 15,
},
ui_reindex: {
close: 1,
open: 4,
start: 2,
stop: 1,
},
features: {
deprecation_logging: {
enabled: true,

View file

@ -5,14 +5,43 @@
* 2.0.
*/
import { ElasticsearchClient, ElasticsearchServiceStart } from 'src/core/server';
import { get } from 'lodash';
import {
ElasticsearchClient,
ElasticsearchServiceStart,
ISavedObjectsRepository,
SavedObjectsServiceStart,
} from 'src/core/server';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { UpgradeAssistantTelemetry } from '../../../common/types';
import {
UPGRADE_ASSISTANT_DOC_ID,
UPGRADE_ASSISTANT_TYPE,
UpgradeAssistantTelemetry,
UpgradeAssistantTelemetrySavedObject,
UpgradeAssistantTelemetrySavedObjectAttributes,
} from '../../../common/types';
import {
isDeprecationLogIndexingEnabled,
isDeprecationLoggingEnabled,
} from '../es_deprecation_logging_apis';
async function getSavedObjectAttributesFromRepo(
savedObjectsRepository: ISavedObjectsRepository,
docType: string,
docID: string
) {
try {
return (
await savedObjectsRepository.get<UpgradeAssistantTelemetrySavedObjectAttributes>(
docType,
docID
)
).attributes;
} catch (e) {
return null;
}
}
async function getDeprecationLoggingStatusValue(esClient: ElasticsearchClient): Promise<boolean> {
try {
const { body: loggerDeprecationCallResult } = await esClient.cluster.getSettings({
@ -28,14 +57,58 @@ async function getDeprecationLoggingStatusValue(esClient: ElasticsearchClient):
}
}
export async function fetchUpgradeAssistantMetrics({
client: esClient,
}: ElasticsearchServiceStart): Promise<UpgradeAssistantTelemetry> {
export async function fetchUpgradeAssistantMetrics(
{ client: esClient }: ElasticsearchServiceStart,
savedObjects: SavedObjectsServiceStart
): Promise<UpgradeAssistantTelemetry> {
const savedObjectsRepository = savedObjects.createInternalRepository();
const upgradeAssistantSOAttributes = await getSavedObjectAttributesFromRepo(
savedObjectsRepository,
UPGRADE_ASSISTANT_TYPE,
UPGRADE_ASSISTANT_DOC_ID
);
const deprecationLoggingStatusValue = await getDeprecationLoggingStatusValue(
esClient.asInternalUser
);
const getTelemetrySavedObject = (
upgradeAssistantTelemetrySavedObjectAttrs: UpgradeAssistantTelemetrySavedObjectAttributes | null
): UpgradeAssistantTelemetrySavedObject => {
const defaultTelemetrySavedObject = {
ui_open: {
overview: 0,
elasticsearch: 0,
kibana: 0,
},
ui_reindex: {
close: 0,
open: 0,
start: 0,
stop: 0,
},
};
if (!upgradeAssistantTelemetrySavedObjectAttrs) {
return defaultTelemetrySavedObject;
}
return {
ui_open: {
overview: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_open.overview', 0),
elasticsearch: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_open.elasticsearch', 0),
kibana: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_open.kibana', 0),
},
ui_reindex: {
close: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_reindex.close', 0),
open: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_reindex.open', 0),
start: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_reindex.start', 0),
stop: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_reindex.stop', 0),
},
} as UpgradeAssistantTelemetrySavedObject;
};
return {
...getTelemetrySavedObject(upgradeAssistantSOAttributes),
features: {
deprecation_logging: {
enabled: deprecationLoggingStatusValue,
@ -46,12 +119,14 @@ export async function fetchUpgradeAssistantMetrics({
interface Dependencies {
elasticsearch: ElasticsearchServiceStart;
savedObjects: SavedObjectsServiceStart;
usageCollection: UsageCollectionSetup;
}
export function registerUpgradeAssistantUsageCollector({
elasticsearch,
usageCollection,
savedObjects,
}: Dependencies) {
const upgradeAssistantUsageCollector =
usageCollection.makeUsageCollector<UpgradeAssistantTelemetry>({
@ -68,8 +143,34 @@ export function registerUpgradeAssistantUsageCollector({
},
},
},
ui_open: {
elasticsearch: {
type: 'long',
_meta: {
description: 'Number of times a user viewed the list of Elasticsearch deprecations.',
},
},
overview: {
type: 'long',
_meta: {
description: 'Number of times a user viewed the Overview page.',
},
},
kibana: {
type: 'long',
_meta: {
description: 'Number of times a user viewed the list of Kibana deprecations',
},
},
},
ui_reindex: {
close: { type: 'long' },
open: { type: 'long' },
start: { type: 'long' },
stop: { type: 'long' },
},
},
fetch: async () => fetchUpgradeAssistantMetrics(elasticsearch),
fetch: async () => fetchUpgradeAssistantMetrics(elasticsearch, savedObjects),
});
usageCollection.registerCollector(upgradeAssistantUsageCollector);

View file

@ -142,10 +142,11 @@ export class UpgradeAssistantServerPlugin implements Plugin {
registerRoutes(dependencies, this.getWorker.bind(this));
if (usageCollection) {
getStartServices().then(([{ elasticsearch }]) => {
getStartServices().then(([{ savedObjects: savedObjectsService, elasticsearch }]) => {
registerUpgradeAssistantUsageCollector({
elasticsearch,
usageCollection,
savedObjects: savedObjectsService,
});
});
}

View file

@ -12,6 +12,7 @@ import { registerCloudBackupStatusRoutes } from './cloud_backup_status';
import { registerESDeprecationRoutes } from './es_deprecations';
import { registerDeprecationLoggingRoutes } from './deprecation_logging';
import { registerReindexIndicesRoutes } from './reindex_indices';
import { registerTelemetryRoutes } from './telemetry';
import { registerUpdateSettingsRoute } from './update_index_settings';
import { registerMlSnapshotRoutes } from './ml_snapshots';
import { ReindexWorker } from '../lib/reindexing';
@ -23,6 +24,7 @@ export function registerRoutes(dependencies: RouteDependencies, getWorker: () =>
registerESDeprecationRoutes(dependencies);
registerDeprecationLoggingRoutes(dependencies);
registerReindexIndicesRoutes(dependencies, getWorker);
registerTelemetryRoutes(dependencies);
registerUpdateSettingsRoute(dependencies);
registerMlSnapshotRoutes(dependencies);
// Route for cloud to retrieve the upgrade status for ES and Kibana

View file

@ -0,0 +1,187 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { kibanaResponseFactory } from 'src/core/server';
import { savedObjectsServiceMock } from 'src/core/server/mocks';
import { createMockRouter, MockRouter, routeHandlerContextMock } from './__mocks__/routes.mock';
import { createRequestMock } from './__mocks__/request.mock';
jest.mock('../lib/telemetry/es_ui_open_apis', () => ({
upsertUIOpenOption: jest.fn(),
}));
jest.mock('../lib/telemetry/es_ui_reindex_apis', () => ({
upsertUIReindexOption: jest.fn(),
}));
import { upsertUIOpenOption } from '../lib/telemetry/es_ui_open_apis';
import { upsertUIReindexOption } from '../lib/telemetry/es_ui_reindex_apis';
import { registerTelemetryRoutes } from './telemetry';
/**
* Since these route callbacks are so thin, these serve simply as integration tests
* to ensure they're wired up to the lib functions correctly. Business logic is tested
* more thoroughly in the lib/telemetry tests.
*/
describe('Upgrade Assistant Telemetry API', () => {
let routeDependencies: any;
let mockRouter: MockRouter;
beforeEach(() => {
mockRouter = createMockRouter();
routeDependencies = {
getSavedObjectsService: () => savedObjectsServiceMock.create(),
router: mockRouter,
};
registerTelemetryRoutes(routeDependencies);
});
afterEach(() => jest.clearAllMocks());
describe('PUT /api/upgrade_assistant/stats/ui_open', () => {
it('returns correct payload with single option', async () => {
const returnPayload = {
overview: true,
elasticsearch: false,
kibana: false,
};
(upsertUIOpenOption as jest.Mock).mockResolvedValue(returnPayload);
const resp = await routeDependencies.router.getHandler({
method: 'put',
pathPattern: '/api/upgrade_assistant/stats/ui_open',
})(
routeHandlerContextMock,
createRequestMock({ body: returnPayload }),
kibanaResponseFactory
);
expect(resp.payload).toEqual(returnPayload);
});
it('returns correct payload with multiple option', async () => {
const returnPayload = {
overview: true,
elasticsearch: true,
kibana: true,
};
(upsertUIOpenOption as jest.Mock).mockResolvedValue(returnPayload);
const resp = await routeDependencies.router.getHandler({
method: 'put',
pathPattern: '/api/upgrade_assistant/stats/ui_open',
})(
routeHandlerContextMock,
createRequestMock({
body: {
overview: true,
elasticsearch: true,
kibana: true,
},
}),
kibanaResponseFactory
);
expect(resp.payload).toEqual(returnPayload);
});
it('returns an error if it throws', async () => {
(upsertUIOpenOption as jest.Mock).mockRejectedValue(new Error(`scary error!`));
await expect(
routeDependencies.router.getHandler({
method: 'put',
pathPattern: '/api/upgrade_assistant/stats/ui_open',
})(
routeHandlerContextMock,
createRequestMock({
body: {
overview: false,
},
}),
kibanaResponseFactory
)
).rejects.toThrowError('scary error!');
});
});
describe('PUT /api/upgrade_assistant/stats/ui_reindex', () => {
it('returns correct payload with single option', async () => {
const returnPayload = {
close: false,
open: false,
start: true,
stop: false,
};
(upsertUIReindexOption as jest.Mock).mockResolvedValue(returnPayload);
const resp = await routeDependencies.router.getHandler({
method: 'put',
pathPattern: '/api/upgrade_assistant/stats/ui_reindex',
})(
routeHandlerContextMock,
createRequestMock({
body: {
overview: false,
},
}),
kibanaResponseFactory
);
expect(resp.payload).toEqual(returnPayload);
});
it('returns correct payload with multiple option', async () => {
const returnPayload = {
close: true,
open: true,
start: true,
stop: true,
};
(upsertUIReindexOption as jest.Mock).mockResolvedValue(returnPayload);
const resp = await routeDependencies.router.getHandler({
method: 'put',
pathPattern: '/api/upgrade_assistant/stats/ui_reindex',
})(
routeHandlerContextMock,
createRequestMock({
body: {
close: true,
open: true,
start: true,
stop: true,
},
}),
kibanaResponseFactory
);
expect(resp.payload).toEqual(returnPayload);
});
it('returns an error if it throws', async () => {
(upsertUIReindexOption as jest.Mock).mockRejectedValue(new Error(`scary error!`));
await expect(
routeDependencies.router.getHandler({
method: 'put',
pathPattern: '/api/upgrade_assistant/stats/ui_reindex',
})(
routeHandlerContextMock,
createRequestMock({
body: {
start: false,
},
}),
kibanaResponseFactory
)
).rejects.toThrowError('scary error!');
});
});
});

View file

@ -0,0 +1,64 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { schema } from '@kbn/config-schema';
import { API_BASE_PATH } from '../../common/constants';
import { upsertUIOpenOption } from '../lib/telemetry/es_ui_open_apis';
import { upsertUIReindexOption } from '../lib/telemetry/es_ui_reindex_apis';
import { RouteDependencies } from '../types';
export function registerTelemetryRoutes({ router, getSavedObjectsService }: RouteDependencies) {
router.put(
{
path: `${API_BASE_PATH}/stats/ui_open`,
validate: {
body: schema.object({
overview: schema.boolean({ defaultValue: false }),
elasticsearch: schema.boolean({ defaultValue: false }),
kibana: schema.boolean({ defaultValue: false }),
}),
},
},
async (ctx, request, response) => {
const { elasticsearch, overview, kibana } = request.body;
return response.ok({
body: await upsertUIOpenOption({
savedObjects: getSavedObjectsService(),
elasticsearch,
overview,
kibana,
}),
});
}
);
router.put(
{
path: `${API_BASE_PATH}/stats/ui_reindex`,
validate: {
body: schema.object({
close: schema.boolean({ defaultValue: false }),
open: schema.boolean({ defaultValue: false }),
start: schema.boolean({ defaultValue: false }),
stop: schema.boolean({ defaultValue: false }),
}),
},
},
async (ctx, request, response) => {
const { close, open, start, stop } = request.body;
return response.ok({
body: await upsertUIReindexOption({
savedObjects: getSavedObjectsService(),
close,
open,
start,
stop,
}),
});
}
);
}

View file

@ -15,6 +15,42 @@ export const telemetrySavedObjectType: SavedObjectsType = {
namespaceType: 'agnostic',
mappings: {
properties: {
ui_open: {
properties: {
overview: {
type: 'long',
null_value: 0,
},
elasticsearch: {
type: 'long',
null_value: 0,
},
kibana: {
type: 'long',
null_value: 0,
},
},
},
ui_reindex: {
properties: {
close: {
type: 'long',
null_value: 0,
},
open: {
type: 'long',
null_value: 0,
},
start: {
type: 'long',
null_value: 0,
},
stop: {
type: 'long',
null_value: 0,
},
},
},
features: {
properties: {
deprecation_logging: {