diff --git a/packages/kbn-analytics/src/index.ts b/packages/kbn-analytics/src/index.ts index 6514347b0b127..c7a1350841168 100644 --- a/packages/kbn-analytics/src/index.ts +++ b/packages/kbn-analytics/src/index.ts @@ -20,3 +20,4 @@ export { ReportHTTP, Reporter, ReporterConfig } from './reporter'; export { UiStatsMetricType, METRIC_TYPE } from './metrics'; export { Report, ReportManager } from './report'; +export { Storage } from './storage'; diff --git a/packages/kbn-analytics/src/report.ts b/packages/kbn-analytics/src/report.ts index 333bc05d28f9b..1c0b37966355f 100644 --- a/packages/kbn-analytics/src/report.ts +++ b/packages/kbn-analytics/src/report.ts @@ -23,23 +23,25 @@ const REPORT_VERSION = 1; export interface Report { reportVersion: typeof REPORT_VERSION; - uiStatsMetrics: { - [key: string]: { + uiStatsMetrics?: Record< + string, + { key: string; appName: string; eventName: string; type: UiStatsMetricType; stats: Stats; - }; - }; - userAgent?: { - [key: string]: { + } + >; + userAgent?: Record< + string, + { userAgent: string; key: string; type: METRIC_TYPE.USER_AGENT; appName: string; - }; - }; + } + >; } export class ReportManager { @@ -49,14 +51,15 @@ export class ReportManager { this.report = report || ReportManager.createReport(); } static createReport(): Report { - return { reportVersion: REPORT_VERSION, uiStatsMetrics: {} }; + return { reportVersion: REPORT_VERSION }; } public clearReport() { this.report = ReportManager.createReport(); } public isReportEmpty(): boolean { - const noUiStats = Object.keys(this.report.uiStatsMetrics).length === 0; - const noUserAgent = !this.report.userAgent || Object.keys(this.report.userAgent).length === 0; + const { uiStatsMetrics, userAgent } = this.report; + const noUiStats = !uiStatsMetrics || Object.keys(uiStatsMetrics).length === 0; + const noUserAgent = !userAgent || Object.keys(userAgent).length === 0; return noUiStats && noUserAgent; } private incrementStats(count: number, stats?: Stats): Stats { @@ -113,14 +116,17 @@ export class ReportManager { case METRIC_TYPE.LOADED: case METRIC_TYPE.COUNT: { const { appName, type, eventName, count } = metric; - const existingStats = (report.uiStatsMetrics[key] || {}).stats; - this.report.uiStatsMetrics[key] = { - key, - appName, - eventName, - type, - stats: this.incrementStats(count, existingStats), - }; + if (report.uiStatsMetrics) { + const existingStats = (report.uiStatsMetrics[key] || {}).stats; + this.report.uiStatsMetrics = this.report.uiStatsMetrics || {}; + this.report.uiStatsMetrics[key] = { + key, + appName, + eventName, + type, + stats: this.incrementStats(count, existingStats), + }; + } return; } default: diff --git a/packages/kbn-analytics/src/storage.ts b/packages/kbn-analytics/src/storage.ts index 9abf9fa7dac2c..5c18d9280ffc7 100644 --- a/packages/kbn-analytics/src/storage.ts +++ b/packages/kbn-analytics/src/storage.ts @@ -19,7 +19,13 @@ import { Report } from './report'; -export type Storage = Map; +export interface Storage { + get: (key: string) => T | null; + set: (key: string, value: T) => S; + remove: (key: string) => T | null; + clear: () => void; +} + export class ReportStorageManager { storageKey: string; private storage?: Storage; diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx b/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx index 4eafd06c7bb20..0d0c3a5640622 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx @@ -23,6 +23,7 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { VisType } from '../legacy_imports'; import { TypesStart } from '../../../../visualizations/public/np_ready/public/types'; +jest.mock('ui/new_platform'); jest.mock('../legacy_imports', () => ({ State: () => null, AppState: () => null, diff --git a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts b/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts index ea7fe2ee5d9f2..af908bea7f4b1 100644 --- a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts +++ b/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts @@ -18,7 +18,6 @@ */ import moment from 'moment'; -import { setCanTrackUiMetrics } from 'ui/ui_metric'; // @ts-ignore import { banners, toastNotifications } from 'ui/notify'; import { npStart } from 'ui/new_platform'; @@ -69,8 +68,6 @@ export function TelemetryOptInProvider($injector: any, chrome: any, sendOptInSta 'telemetryNotifyUserAboutOptInDefault' ) as boolean; - setCanTrackUiMetrics(currentOptInStatus); - const provider = { getBannerId: () => bannerId, getOptInBannerNoticeId: () => optInBannerNoticeId, @@ -116,7 +113,6 @@ export function TelemetryOptInProvider($injector: any, chrome: any, sendOptInSta if (!allowChangingOptInStatus) { return; } - setCanTrackUiMetrics(enabled); const $http = $injector.get('$http'); try { diff --git a/src/legacy/core_plugins/ui_metric/README.md b/src/legacy/core_plugins/ui_metric/README.md deleted file mode 100644 index 90855faff61a6..0000000000000 --- a/src/legacy/core_plugins/ui_metric/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# UI Metric app - -## Purpose - -The purpose of the UI Metric app is to provide a tool for gathering data on how users interact with -various UIs within Kibana. It's useful for gathering _aggregate_ information, e.g. "How many times -has Button X been clicked" or "How many times has Page Y been viewed". - -With some finagling, it's even possible to add more meaning to the info you gather, such as "How many -visualizations were created in less than 5 minutes". - -### What it doesn't do - -The UI Metric app doesn't gather any metadata around a user interaction, e.g. the user's identity, -the name of a dashboard they've viewed, or the timestamp of the interaction. - -## How to use it - -To track a user interaction, import the `createUiStatsReporter` helper function from UI Metric app: - -```js -import { createUiStatsReporter, METRIC_TYPE } from 'relative/path/to/src/legacy/core_plugins/ui_metric/public'; -const trackMetric = createUiStatsReporter(``); -trackMetric(METRIC_TYPE.CLICK, ``); -trackMetric('click', ``); -``` - -Metric Types: - - `METRIC_TYPE.CLICK` for tracking clicks `trackMetric(METRIC_TYPE.CLICK, 'my_button_clicked');` - - `METRIC_TYPE.LOADED` for a component load or page load `trackMetric(METRIC_TYPE.LOADED', 'my_component_loaded');` - - `METRIC_TYPE.COUNT` for a tracking a misc count `trackMetric(METRIC_TYPE.COUNT', 'my_counter', });` - -Call this function whenever you would like to track a user interaction within your app. The function -accepts two arguments, `metricType` and `eventNames`. These should be underscore-delimited strings. -For example, to track the `my_event` metric in the app `my_app` call `trackUiMetric(METRIC_TYPE.*, 'my_event)`. - -That's all you need to do! - -To track multiple metrics within a single request, provide an array of events, e.g. `trackMetric(METRIC_TYPE.*, ['my_event1', 'my_event2', 'my_event3'])`. - -### Disallowed characters - -The colon character (`:`) should not be used in app name or event names. Colons play -a special role in how metrics are stored as saved objects. - -### Tracking timed interactions - -If you want to track how long it takes a user to do something, you'll need to implement the timing -logic yourself. You'll also need to predefine some buckets into which the UI metric can fall. -For example, if you're timing how long it takes to create a visualization, you may decide to -measure interactions that take less than 1 minute, 1-5 minutes, 5-20 minutes, and longer than 20 minutes. -To track these interactions, you'd use the timed length of the interaction to determine whether to -use a `eventName` of `create_vis_1m`, `create_vis_5m`, `create_vis_20m`, or `create_vis_infinity`. - -## How it works - -Under the hood, your app and metric type will be stored in a saved object of type `user-metric` and the -ID `ui-metric:my_app:my_metric`. This saved object will have a `count` property which will be incremented -every time the above URI is hit. - -These saved objects are automatically consumed by the stats API and surfaced under the -`ui_metric` namespace. - -```json -{ - "ui_metric": { - "my_app": [ - { - "key": "my_metric", - "value": 3 - } - ] - } -} -``` - -By storing these metrics and their counts as key-value pairs, we can add more metrics without having -to worry about exceeding the 1000-field soft limit in Elasticsearch. \ No newline at end of file diff --git a/src/legacy/core_plugins/ui_metric/index.ts b/src/legacy/core_plugins/ui_metric/index.ts index 964e3497accba..86d75a9f1818a 100644 --- a/src/legacy/core_plugins/ui_metric/index.ts +++ b/src/legacy/core_plugins/ui_metric/index.ts @@ -18,10 +18,7 @@ */ import { resolve } from 'path'; -import JoiNamespace from 'joi'; -import { Server } from 'hapi'; import { Legacy } from '../../../../kibana'; -import { registerUiMetricRoute } from './server/routes/api/ui_metric'; // eslint-disable-next-line import/no-default-export export default function(kibana: any) { @@ -29,25 +26,16 @@ export default function(kibana: any) { id: 'ui_metric', require: ['kibana', 'elasticsearch'], publicDir: resolve(__dirname, 'public'), - config(Joi: typeof JoiNamespace) { - return Joi.object({ - enabled: Joi.boolean().default(true), - debug: Joi.boolean().default(Joi.ref('$dev')), - }).default(); - }, uiExports: { - injectDefaultVars(server: Server) { - const config = server.config(); - return { - uiMetricEnabled: config.get('ui_metric.enabled'), - debugUiMetric: config.get('ui_metric.debug'), - }; - }, mappings: require('./mappings.json'), - hacks: ['plugins/ui_metric/hacks/ui_metric_init'], }, init(server: Legacy.Server) { - registerUiMetricRoute(server); + const { getSavedObjectsRepository } = server.savedObjects; + const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin'); + const internalRepository = getSavedObjectsRepository(callWithInternalUser); + const { usageCollection } = server.newPlatform.setup.plugins; + + usageCollection.registerLegacySavedObjects(internalRepository); }, }); } diff --git a/src/legacy/core_plugins/ui_metric/public/index.ts b/src/legacy/core_plugins/ui_metric/public/index.ts index 5c327234b1e7c..19246b571cb84 100644 --- a/src/legacy/core_plugins/ui_metric/public/index.ts +++ b/src/legacy/core_plugins/ui_metric/public/index.ts @@ -17,5 +17,5 @@ * under the License. */ -export { createUiStatsReporter, trackUserAgent } from './services/telemetry_analytics'; +export { createUiStatsReporter } from './services/telemetry_analytics'; export { METRIC_TYPE, UiStatsMetricType } from '@kbn/analytics'; diff --git a/src/legacy/core_plugins/ui_metric/public/services/telemetry_analytics.ts b/src/legacy/core_plugins/ui_metric/public/services/telemetry_analytics.ts index ee928b8a1d9ee..0e517e6ff2244 100644 --- a/src/legacy/core_plugins/ui_metric/public/services/telemetry_analytics.ts +++ b/src/legacy/core_plugins/ui_metric/public/services/telemetry_analytics.ts @@ -16,61 +16,9 @@ * specific language governing permissions and limitations * under the License. */ +import { npSetup } from 'ui/new_platform'; -import { Reporter, UiStatsMetricType } from '@kbn/analytics'; -// @ts-ignore -import { addSystemApiHeader } from 'ui/system_api'; - -let telemetryReporter: Reporter; - -export const setTelemetryReporter = (aTelemetryReporter: Reporter): void => { - telemetryReporter = aTelemetryReporter; -}; - -export const getTelemetryReporter = () => { - return telemetryReporter; -}; - -export const createUiStatsReporter = (appName: string) => ( - type: UiStatsMetricType, - eventNames: string | string[], - count?: number -): void => { - if (telemetryReporter) { - return telemetryReporter.reportUiStats(appName, type, eventNames, count); - } +export const createUiStatsReporter = (appName: string) => { + const { usageCollection } = npSetup.plugins; + return usageCollection.reportUiStats.bind(usageCollection, appName); }; - -export const trackUserAgent = (appName: string) => { - if (telemetryReporter) { - return telemetryReporter.reportUserAgent(appName); - } -}; - -interface AnalyicsReporterConfig { - localStorage: any; - debug: boolean; - kfetch: any; -} - -export function createAnalyticsReporter(config: AnalyicsReporterConfig) { - const { localStorage, debug, kfetch } = config; - - return new Reporter({ - debug, - storage: localStorage, - async http(report) { - const response = await kfetch({ - method: 'POST', - pathname: '/api/telemetry/report', - body: JSON.stringify(report), - headers: addSystemApiHeader({}), - }); - - if (response.status !== 'ok') { - throw Error('Unable to store report.'); - } - return response; - }, - }); -} diff --git a/src/legacy/core_plugins/ui_metric/server/routes/api/ui_metric.ts b/src/legacy/core_plugins/ui_metric/server/routes/api/ui_metric.ts deleted file mode 100644 index e2de23ea806e4..0000000000000 --- a/src/legacy/core_plugins/ui_metric/server/routes/api/ui_metric.ts +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 Joi from 'joi'; -import { Report } from '@kbn/analytics'; -import { Server } from 'hapi'; - -export async function storeReport(server: any, report: Report) { - const { getSavedObjectsRepository } = server.savedObjects; - const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin'); - const internalRepository = getSavedObjectsRepository(callWithInternalUser); - - const uiStatsMetrics = report.uiStatsMetrics ? Object.entries(report.uiStatsMetrics) : []; - const userAgents = report.userAgent ? Object.entries(report.userAgent) : []; - return Promise.all([ - ...userAgents.map(async ([key, metric]) => { - const { userAgent } = metric; - const savedObjectId = `${key}:${userAgent}`; - return await internalRepository.create( - 'ui-metric', - { count: 1 }, - { - id: savedObjectId, - overwrite: true, - } - ); - }), - ...uiStatsMetrics.map(async ([key, metric]) => { - const { appName, eventName } = metric; - const savedObjectId = `${appName}:${eventName}`; - return await internalRepository.incrementCounter('ui-metric', savedObjectId, 'count'); - }), - ]); -} - -export function registerUiMetricRoute(server: Server) { - server.route({ - method: 'POST', - path: '/api/telemetry/report', - options: { - validate: { - payload: Joi.object({ - reportVersion: Joi.number().optional(), - userAgent: Joi.object() - .pattern( - /.*/, - Joi.object({ - key: Joi.string().required(), - type: Joi.string().required(), - appName: Joi.string().required(), - userAgent: Joi.string().required(), - }) - ) - .allow(null) - .optional(), - uiStatsMetrics: Joi.object() - .pattern( - /.*/, - Joi.object({ - key: Joi.string().required(), - type: Joi.string().required(), - appName: Joi.string().required(), - eventName: Joi.string().required(), - stats: Joi.object({ - min: Joi.number(), - sum: Joi.number(), - max: Joi.number(), - avg: Joi.number(), - }).allow(null), - }) - ) - .allow(null), - }), - }, - }, - handler: async (req: any, h: any) => { - try { - const report = req.payload; - await storeReport(server, report); - return { status: 'ok' }; - } catch (error) { - return { status: 'fail' }; - } - }, - }); -} diff --git a/src/legacy/ui/public/new_platform/__mocks__/helpers.ts b/src/legacy/ui/public/new_platform/__mocks__/helpers.ts index 5c7f7be060374..006497435aec8 100644 --- a/src/legacy/ui/public/new_platform/__mocks__/helpers.ts +++ b/src/legacy/ui/public/new_platform/__mocks__/helpers.ts @@ -24,6 +24,7 @@ import { embeddablePluginMock } from '../../../../../plugins/embeddable/public/m import { expressionsPluginMock } from '../../../../../plugins/expressions/public/mocks'; import { inspectorPluginMock } from '../../../../../plugins/inspector/public/mocks'; import { uiActionsPluginMock } from '../../../../../plugins/ui_actions/public/mocks'; +import { usageCollectionPluginMock } from '../../../../../plugins/usage_collection/public/mocks'; /* eslint-enable @kbn/eslint/no-restricted-paths */ export const pluginsMock = { @@ -33,6 +34,7 @@ export const pluginsMock = { inspector: inspectorPluginMock.createSetupContract(), expressions: expressionsPluginMock.createSetupContract(), uiActions: uiActionsPluginMock.createSetupContract(), + usageCollection: usageCollectionPluginMock.createSetupContract(), }), createStart: () => ({ data: dataPluginMock.createStartContract(), diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 6a1ee9663e805..dd91a538e339f 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -19,6 +19,7 @@ import sinon from 'sinon'; import { getFieldFormatsRegistry } from '../../../../test_utils/public/stub_field_formats'; +import { METRIC_TYPE } from '@kbn/analytics'; const mockObservable = () => { return { @@ -50,6 +51,11 @@ export const npSetup = { uiSettings: mockUiSettings, }, plugins: { + usageCollection: { + allowTrackUserAgent: sinon.fake(), + reportUiStats: sinon.fake(), + METRIC_TYPE, + }, embeddable: { registerEmbeddableFactory: sinon.fake(), }, diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index 547d22c58cfc1..f88d5be1328ca 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -33,6 +33,7 @@ import { KibanaLegacySetup, KibanaLegacyStart } from '../../../../plugins/kibana import { HomePublicPluginSetup, HomePublicPluginStart } from '../../../../plugins/home/public'; import { SharePluginSetup, SharePluginStart } from '../../../../plugins/share/public'; import { LicensingPluginSetup } from '../../../../../x-pack/plugins/licensing/common/types'; +import { UsageCollectionSetup } from '../../../../plugins/usage_collection/public'; export interface PluginsSetup { data: ReturnType; @@ -45,6 +46,7 @@ export interface PluginsSetup { kibana_legacy: KibanaLegacySetup; share: SharePluginSetup; licensing: LicensingPluginSetup; + usageCollection: UsageCollectionSetup; } export interface PluginsStart { diff --git a/src/plugins/usage_collection/README.md b/src/plugins/usage_collection/README.md index 4502e1a6ceacf..4b81fe9b22083 100644 --- a/src/plugins/usage_collection/README.md +++ b/src/plugins/usage_collection/README.md @@ -137,3 +137,83 @@ There are a few ways you can test that your usage collector is working properly. Keep it simple, and keep it to a model that Kibana will be able to understand. In short, that means don't rely on nested fields (arrays with objects). Flat arrays, such as arrays of strings are fine. 2. **If I accumulate an event counter in server memory, which my fetch method returns, won't it reset when the Kibana server restarts?** Yes, but that is not a major concern. A visualization on such info might be a date histogram that gets events-per-second or something, which would be impacted by server restarts, so we'll have to offset the beginning of the time range when we detect that the latest metric is smaller than the earliest metric. That would be a pretty custom visualization, but perhaps future Kibana enhancements will be able to support that. + + +# UI Metric app + +## Purpose + +The purpose of the UI Metric app is to provide a tool for gathering data on how users interact with +various UIs within Kibana. It's useful for gathering _aggregate_ information, e.g. "How many times +has Button X been clicked" or "How many times has Page Y been viewed". + +With some finagling, it's even possible to add more meaning to the info you gather, such as "How many +visualizations were created in less than 5 minutes". + +### What it doesn't do + +The UI Metric app doesn't gather any metadata around a user interaction, e.g. the user's identity, +the name of a dashboard they've viewed, or the timestamp of the interaction. + +## How to use it + +To track a user interaction, import the `createUiStatsReporter` helper function from UI Metric app: + +```js +import { createUiStatsReporter, METRIC_TYPE } from 'relative/path/to/src/legacy/core_plugins/ui_metric/public'; +const trackMetric = createUiStatsReporter(``); +trackMetric(METRIC_TYPE.CLICK, ``); +trackMetric('click', ``); +``` + +Metric Types: + - `METRIC_TYPE.CLICK` for tracking clicks `trackMetric(METRIC_TYPE.CLICK, 'my_button_clicked');` + - `METRIC_TYPE.LOADED` for a component load or page load `trackMetric(METRIC_TYPE.LOADED', 'my_component_loaded');` + - `METRIC_TYPE.COUNT` for a tracking a misc count `trackMetric(METRIC_TYPE.COUNT', 'my_counter', });` + +Call this function whenever you would like to track a user interaction within your app. The function +accepts two arguments, `metricType` and `eventNames`. These should be underscore-delimited strings. +For example, to track the `my_event` metric in the app `my_app` call `trackUiMetric(METRIC_TYPE.*, 'my_event)`. + +That's all you need to do! + +To track multiple metrics within a single request, provide an array of events, e.g. `trackMetric(METRIC_TYPE.*, ['my_event1', 'my_event2', 'my_event3'])`. + +### Disallowed characters + +The colon character (`:`) should not be used in app name or event names. Colons play +a special role in how metrics are stored as saved objects. + +### Tracking timed interactions + +If you want to track how long it takes a user to do something, you'll need to implement the timing +logic yourself. You'll also need to predefine some buckets into which the UI metric can fall. +For example, if you're timing how long it takes to create a visualization, you may decide to +measure interactions that take less than 1 minute, 1-5 minutes, 5-20 minutes, and longer than 20 minutes. +To track these interactions, you'd use the timed length of the interaction to determine whether to +use a `eventName` of `create_vis_1m`, `create_vis_5m`, `create_vis_20m`, or `create_vis_infinity`. + +## How it works + +Under the hood, your app and metric type will be stored in a saved object of type `user-metric` and the +ID `ui-metric:my_app:my_metric`. This saved object will have a `count` property which will be incremented +every time the above URI is hit. + +These saved objects are automatically consumed by the stats API and surfaced under the +`ui_metric` namespace. + +```json +{ + "ui_metric": { + "my_app": [ + { + "key": "my_metric", + "value": 3 + } + ] + } +} +``` + +By storing these metrics and their counts as key-value pairs, we can add more metrics without having +to worry about exceeding the 1000-field soft limit in Elasticsearch. \ No newline at end of file diff --git a/src/plugins/usage_collection/common/constants.ts b/src/plugins/usage_collection/common/constants.ts index edd06b171a72c..96b24c5e7475e 100644 --- a/src/plugins/usage_collection/common/constants.ts +++ b/src/plugins/usage_collection/common/constants.ts @@ -18,3 +18,4 @@ */ export const KIBANA_STATS_TYPE = 'kibana_stats'; +export const DEFAULT_MAXIMUM_WAIT_TIME_FOR_ALL_COLLECTORS_IN_S = 60; diff --git a/src/plugins/usage_collection/kibana.json b/src/plugins/usage_collection/kibana.json index 145cd89ff884d..ae86b6c5d7ad1 100644 --- a/src/plugins/usage_collection/kibana.json +++ b/src/plugins/usage_collection/kibana.json @@ -3,5 +3,5 @@ "configPath": ["usageCollection"], "version": "kibana", "server": true, - "ui": false + "ui": true } diff --git a/src/plugins/usage_collection/public/index.ts b/src/plugins/usage_collection/public/index.ts new file mode 100644 index 0000000000000..712e6a76152a2 --- /dev/null +++ b/src/plugins/usage_collection/public/index.ts @@ -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 { PluginInitializerContext } from '../../../core/public'; +import { UsageCollectionPlugin } from './plugin'; + +export { METRIC_TYPE } from '@kbn/analytics'; +export { UsageCollectionSetup } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new UsageCollectionPlugin(initializerContext); +} diff --git a/src/plugins/usage_collection/public/mocks.ts b/src/plugins/usage_collection/public/mocks.ts new file mode 100644 index 0000000000000..69fbf56ca5604 --- /dev/null +++ b/src/plugins/usage_collection/public/mocks.ts @@ -0,0 +1,36 @@ +/* + * 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 { UsageCollectionSetup, METRIC_TYPE } from '.'; + +export type Setup = jest.Mocked; + +const createSetupContract = (): Setup => { + const setupContract: Setup = { + allowTrackUserAgent: jest.fn(), + reportUiStats: jest.fn(), + METRIC_TYPE, + }; + + return setupContract; +}; + +export const usageCollectionPluginMock = { + createSetupContract, +}; diff --git a/src/plugins/usage_collection/public/plugin.ts b/src/plugins/usage_collection/public/plugin.ts new file mode 100644 index 0000000000000..2ecc6c8bc2038 --- /dev/null +++ b/src/plugins/usage_collection/public/plugin.ts @@ -0,0 +1,91 @@ +/* + * 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 { Reporter, METRIC_TYPE } from '@kbn/analytics'; +import { Storage } from '../../kibana_utils/public'; +import { createReporter } from './services'; +import { + PluginInitializerContext, + Plugin, + CoreSetup, + CoreStart, + HttpServiceBase, +} from '../../../core/public'; + +interface PublicConfigType { + uiMetric: { + enabled: boolean; + debug: boolean; + }; +} + +export interface UsageCollectionSetup { + allowTrackUserAgent: (allow: boolean) => void; + reportUiStats: Reporter['reportUiStats']; + METRIC_TYPE: typeof METRIC_TYPE; +} + +export function isUnauthenticated(http: HttpServiceBase) { + const { anonymousPaths } = http; + return anonymousPaths.isAnonymous(window.location.pathname); +} + +export class UsageCollectionPlugin implements Plugin { + private trackUserAgent: boolean = true; + private reporter?: Reporter; + private config: PublicConfigType; + constructor(initializerContext: PluginInitializerContext) { + this.config = initializerContext.config.get(); + } + + public setup({ http }: CoreSetup): UsageCollectionSetup { + const localStorage = new Storage(window.localStorage); + const debug = this.config.uiMetric.debug; + + this.reporter = createReporter({ + localStorage, + debug, + fetch: http, + }); + + return { + allowTrackUserAgent: (allow: boolean) => { + this.trackUserAgent = allow; + }, + reportUiStats: this.reporter.reportUiStats, + METRIC_TYPE, + }; + } + + public start({ http }: CoreStart) { + if (!this.reporter) { + return; + } + + if (this.config.uiMetric.enabled && !isUnauthenticated(http)) { + this.reporter.start(); + } + + if (this.trackUserAgent) { + this.reporter.reportUserAgent('kibana'); + } + } + + public stop() {} +} diff --git a/src/legacy/core_plugins/ui_metric/public/hacks/ui_metric_init.ts b/src/plugins/usage_collection/public/services/create_reporter.ts similarity index 50% rename from src/legacy/core_plugins/ui_metric/public/hacks/ui_metric_init.ts rename to src/plugins/usage_collection/public/services/create_reporter.ts index 983434f09922b..6bc35de8972c3 100644 --- a/src/legacy/core_plugins/ui_metric/public/hacks/ui_metric_init.ts +++ b/src/plugins/usage_collection/public/services/create_reporter.ts @@ -17,29 +17,30 @@ * under the License. */ -// @ts-ignore -import { uiModules } from 'ui/modules'; -import chrome from 'ui/chrome'; -import { kfetch } from 'ui/kfetch'; -import { - createAnalyticsReporter, - setTelemetryReporter, - trackUserAgent, -} from '../services/telemetry_analytics'; -import { isUnauthenticated } from '../../../telemetry/public/services'; +import { Reporter, Storage } from '@kbn/analytics'; +import { HttpServiceBase } from 'kibana/public'; -function telemetryInit($injector: any) { - const uiMetricEnabled = chrome.getInjected('uiMetricEnabled'); - const debug = chrome.getInjected('debugUiMetric'); - if (!uiMetricEnabled || isUnauthenticated()) { - return; - } - const localStorage = $injector.get('localStorage'); - - const uiReporter = createAnalyticsReporter({ localStorage, debug, kfetch }); - setTelemetryReporter(uiReporter); - uiReporter.start(); - trackUserAgent('kibana'); +interface AnalyicsReporterConfig { + localStorage: Storage; + debug: boolean; + fetch: HttpServiceBase; } -uiModules.get('kibana').run(telemetryInit); +export function createReporter(config: AnalyicsReporterConfig): Reporter { + const { localStorage, debug, fetch } = config; + + return new Reporter({ + debug, + storage: localStorage, + async http(report) { + const response = await fetch.post('/api/ui_metric/report', { + body: JSON.stringify({ report }), + }); + + if (response.status !== 'ok') { + throw Error('Unable to store report.'); + } + return response; + }, + }); +} diff --git a/src/legacy/core_plugins/ui_metric/common/index.ts b/src/plugins/usage_collection/public/services/index.ts similarity index 93% rename from src/legacy/core_plugins/ui_metric/common/index.ts rename to src/plugins/usage_collection/public/services/index.ts index 02aa55c30965d..7703d5cc3bfeb 100644 --- a/src/legacy/core_plugins/ui_metric/common/index.ts +++ b/src/plugins/usage_collection/public/services/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export const API_BASE_PATH = '/api/ui_metric'; +export { createReporter } from './create_reporter'; diff --git a/src/plugins/usage_collection/server/config.ts b/src/plugins/usage_collection/server/config.ts index 987db1f2b0ff3..76379d9385cff 100644 --- a/src/plugins/usage_collection/server/config.ts +++ b/src/plugins/usage_collection/server/config.ts @@ -17,8 +17,29 @@ * under the License. */ -import { schema } from '@kbn/config-schema'; +import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from 'kibana/server'; +import { DEFAULT_MAXIMUM_WAIT_TIME_FOR_ALL_COLLECTORS_IN_S } from '../common/constants'; -export const ConfigSchema = schema.object({ - maximumWaitTimeForAllCollectorsInS: schema.number({ defaultValue: 60 }), +export const configSchema = schema.object({ + uiMetric: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + debug: schema.boolean({ defaultValue: schema.contextRef('dev') }), + }), + maximumWaitTimeForAllCollectorsInS: schema.number({ + defaultValue: DEFAULT_MAXIMUM_WAIT_TIME_FOR_ALL_COLLECTORS_IN_S, + }), }); + +export type ConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, + deprecations: ({ renameFromRoot }) => [ + renameFromRoot('ui_metric.enabled', 'usageCollection.uiMetric.enabled'), + renameFromRoot('ui_metric.debug', 'usageCollection.uiMetric.debug'), + ], + exposeToBrowser: { + uiMetric: true, + }, +}; diff --git a/src/plugins/usage_collection/server/index.ts b/src/plugins/usage_collection/server/index.ts index 33a1a0adc6713..6a28dba50a915 100644 --- a/src/plugins/usage_collection/server/index.ts +++ b/src/plugins/usage_collection/server/index.ts @@ -18,10 +18,9 @@ */ import { PluginInitializerContext } from '../../../../src/core/server'; -import { Plugin } from './plugin'; -import { ConfigSchema } from './config'; +import { UsageCollectionPlugin } from './plugin'; export { UsageCollectionSetup } from './plugin'; -export const config = { schema: ConfigSchema }; +export { config } from './config'; export const plugin = (initializerContext: PluginInitializerContext) => - new Plugin(initializerContext); + new UsageCollectionPlugin(initializerContext); diff --git a/src/plugins/usage_collection/server/plugin.ts b/src/plugins/usage_collection/server/plugin.ts index e8bbc8e512a41..5c5b58ae84936 100644 --- a/src/plugins/usage_collection/server/plugin.ts +++ b/src/plugins/usage_collection/server/plugin.ts @@ -18,22 +18,25 @@ */ import { first } from 'rxjs/operators'; -import { TypeOf } from '@kbn/config-schema'; -import { ConfigSchema } from './config'; -import { PluginInitializerContext, Logger } from '../../../../src/core/server'; +import { ConfigType } from './config'; +import { PluginInitializerContext, Logger, CoreSetup } from '../../../../src/core/server'; import { CollectorSet } from './collector'; +import { setupRoutes } from './routes'; -export type UsageCollectionSetup = CollectorSet; +export type UsageCollectionSetup = CollectorSet & { + registerLegacySavedObjects: (legacySavedObjects: any) => void; +}; -export class Plugin { +export class UsageCollectionPlugin { logger: Logger; + private legacySavedObjects: any; constructor(private readonly initializerContext: PluginInitializerContext) { this.logger = this.initializerContext.logger.get(); } - public async setup(): Promise { + public async setup(core: CoreSetup) { const config = await this.initializerContext.config - .create>() + .create() .pipe(first()) .toPromise(); @@ -42,7 +45,16 @@ export class Plugin { maximumWaitTimeForAllCollectorsInS: config.maximumWaitTimeForAllCollectorsInS, }); - return collectorSet; + const router = core.http.createRouter(); + const getLegacySavedObjects = () => this.legacySavedObjects; + setupRoutes(router, getLegacySavedObjects); + + return { + ...collectorSet, + registerLegacySavedObjects: (legacySavedObjects: any) => { + this.legacySavedObjects = legacySavedObjects; + }, + }; } public start() { diff --git a/src/legacy/ui/public/ui_metric/index.ts b/src/plugins/usage_collection/server/report/index.ts similarity index 80% rename from src/legacy/ui/public/ui_metric/index.ts rename to src/plugins/usage_collection/server/report/index.ts index ad43f27201ce4..bb8c1d149fdd2 100644 --- a/src/legacy/ui/public/ui_metric/index.ts +++ b/src/plugins/usage_collection/server/report/index.ts @@ -17,12 +17,5 @@ * under the License. */ -let _canTrackUiMetrics = false; - -export function setCanTrackUiMetrics(flag: boolean) { - _canTrackUiMetrics = flag; -} - -export function getCanTrackUiMetrics(): boolean { - return _canTrackUiMetrics; -} +export { storeReport } from './store_report'; +export { reportSchema } from './schema'; diff --git a/src/plugins/usage_collection/server/report/schema.ts b/src/plugins/usage_collection/server/report/schema.ts new file mode 100644 index 0000000000000..5adf7d6575a70 --- /dev/null +++ b/src/plugins/usage_collection/server/report/schema.ts @@ -0,0 +1,59 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; +import { METRIC_TYPE } from '@kbn/analytics'; + +export const reportSchema = schema.object({ + reportVersion: schema.maybe(schema.literal(1)), + userAgent: schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + key: schema.string(), + type: schema.string(), + appName: schema.string(), + userAgent: schema.string(), + }) + ) + ), + uiStatsMetrics: schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + key: schema.string(), + type: schema.oneOf([ + schema.literal(METRIC_TYPE.CLICK), + schema.literal(METRIC_TYPE.LOADED), + schema.literal(METRIC_TYPE.COUNT), + ]), + appName: schema.string(), + eventName: schema.string(), + stats: schema.object({ + min: schema.number(), + sum: schema.number(), + max: schema.number(), + avg: schema.number(), + }), + }) + ) + ), +}); + +export type ReportSchemaType = TypeOf; diff --git a/src/plugins/usage_collection/server/report/store_report.ts b/src/plugins/usage_collection/server/report/store_report.ts new file mode 100644 index 0000000000000..9232a23d6151b --- /dev/null +++ b/src/plugins/usage_collection/server/report/store_report.ts @@ -0,0 +1,44 @@ +/* + * 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 { ReportSchemaType } from './schema'; + +export async function storeReport(internalRepository: any, report: ReportSchemaType) { + const uiStatsMetrics = report.uiStatsMetrics ? Object.entries(report.uiStatsMetrics) : []; + const userAgents = report.userAgent ? Object.entries(report.userAgent) : []; + return Promise.all([ + ...userAgents.map(async ([key, metric]) => { + const { userAgent } = metric; + const savedObjectId = `${key}:${userAgent}`; + return await internalRepository.create( + 'ui-metric', + { count: 1 }, + { + id: savedObjectId, + overwrite: true, + } + ); + }), + ...uiStatsMetrics.map(async ([key, metric]) => { + const { appName, eventName } = metric; + const savedObjectId = `${appName}:${eventName}`; + return await internalRepository.incrementCounter('ui-metric', savedObjectId, 'count'); + }), + ]); +} diff --git a/src/plugins/usage_collection/server/routes/index.ts b/src/plugins/usage_collection/server/routes/index.ts new file mode 100644 index 0000000000000..9e0d74add57bd --- /dev/null +++ b/src/plugins/usage_collection/server/routes/index.ts @@ -0,0 +1,25 @@ +/* + * 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 { IRouter } from '../../../../../src/core/server'; +import { registerUiMetricRoute } from './report_metrics'; + +export function setupRoutes(router: IRouter, getLegacySavedObjects: any) { + registerUiMetricRoute(router, getLegacySavedObjects); +} diff --git a/src/plugins/usage_collection/server/routes/report_metrics.ts b/src/plugins/usage_collection/server/routes/report_metrics.ts new file mode 100644 index 0000000000000..93f03ea8067d2 --- /dev/null +++ b/src/plugins/usage_collection/server/routes/report_metrics.ts @@ -0,0 +1,45 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { IRouter } from '../../../../../src/core/server'; +import { storeReport, reportSchema } from '../report'; + +export function registerUiMetricRoute(router: IRouter, getLegacySavedObjects: () => any) { + router.post( + { + path: '/api/ui_metric/report', + validate: { + body: schema.object({ + report: reportSchema, + }), + }, + }, + async (context, req, res) => { + const { report } = req.body; + try { + const internalRepository = getLegacySavedObjects(); + await storeReport(internalRepository, report); + return res.ok({ body: { status: 'ok' } }); + } catch (error) { + return res.ok({ body: { status: 'fail' } }); + } + } + ); +} diff --git a/test/api_integration/apis/ui_metric/ui_metric.js b/test/api_integration/apis/ui_metric/ui_metric.js index 51959bf5f7fda..3669d0e21c776 100644 --- a/test/api_integration/apis/ui_metric/ui_metric.js +++ b/test/api_integration/apis/ui_metric/ui_metric.js @@ -49,10 +49,10 @@ export default function ({ getService }) { } }; await supertest - .post('/api/telemetry/report') + .post('/api/ui_metric/report') .set('kbn-xsrf', 'kibana') .set('content-type', 'application/json') - .send(report) + .send({ report }) .expect(200); const response = await es.search({ index: '.kibana', q: 'type:ui-metric' }); @@ -77,10 +77,10 @@ export default function ({ getService }) { } }; await supertest - .post('/api/telemetry/report') + .post('/api/ui_metric/report') .set('kbn-xsrf', 'kibana') .set('content-type', 'application/json') - .send(report) + .send({ report }) .expect(200); const response = await es.search({ index: '.kibana', q: 'type:ui-metric' }); diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx index 9f48880090369..9d9543cb68ddd 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx @@ -17,7 +17,7 @@ import { SessionStorageMock } from '../../../../services/__test__/SessionStorage function renderServiceOverview() { return render(); } - +jest.mock('ui/new_platform'); const coreMock = ({ http: { basePath: { diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/__jest__/TransactionOverview.test.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/__jest__/TransactionOverview.test.tsx index a5356be72f5e4..19cf04fedf95e 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/__jest__/TransactionOverview.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/__jest__/TransactionOverview.test.tsx @@ -27,6 +27,7 @@ import { LegacyCoreStart } from 'kibana/public'; jest.spyOn(history, 'push'); jest.spyOn(history, 'replace'); +jest.mock('ui/new_platform'); const coreMock = ({ notifications: { toasts: { addWarning: () => {} } } } as unknown) as LegacyCoreStart; diff --git a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/__tests__/workpad_telemetry.test.tsx b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.test.tsx similarity index 97% rename from x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/__tests__/workpad_telemetry.test.tsx rename to x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.test.tsx index d486440c1fd7d..e855a381413eb 100644 --- a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/__tests__/workpad_telemetry.test.tsx +++ b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.test.tsx @@ -10,9 +10,10 @@ import { withUnconnectedElementsLoadedTelemetry, WorkpadLoadedMetric, WorkpadLoadedWithErrorsMetric, -} from '../workpad_telemetry'; -import { METRIC_TYPE } from '../../../../lib/ui_metric'; +} from './workpad_telemetry'; +import { METRIC_TYPE } from '../../../lib/ui_metric'; +jest.mock('ui/new_platform'); const trackMetric = jest.fn(); const Component = withUnconnectedElementsLoadedTelemetry(() =>
, trackMetric); diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/components/follower_index_form/follower_index_form.test.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/components/follower_index_form/follower_index_form.test.js index 74fdf3301bcca..d1dfd7a206289 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/components/follower_index_form/follower_index_form.test.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/components/follower_index_form/follower_index_form.test.js @@ -7,6 +7,7 @@ import { updateFields, updateFormErrors } from './follower_index_form'; +jest.mock('ui/new_platform'); jest.mock('ui/indices', () => ({ INDEX_ILLEGAL_CHARACTERS_VISIBLE: [], })); diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/store/reducers/api.test.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/store/reducers/api.test.js index 43d1da3f242a2..c7b4b56ad1240 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/store/reducers/api.test.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/store/reducers/api.test.js @@ -8,6 +8,7 @@ import { reducer, initialState } from './api'; import { API_STATUS } from '../../constants'; import { apiRequestStart, apiRequestEnd, setApiError } from '../actions'; +jest.mock('ui/new_platform'); jest.mock('../../constants', () => ({ API_STATUS: { IDLE: 'idle', diff --git a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.js b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.js index a97296e94042d..059a6a6f8221a 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.js @@ -33,6 +33,8 @@ import { maximumDocumentsRequiredMessage, } from '../../public/store/selectors/lifecycle'; +jest.mock('ui/new_platform'); + let server; let store; const policy = { diff --git a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/components/policy_table.test.js b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/components/policy_table.test.js index 31d8337857911..121f7d3bcda0d 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/components/policy_table.test.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/components/policy_table.test.js @@ -17,6 +17,7 @@ import axiosXhrAdapter from 'axios/lib/adapters/xhr'; import { setHttpClient } from '../../public/services/api'; setHttpClient(axios.create({ adapter: axiosXhrAdapter })); import sinon from 'sinon'; +jest.mock('ui/new_platform'); let server = null; let store = null; diff --git a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js index ed5069a56141c..25a76b5db28f6 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js @@ -14,6 +14,8 @@ import { ilmFilterExtension, ilmSummaryExtension, } from '../public/extend_index_management'; + +jest.mock('ui/new_platform'); const indexWithoutLifecyclePolicy = { health: 'yellow', status: 'open', diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/services/ui_metric.test.js b/x-pack/legacy/plugins/index_lifecycle_management/public/services/ui_metric.test.js index ab100db97d70c..1fe1d0ccf6afa 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/public/services/ui_metric.test.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/public/services/ui_metric.test.js @@ -21,6 +21,7 @@ import { } from '../constants'; import { getUiMetricsForPhases } from './ui_metric'; +jest.mock('ui/new_platform'); describe('getUiMetricsForPhases', () => { test('gets cold phase', () => { diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js b/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js index c3bf8668af8dc..329c1558c2710 100644 --- a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js +++ b/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js @@ -15,7 +15,6 @@ import { init as initHttp } from '../../../public/app/services/http'; import { init as initNotification } from '../../../public/app/services/notification'; import { init as initUiMetric } from '../../../public/app/services/ui_metric'; import { init as initHttpRequests } from './http_requests'; -import { createUiStatsReporter } from '../../../../../../../src/legacy/core_plugins/ui_metric/public'; export const setupEnvironment = () => { chrome.breadcrumbs = { @@ -25,7 +24,7 @@ export const setupEnvironment = () => { initHttp(axios.create({ adapter: axiosXhrAdapter }), (path) => path); initBreadcrumb(() => {}, MANAGEMENT_BREADCRUMB); initNotification(toastNotifications, fatalError); - initUiMetric(createUiStatsReporter); + initUiMetric(() => () => {}); const { server, httpRequestsMockHelpers } = initHttpRequests(); diff --git a/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js b/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js index f7b75bcfad6f4..b1b9eb48d2e6e 100644 --- a/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js +++ b/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js @@ -17,6 +17,7 @@ import { tabToHumanizedMap, } from '../../components'; +jest.mock('ui/new_platform'); jest.mock('../../../services', () => { const services = require.requireActual('../../../services'); return { diff --git a/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js b/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js index aabe240af0e6b..3a565909ed68c 100644 --- a/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js +++ b/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js @@ -8,6 +8,8 @@ import { registerTestBed } from '../../../../../../../test_utils'; import { rollupJobsStore } from '../../store'; import { JobList } from './job_list'; +jest.mock('ui/new_platform'); + jest.mock('ui/chrome', () => ({ addBasePath: () => {}, breadcrumbs: { set: () => {} }, diff --git a/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js b/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js index a68c805390807..2a116e7b0e1f3 100644 --- a/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js +++ b/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js @@ -11,6 +11,7 @@ import { getJobs, jobCount } from '../../../../../fixtures'; import { rollupJobsStore } from '../../../store'; import { JobTable } from './job_table'; +jest.mock('ui/new_platform'); jest.mock('../../../services', () => { const services = require.requireActual('../../../services'); return { diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.test.tsx index acea2d1cce468..66e9bc700b3a1 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.test.tsx @@ -19,6 +19,7 @@ const testWidth = 640; const usersViewing = ['elastic']; const mockUseKibanaCore = useKibanaCore as jest.Mock; +jest.mock('ui/new_platform'); jest.mock('../../../lib/compose/kibana_core'); mockUseKibanaCore.mockImplementation(() => ({ uiSettings: mockUiSettings, diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx index fbeb1a2090cfd..d7061ba4efd9c 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx @@ -15,6 +15,7 @@ import { HostsTableType } from '../../store/hosts/model'; import { RouteSpyState } from '../../utils/route/types'; import { SiemNavigationProps, SiemNavigationComponentProps } from './types'; +jest.mock('ui/new_platform'); jest.mock('./breadcrumbs', () => ({ setBreadcrumbs: jest.fn(), })); diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx index e84e3066e4f69..00b1d4c066d4a 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx @@ -16,6 +16,8 @@ import { CONSTANTS } from '../../url_state/constants'; import { TabNavigationComponent } from './'; import { TabNavigationProps } from './types'; +jest.mock('ui/new_platform'); + describe('Tab Navigation', () => { const pageName = SiemPageName.hosts; const hostName = 'siem-window';