From d280d71361856af84ef0f4277e9688c49f5a5fe0 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 4 Mar 2021 17:33:09 +0300 Subject: [PATCH 01/17] Make 'enter time range' value as default and add telemetry for 'last value' mode --- src/plugins/vis_type_timeseries/kibana.json | 1 + .../application/components/index_pattern.js | 14 +-- .../vis_type_timeseries/server/plugin.ts | 6 ++ .../vis_type_timeseries/server/types.ts | 4 + .../get_usage_collector.mock.ts | 14 +++ .../get_usage_collector.test.ts | 91 +++++++++++++++++++ .../usage_collector/get_usage_collector.ts | 71 +++++++++++++++ .../server/usage_collector/index.ts | 9 ++ .../register_timeseries_collector.test.ts | 57 ++++++++++++ .../register_timeseries_collector.ts | 32 +++++++ 10 files changed, 292 insertions(+), 7 deletions(-) create mode 100644 src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.mock.ts create mode 100644 src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts create mode 100644 src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts create mode 100644 src/plugins/vis_type_timeseries/server/usage_collector/index.ts create mode 100644 src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts create mode 100644 src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts diff --git a/src/plugins/vis_type_timeseries/kibana.json b/src/plugins/vis_type_timeseries/kibana.json index aa5eac84663ad..242b62a2c5ee4 100644 --- a/src/plugins/vis_type_timeseries/kibana.json +++ b/src/plugins/vis_type_timeseries/kibana.json @@ -5,5 +5,6 @@ "server": true, "ui": true, "requiredPlugins": ["charts", "data", "expressions", "visualizations", "visualize"], + "optionalPlugins": ["usageCollection"], "requiredBundles": ["kibanaUtils", "kibanaReact"] } diff --git a/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js b/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js index d36cea80bffff..e1ca48883696a 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js +++ b/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js @@ -89,13 +89,6 @@ export const IndexPattern = ({ const handleTextChange = createTextHandler(onChange); const timeRangeOptions = [ - { - label: i18n.translate('visTypeTimeseries.indexPattern.timeRange.lastValue', { - defaultMessage: 'Last value', - }), - value: TIME_RANGE_DATA_MODES.LAST_VALUE, - disabled: !isTimerangeModeEnabled(TIME_RANGE_DATA_MODES.LAST_VALUE, uiRestrictions), - }, { label: i18n.translate('visTypeTimeseries.indexPattern.timeRange.entireTimeRange', { defaultMessage: 'Entire time range', @@ -103,6 +96,13 @@ export const IndexPattern = ({ value: TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, disabled: !isTimerangeModeEnabled(TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, uiRestrictions), }, + { + label: i18n.translate('visTypeTimeseries.indexPattern.timeRange.lastValue', { + defaultMessage: 'Last value', + }), + value: TIME_RANGE_DATA_MODES.LAST_VALUE, + disabled: !isTimerangeModeEnabled(TIME_RANGE_DATA_MODES.LAST_VALUE, uiRestrictions), + }, ]; const defaults = { diff --git a/src/plugins/vis_type_timeseries/server/plugin.ts b/src/plugins/vis_type_timeseries/server/plugin.ts index cd53d3b1308a5..a9f2e7ce9c8ea 100644 --- a/src/plugins/vis_type_timeseries/server/plugin.ts +++ b/src/plugins/vis_type_timeseries/server/plugin.ts @@ -32,6 +32,8 @@ import { RollupSearchStrategy, } from './lib/search_strategies'; +import { registerTimeseriesUsageCollector } from './usage_collector'; + export interface LegacySetup { server: Server; } @@ -96,6 +98,10 @@ export class VisTypeTimeseriesPlugin implements Plugin { visDataRoutes(router, framework); fieldsRoutes(framework); + if (plugins.usageCollection) { + registerTimeseriesUsageCollector(plugins.usageCollection, globalConfig$); + } + return { getVisData: async ( requestContext: VisTypeTimeseriesRequestHandlerContext, diff --git a/src/plugins/vis_type_timeseries/server/types.ts b/src/plugins/vis_type_timeseries/server/types.ts index 88a7eff517dff..db266360d7a39 100644 --- a/src/plugins/vis_type_timeseries/server/types.ts +++ b/src/plugins/vis_type_timeseries/server/types.ts @@ -6,8 +6,12 @@ * Side Public License, v 1. */ +import { Observable } from 'rxjs'; +import { SharedGlobalConfig } from 'kibana/server'; import type { IRouter } from 'src/core/server'; import type { DataRequestHandlerContext } from '../../data/server'; +export type ConfigObservable = Observable; + export type VisTypeTimeseriesRequestHandlerContext = DataRequestHandlerContext; export type VisTypeTimeseriesRouter = IRouter; diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.mock.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.mock.ts new file mode 100644 index 0000000000000..bb52d215c67e8 --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.mock.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const mockStats = { somestat: 1 }; +export const mockGetStats = jest.fn().mockResolvedValue(mockStats); + +jest.doMock('./get_usage_collector', () => ({ + getStats: mockGetStats, +})); diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts new file mode 100644 index 0000000000000..519de43b3d657 --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getStats } from './get_usage_collector'; +import { createCollectorFetchContextMock } from 'src/plugins/usage_collection/server/mocks'; +import { TIME_RANGE_DATA_MODES } from '../../common/timerange_data_modes'; + +const mockedSavedObjects = [ + { + _id: 'visualization:timeseries-123', + _source: { + type: 'visualization', + visualization: { + visState: JSON.stringify({ + type: 'metrics', + title: 'TSVB visualization 1', + params: { + time_range_mode: TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + }, + }), + }, + }, + }, + { + _id: 'visualization:timeseries-321', + _source: { + type: 'visualization', + visualization: { + visState: JSON.stringify({ + type: 'metrics', + title: 'TSVB visualization 2', + params: { + time_range_mode: TIME_RANGE_DATA_MODES.LAST_VALUE, + }, + }), + }, + }, + }, +]; + +const getMockCollectorFetchContext = (hits?: unknown[]) => { + const fetchParamsMock = createCollectorFetchContextMock(); + + fetchParamsMock.esClient.search = jest.fn().mockResolvedValue({ body: { hits: { hits } } }); + return fetchParamsMock; +}; + +describe('Timelion visualization usage collector', () => { + const mockIndex = 'mock_index'; + + test('Returns undefined when no results found (undefined)', async () => { + const result = await getStats(getMockCollectorFetchContext().esClient, mockIndex); + + expect(result).toBeUndefined(); + }); + + test('Returns undefined when no results found (0 results)', async () => { + const result = await getStats(getMockCollectorFetchContext([]).esClient, mockIndex); + + expect(result).toBeUndefined(); + }); + + test('Returns undefined when no TSVB saved objects found', async () => { + const mockCollectorFetchContext = getMockCollectorFetchContext([ + { + _id: 'visualization:myvis-123', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "area"}' }, + }, + }, + ]); + const result = await getStats(mockCollectorFetchContext.esClient, mockIndex); + + expect(result).toBeUndefined(); + }); + + test('Summarizes visualizations response data', async () => { + const mockCollectorFetchContext = getMockCollectorFetchContext(mockedSavedObjects); + const result = await getStats(mockCollectorFetchContext.esClient, mockIndex); + + expect(result).toMatchObject({ + timeseries_use_last_value_mode_total: 1, + }); + }); +}); diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts new file mode 100644 index 0000000000000..4d63dff160e0a --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SearchResponse } from 'elasticsearch'; +import { ElasticsearchClient } from 'src/core/server'; +import { TIME_RANGE_DATA_MODES } from '../../common/timerange_data_modes'; + +type ESResponse = SearchResponse<{ visualization: { visState: string }; updated_at: string }>; + +export interface TimeseriesUsage { + timeseries_use_last_value_mode_total: number; +} + +interface VisState { + type?: string; + params?: any; +} + +export const getStats = async ( + esClient: ElasticsearchClient, + index: string +): Promise => { + const timeseriesUsage = { + timeseries_use_last_value_mode_total: 0, + }; + + const searchParams = { + size: 10000, + index, + ignoreUnavailable: true, + filterPath: ['hits.hits._id', 'hits.hits._source.visualization'], + body: { + query: { + bool: { + filter: { term: { type: 'visualization' } }, + }, + }, + }, + }; + + const { body: esResponse } = await esClient.search(searchParams); + const size = esResponse?.hits?.hits?.length ?? 0; + + if (!size) { + return; + } + + for (const hit of esResponse.hits.hits) { + const visualization = hit._source?.visualization; + let visState: VisState = {}; + try { + visState = JSON.parse(visualization?.visState ?? '{}'); + } catch (e) { + // invalid visState + } + + if ( + visState.type === 'metrics' && + visState.params.time_range_mode === TIME_RANGE_DATA_MODES.LAST_VALUE + ) { + timeseriesUsage.timeseries_use_last_value_mode_total++; + } + } + + return timeseriesUsage.timeseries_use_last_value_mode_total ? timeseriesUsage : undefined; +}; diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/index.ts b/src/plugins/vis_type_timeseries/server/usage_collector/index.ts new file mode 100644 index 0000000000000..7f72662e154ea --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/usage_collector/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { registerTimeseriesUsageCollector } from './register_timeseries_collector'; diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts b/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts new file mode 100644 index 0000000000000..b7e940ee32e32 --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { of } from 'rxjs'; +import { mockStats, mockGetStats } from './get_usage_collector.mock'; +import { createUsageCollectionSetupMock } from 'src/plugins/usage_collection/server/usage_collection.mock'; +import { createCollectorFetchContextMock } from 'src/plugins/usage_collection/server/mocks'; +import { registerTimeseriesUsageCollector } from './register_timeseries_collector'; +import { ConfigObservable } from '../types'; + +describe('registerTimelionUsageCollector', () => { + const mockIndex = 'mock_index'; + const mockConfig = of({ kibana: { index: mockIndex } }) as ConfigObservable; + + it('makes a usage collector and registers it`', () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerTimeseriesUsageCollector(mockCollectorSet, mockConfig); + expect(mockCollectorSet.makeUsageCollector).toBeCalledTimes(1); + expect(mockCollectorSet.registerCollector).toBeCalledTimes(1); + }); + + it('makeUsageCollector configs fit the shape', () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerTimeseriesUsageCollector(mockCollectorSet, mockConfig); + expect(mockCollectorSet.makeUsageCollector).toHaveBeenCalledWith({ + type: 'vis_type_timeseries', + isReady: expect.any(Function), + fetch: expect.any(Function), + schema: expect.any(Object), + }); + const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0]; + expect(usageCollectorConfig.isReady()).toBe(true); + }); + + it('makeUsageCollector config.isReady returns true', () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerTimeseriesUsageCollector(mockCollectorSet, mockConfig); + const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0]; + expect(usageCollectorConfig.isReady()).toBe(true); + }); + + it('makeUsageCollector config.fetch calls getStats', async () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerTimeseriesUsageCollector(mockCollectorSet, mockConfig); + const usageCollector = mockCollectorSet.makeUsageCollector.mock.results[0].value; + const mockedCollectorFetchContext = createCollectorFetchContextMock(); + const fetchResult = await usageCollector.fetch(mockedCollectorFetchContext); + expect(mockGetStats).toBeCalledTimes(1); + expect(mockGetStats).toBeCalledWith(mockedCollectorFetchContext.esClient, mockIndex); + expect(fetchResult).toBe(mockStats); + }); +}); diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts new file mode 100644 index 0000000000000..b8af74be440a5 --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { first } from 'rxjs/operators'; +import { getStats, TimeseriesUsage } from './get_usage_collector'; +import { ConfigObservable } from '../types'; + +export function registerTimeseriesUsageCollector( + collectorSet: UsageCollectionSetup, + config: ConfigObservable +) { + const collector = collectorSet.makeUsageCollector({ + type: 'vis_type_timeseries', + isReady: () => true, + schema: { + timeseries_use_last_value_mode_total: { type: 'long' }, + }, + fetch: async ({ esClient }) => { + const { index } = (await config.pipe(first()).toPromise()).kibana; + + return await getStats(esClient, index); + }, + }); + + collectorSet.registerCollector(collector); +} From c40435cd8b3893b7fba7a7fb5d39eb35c69469fd Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 4 Mar 2021 17:57:28 +0300 Subject: [PATCH 02/17] Fix telemetry schema --- src/plugins/telemetry/schema/oss_plugins.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index f7795dbf9b2f8..a5f39a5b9ef8c 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -9327,6 +9327,13 @@ } } }, + "vis_type_timeseries": { + "properties": { + "timeseries_use_last_value_mode_total": { + "type": "long" + } + } + }, "vis_type_vega": { "properties": { "vega_lib_specs_total": { From 1d97f648be3c887c2407aa39015e0fd9f15fd622 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 5 Mar 2021 15:58:02 +0300 Subject: [PATCH 03/17] Fix test --- test/functional/page_objects/visual_builder_page.ts | 7 +++++++ x-pack/test/functional/apps/rollup_job/tsvb.js | 1 + 2 files changed, 8 insertions(+) diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 963a6bff0cd0b..d0bb0cbcb5c8a 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -614,6 +614,13 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro ); return await comboBox.isOptionSelected(groupBy, value); } + + public async setMetricsDataTimerangeMode(value: string) { + const timerangeMode = await find.byCssSelector( + '[id$="_timeRange-row"] [data-test-subj="comboBoxInput"]' + ); + return await comboBox.setElement(timerangeMode, value); + } } return new VisualBuilderPage(); diff --git a/x-pack/test/functional/apps/rollup_job/tsvb.js b/x-pack/test/functional/apps/rollup_job/tsvb.js index 2172a149de6f7..448acc1089fb5 100644 --- a/x-pack/test/functional/apps/rollup_job/tsvb.js +++ b/x-pack/test/functional/apps/rollup_job/tsvb.js @@ -84,6 +84,7 @@ export default function ({ getService, getPageObjects }) { ); await PageObjects.visualBuilder.clickPanelOptions('metric'); await PageObjects.visualBuilder.setIndexPatternValue(rollupTargetIndexName); + await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value'); await PageObjects.visualBuilder.setIntervalValue('1d'); await PageObjects.visualBuilder.setDropLastBucket(false); await PageObjects.common.sleep(3000); From aa3e14e8f4d0e57ccbb6498ee3eb44ac771d437c Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 10 Mar 2021 18:04:46 +0300 Subject: [PATCH 04/17] Add possibility count timeseries created from dashboard --- .../get_usage_collector.test.ts | 31 +++++++++++++++---- .../usage_collector/get_usage_collector.ts | 23 +++++++++++--- .../register_timeseries_collector.test.ts | 8 +++-- .../register_timeseries_collector.ts | 4 +-- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts index 519de43b3d657..dfa0eee0c7fae 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts @@ -47,25 +47,36 @@ const getMockCollectorFetchContext = (hits?: unknown[]) => { const fetchParamsMock = createCollectorFetchContextMock(); fetchParamsMock.esClient.search = jest.fn().mockResolvedValue({ body: { hits: { hits } } }); + fetchParamsMock.soClient.find = jest.fn().mockResolvedValue({ saved_objects: [] }); return fetchParamsMock; }; -describe('Timelion visualization usage collector', () => { +describe('Timeseries visualization usage collector', () => { const mockIndex = 'mock_index'; test('Returns undefined when no results found (undefined)', async () => { - const result = await getStats(getMockCollectorFetchContext().esClient, mockIndex); + const mockCollectorFetchContext = getMockCollectorFetchContext([]); + const result = await getStats( + mockCollectorFetchContext.esClient, + mockCollectorFetchContext.soClient, + mockIndex + ); expect(result).toBeUndefined(); }); test('Returns undefined when no results found (0 results)', async () => { - const result = await getStats(getMockCollectorFetchContext([]).esClient, mockIndex); + const mockCollectorFetchContext = getMockCollectorFetchContext([]); + const result = await getStats( + mockCollectorFetchContext.esClient, + mockCollectorFetchContext.soClient, + mockIndex + ); expect(result).toBeUndefined(); }); - test('Returns undefined when no TSVB saved objects found', async () => { + test('Returns undefined when no timeseries saved objects found', async () => { const mockCollectorFetchContext = getMockCollectorFetchContext([ { _id: 'visualization:myvis-123', @@ -75,14 +86,22 @@ describe('Timelion visualization usage collector', () => { }, }, ]); - const result = await getStats(mockCollectorFetchContext.esClient, mockIndex); + const result = await getStats( + mockCollectorFetchContext.esClient, + mockCollectorFetchContext.soClient, + mockIndex + ); expect(result).toBeUndefined(); }); test('Summarizes visualizations response data', async () => { const mockCollectorFetchContext = getMockCollectorFetchContext(mockedSavedObjects); - const result = await getStats(mockCollectorFetchContext.esClient, mockIndex); + const result = await getStats( + mockCollectorFetchContext.esClient, + mockCollectorFetchContext.soClient, + mockIndex + ); expect(result).toMatchObject({ timeseries_use_last_value_mode_total: 1, diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts index 4d63dff160e0a..5a5a7ab119034 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts @@ -8,7 +8,9 @@ import { SearchResponse } from 'elasticsearch'; import { ElasticsearchClient } from 'src/core/server'; +import { SavedObjectsClientContract, ISavedObjectsRepository } from 'kibana/server'; import { TIME_RANGE_DATA_MODES } from '../../common/timerange_data_modes'; +import { findByValueEmbeddables } from '../../../dashboard/server'; type ESResponse = SearchResponse<{ visualization: { visState: string }; updated_at: string }>; @@ -23,6 +25,7 @@ interface VisState { export const getStats = async ( esClient: ElasticsearchClient, + soClient: SavedObjectsClientContract | ISavedObjectsRepository, index: string ): Promise => { const timeseriesUsage = { @@ -50,6 +53,7 @@ export const getStats = async ( return; } + const timeseriesEmbeddables: VisState[] = []; for (const hit of esResponse.hits.hits) { const visualization = hit._source?.visualization; let visState: VisState = {}; @@ -59,10 +63,21 @@ export const getStats = async ( // invalid visState } - if ( - visState.type === 'metrics' && - visState.params.time_range_mode === TIME_RANGE_DATA_MODES.LAST_VALUE - ) { + if (visState.type === 'metrics') { + timeseriesEmbeddables.push(visState); + } + } + + const byValueVisualizations = await findByValueEmbeddables(soClient, 'visualization'); + + for (const item of byValueVisualizations) { + if ((item.savedVis as { type: string }).type === 'metrics') { + timeseriesEmbeddables.push(item.savedVis as VisState); + } + } + + for (const visState of timeseriesEmbeddables) { + if (visState.params.time_range_mode === TIME_RANGE_DATA_MODES.LAST_VALUE) { timeseriesUsage.timeseries_use_last_value_mode_total++; } } diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts b/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts index b7e940ee32e32..2612a3882af2d 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts @@ -13,7 +13,7 @@ import { createCollectorFetchContextMock } from 'src/plugins/usage_collection/se import { registerTimeseriesUsageCollector } from './register_timeseries_collector'; import { ConfigObservable } from '../types'; -describe('registerTimelionUsageCollector', () => { +describe('registerTimeseriesUsageCollector', () => { const mockIndex = 'mock_index'; const mockConfig = of({ kibana: { index: mockIndex } }) as ConfigObservable; @@ -51,7 +51,11 @@ describe('registerTimelionUsageCollector', () => { const mockedCollectorFetchContext = createCollectorFetchContextMock(); const fetchResult = await usageCollector.fetch(mockedCollectorFetchContext); expect(mockGetStats).toBeCalledTimes(1); - expect(mockGetStats).toBeCalledWith(mockedCollectorFetchContext.esClient, mockIndex); + expect(mockGetStats).toBeCalledWith( + mockedCollectorFetchContext.esClient, + mockedCollectorFetchContext.soClient, + mockIndex + ); expect(fetchResult).toBe(mockStats); }); }); diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts index b8af74be440a5..aea1b07abf1c5 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts @@ -21,10 +21,10 @@ export function registerTimeseriesUsageCollector( schema: { timeseries_use_last_value_mode_total: { type: 'long' }, }, - fetch: async ({ esClient }) => { + fetch: async ({ esClient, soClient }) => { const { index } = (await config.pipe(first()).toPromise()).kibana; - return await getStats(esClient, index); + return await getStats(esClient, soClient, index); }, }); From 022536d33aeebcfa804e5842d49cee105322977e Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 11 Mar 2021 11:13:35 +0300 Subject: [PATCH 05/17] Fix remark --- .../usage_collector/get_usage_collector.ts | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts index 5a5a7ab119034..98ddd7f58ea12 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts @@ -53,7 +53,15 @@ export const getStats = async ( return; } - const timeseriesEmbeddables: VisState[] = []; + function telemetryUseLastValueMode(visState: VisState) { + if ( + visState.type === 'metrics' && + visState.params.time_range_mode === TIME_RANGE_DATA_MODES.LAST_VALUE + ) { + timeseriesUsage.timeseries_use_last_value_mode_total++; + } + } + for (const hit of esResponse.hits.hits) { const visualization = hit._source?.visualization; let visState: VisState = {}; @@ -63,23 +71,13 @@ export const getStats = async ( // invalid visState } - if (visState.type === 'metrics') { - timeseriesEmbeddables.push(visState); - } + telemetryUseLastValueMode(visState); } const byValueVisualizations = await findByValueEmbeddables(soClient, 'visualization'); for (const item of byValueVisualizations) { - if ((item.savedVis as { type: string }).type === 'metrics') { - timeseriesEmbeddables.push(item.savedVis as VisState); - } - } - - for (const visState of timeseriesEmbeddables) { - if (visState.params.time_range_mode === TIME_RANGE_DATA_MODES.LAST_VALUE) { - timeseriesUsage.timeseries_use_last_value_mode_total++; - } + telemetryUseLastValueMode(item.savedVis as VisState); } return timeseriesUsage.timeseries_use_last_value_mode_total ? timeseriesUsage : undefined; From 07d57eb7314dea2c1fe818f8d3fe2c6752888954 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 11 Mar 2021 15:07:48 +0300 Subject: [PATCH 06/17] Fix remark --- .../public/application/components/index_pattern.js | 1 + test/functional/page_objects/visual_builder_page.ts | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js b/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js index e1ca48883696a..d2de9218b3bae 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js +++ b/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js @@ -137,6 +137,7 @@ export const IndexPattern = ({ })} > Date: Thu, 11 Mar 2021 18:17:11 +0300 Subject: [PATCH 07/17] Fix problem with time_range_mode --- .../public/application/components/vis_editor.js | 8 ++++++++ .../server/usage_collector/get_usage_collector.ts | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js index 11586628ea005..1a46cf17bfb49 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js @@ -16,6 +16,7 @@ import { VisPicker } from './vis_picker'; import { PanelConfig } from './panel_config'; import { fetchFields } from '../lib/fetch_fields'; import { extractIndexPatterns } from '../../../common/extract_index_patterns'; +import { TIME_RANGE_DATA_MODES, TIME_RANGE_MODE_KEY } from '../../../common/timerange_data_modes'; import { getSavedObjectsClient, getUISettings, getDataStart, getCoreStart } from '../../services'; import { CoreStartContextProvider } from '../contexts/query_input_bar_context'; @@ -186,6 +187,13 @@ export class VisEditor extends Component { this.setState({ model: { + // we should set default value for 'time_range_mode' in model so that when user save visualization + // we set right mode in savedObject + // ternary operator needed because old visualization have 'time_range_mode' as undefined for 'last_value' + // but for creating new visaulization we should use 'entire_timerange' as default. + [TIME_RANGE_MODE_KEY]: this.props.vis.title + ? TIME_RANGE_DATA_MODES.LAST_VALUE + : TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, ...this.props.visParams, /** @legacy * please use IndexPatterns service instead diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts index 98ddd7f58ea12..57d79fe252b01 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts @@ -56,7 +56,8 @@ export const getStats = async ( function telemetryUseLastValueMode(visState: VisState) { if ( visState.type === 'metrics' && - visState.params.time_range_mode === TIME_RANGE_DATA_MODES.LAST_VALUE + (!visState.params.time_range_mode || + visState.params.time_range_mode === TIME_RANGE_DATA_MODES.LAST_VALUE) ) { timeseriesUsage.timeseries_use_last_value_mode_total++; } From aa1fd20e4eb98547405351564a7d74312800cb06 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 12 Mar 2021 16:23:03 +0300 Subject: [PATCH 08/17] Fix tests --- .../components/panel_config/gauge.js | 12 +- .../components/panel_config/metric.js | 6 +- .../components/panel_config/top_n.js | 12 +- .../get_usage_collector.test.ts | 106 ++++++++++++++---- .../usage_collector/get_usage_collector.ts | 5 - test/functional/apps/visualize/_tsvb_chart.ts | 10 +- .../page_objects/visual_builder_page.ts | 5 + 7 files changed, 120 insertions(+), 36 deletions(-) diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.js b/src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.js index ef986a2f2111b..31ebb794bc308 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.js +++ b/src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.js @@ -321,13 +321,21 @@ class GaugePanelConfigUi extends Component { return ( <> - this.switchTab('data')}> + this.switchTab('data')} + data-test-subj="gaugeEditorDataBtn" + > - this.switchTab('options')}> + this.switchTab('options')} + data-test-subj="gaugeEditorPanelOptionsBtn" + > - this.switchTab('data')}> + this.switchTab('data')} + data-test-subj="metricEditorDataBtn" + > - this.switchTab('data')}> + this.switchTab('data')} + data-test-subj="topNEditorDataBtn" + > - this.switchTab('options')}> + this.switchTab('options')} + data-test-subj="topNEditorPanelOptionsBtn" + > { +const getMockCollectorFetchContext = (hits?: unknown[], savedObjectsByValue: unknown[] = []) => { const fetchParamsMock = createCollectorFetchContextMock(); fetchParamsMock.esClient.search = jest.fn().mockResolvedValue({ body: { hits: { hits } } }); - fetchParamsMock.soClient.find = jest.fn().mockResolvedValue({ saved_objects: [] }); + fetchParamsMock.soClient.find = jest.fn().mockResolvedValue({ + saved_objects: savedObjectsByValue, + }); return fetchParamsMock; }; @@ -55,18 +105,7 @@ describe('Timeseries visualization usage collector', () => { const mockIndex = 'mock_index'; test('Returns undefined when no results found (undefined)', async () => { - const mockCollectorFetchContext = getMockCollectorFetchContext([]); - const result = await getStats( - mockCollectorFetchContext.esClient, - mockCollectorFetchContext.soClient, - mockIndex - ); - - expect(result).toBeUndefined(); - }); - - test('Returns undefined when no results found (0 results)', async () => { - const mockCollectorFetchContext = getMockCollectorFetchContext([]); + const mockCollectorFetchContext = getMockCollectorFetchContext([], []); const result = await getStats( mockCollectorFetchContext.esClient, mockCollectorFetchContext.soClient, @@ -77,15 +116,31 @@ describe('Timeseries visualization usage collector', () => { }); test('Returns undefined when no timeseries saved objects found', async () => { - const mockCollectorFetchContext = getMockCollectorFetchContext([ - { - _id: 'visualization:myvis-123', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "area"}' }, + const mockCollectorFetchContext = getMockCollectorFetchContext( + [ + { + _id: 'visualization:myvis-123', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "area"}' }, + }, }, - }, - ]); + ], + [ + { + attributes: { + panelsJSON: JSON.stringify({ + type: 'visualization', + embeddableConfig: { + savedVis: { + type: 'area', + }, + }, + }), + }, + }, + ] + ); const result = await getStats( mockCollectorFetchContext.esClient, mockCollectorFetchContext.soClient, @@ -96,7 +151,10 @@ describe('Timeseries visualization usage collector', () => { }); test('Summarizes visualizations response data', async () => { - const mockCollectorFetchContext = getMockCollectorFetchContext(mockedSavedObjects); + const mockCollectorFetchContext = getMockCollectorFetchContext( + mockedSavedObjects, + mockedSavedObjectsByValue + ); const result = await getStats( mockCollectorFetchContext.esClient, mockCollectorFetchContext.soClient, @@ -104,7 +162,7 @@ describe('Timeseries visualization usage collector', () => { ); expect(result).toMatchObject({ - timeseries_use_last_value_mode_total: 1, + timeseries_use_last_value_mode_total: 3, }); }); }); diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts index 57d79fe252b01..2aa521a7c17bc 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts @@ -47,11 +47,6 @@ export const getStats = async ( }; const { body: esResponse } = await esClient.search(searchParams); - const size = esResponse?.hits?.hits?.length ?? 0; - - if (!size) { - return; - } function telemetryUseLastValueMode(visState: VisState) { if ( diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index ba500904d75c7..fe8493156d402 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -25,7 +25,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'common', ]); - describe('visual builder', function describeIndexTests() { + describe('tsvb super visual builder', function describeIndexTests() { this.tags('includeFirefox'); beforeEach(async () => { await security.testUser.setRoles([ @@ -43,6 +43,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visualBuilder.resetPage(); await PageObjects.visualBuilder.clickMetric(); await PageObjects.visualBuilder.checkMetricTabIsPresent(); + await PageObjects.visualBuilder.clickPanelOptions('metric'); + await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value'); + await PageObjects.visualBuilder.clickDataTab('metric'); }); it('should not have inspector enabled', async () => { @@ -86,7 +89,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const labelString = await PageObjects.visualBuilder.getGaugeLabel(); expect(labelString).to.be('Count'); const gaugeCount = await PageObjects.visualBuilder.getGaugeCount(); - expect(gaugeCount).to.be('156'); + expect(gaugeCount).to.be('13,830'); }); }); @@ -95,6 +98,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visualBuilder.resetPage(); await PageObjects.visualBuilder.clickTopN(); await PageObjects.visualBuilder.checkTopNTabIsPresent(); + await PageObjects.visualBuilder.clickPanelOptions('topN'); + await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value'); + await PageObjects.visualBuilder.clickDataTab('topN'); }); it('should verify topN label and count display', async () => { diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 46281cc69c3bd..0dc58bd1046fe 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -431,6 +431,11 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro await PageObjects.header.waitUntilLoadingHasFinished(); } + public async clickDataTab(tabName: string) { + await testSubjects.click(`${tabName}EditorDataBtn`); + await PageObjects.header.waitUntilLoadingHasFinished(); + } + public async setIndexPatternValue(value: string) { const el = await testSubjects.find('metricsIndexPatternInput'); await el.clearValue(); From 7ba6e7618a599b91f80a9448d38ecf5082889646 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 12 Mar 2021 18:04:15 +0300 Subject: [PATCH 09/17] Fix tests --- test/functional/apps/visualize/_tsvb_chart.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index fe8493156d402..a95138ebe24ca 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -25,7 +25,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'common', ]); - describe('tsvb super visual builder', function describeIndexTests() { + describe('visual builder', function describeIndexTests() { this.tags('includeFirefox'); beforeEach(async () => { await security.testUser.setRoles([ @@ -119,6 +119,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visualBuilder.resetPage(); await PageObjects.visualBuilder.clickMetric(); await PageObjects.visualBuilder.checkMetricTabIsPresent(); + await PageObjects.visualBuilder.clickPanelOptions('metric'); + await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value'); + await PageObjects.visualBuilder.clickDataTab('metric'); }); after(async () => { await security.testUser.restoreDefaults(); From e1d2428c0f45a37ea06eae6a64836f2f16a83d13 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 12 Mar 2021 20:38:48 +0300 Subject: [PATCH 10/17] Fix tests for markdown and table --- .../application/components/panel_config/table.js | 12 ++++++++++-- test/functional/apps/visualize/_tsvb_markdown.ts | 3 +++ test/functional/apps/visualize/_tsvb_table.ts | 3 +++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/table.js b/src/plugins/vis_type_timeseries/public/application/components/panel_config/table.js index 7a8b919c25db8..58739ad98fdcd 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config/table.js +++ b/src/plugins/vis_type_timeseries/public/application/components/panel_config/table.js @@ -265,13 +265,21 @@ export class TablePanelConfig extends Component { return ( <> - this.switchTab('data')}> + this.switchTab('data')} + > - this.switchTab('options')}> + this.switchTab('options')} + > { diff --git a/test/functional/apps/visualize/_tsvb_table.ts b/test/functional/apps/visualize/_tsvb_table.ts index dfa232b6e527d..36c0e26430ff5 100644 --- a/test/functional/apps/visualize/_tsvb_table.ts +++ b/test/functional/apps/visualize/_tsvb_table.ts @@ -24,6 +24,9 @@ export default function ({ getPageObjects }: FtrProviderContext) { await visualBuilder.clickTable(); await visualBuilder.checkTableTabIsPresent(); + await visualBuilder.clickPanelOptions('table'); + await visualBuilder.setMetricsDataTimerangeMode('Last value'); + await visualBuilder.clickDataTab('table'); await visualBuilder.selectGroupByField('machine.os.raw'); await visualBuilder.setColumnLabelValue('OS'); await visChart.waitForVisualizationRenderingStabilized(); From 7b09ec5e5690a010068b17fee9f822604c92e712 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 17 Mar 2021 16:44:09 +0300 Subject: [PATCH 11/17] exclude TSVB which have type as timeseries --- .../server/usage_collector/get_usage_collector.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts index 2aa521a7c17bc..f6667a4f6bb98 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts @@ -51,6 +51,7 @@ export const getStats = async ( function telemetryUseLastValueMode(visState: VisState) { if ( visState.type === 'metrics' && + visState.params.type !== 'timeseries' && (!visState.params.time_range_mode || visState.params.time_range_mode === TIME_RANGE_DATA_MODES.LAST_VALUE) ) { From 708fe8bf8f5cf16ece3b9e83ad5b1467900d7233 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 18 Mar 2021 16:05:35 +0300 Subject: [PATCH 12/17] Add description for field in schema in telemetry --- .../server/usage_collector/register_timeseries_collector.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts index aea1b07abf1c5..5edeb6654020e 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts @@ -19,7 +19,10 @@ export function registerTimeseriesUsageCollector( type: 'vis_type_timeseries', isReady: () => true, schema: { - timeseries_use_last_value_mode_total: { type: 'long' }, + timeseries_use_last_value_mode_total: { + type: 'long', + _meta: { description: 'Number of TSVB visualizations using "last value" as a time range' }, + }, }, fetch: async ({ esClient, soClient }) => { const { index } = (await config.pipe(first()).toPromise()).kibana; From 744d61613b51aad3eb38a44955594bea04f6d9dc Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 18 Mar 2021 16:33:38 +0300 Subject: [PATCH 13/17] Fix telemetry schema --- src/plugins/telemetry/schema/oss_plugins.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index a5f39a5b9ef8c..8e0b789948723 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -9330,7 +9330,10 @@ "vis_type_timeseries": { "properties": { "timeseries_use_last_value_mode_total": { - "type": "long" + "type": "long", + "_meta": { + "description": "Number of TSVB visualizations using \"last value\" as a time range" + } } } }, From 2726d4e39200821ad0771ff35803050511c8143b Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 25 Mar 2021 17:47:18 +0300 Subject: [PATCH 14/17] Fix some remarks --- .../public/application/components/vis_editor.tsx | 7 ++++--- test/functional/apps/visualize/_tsvb_chart.ts | 6 ++++++ test/functional/page_objects/visual_builder_page.ts | 5 +++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx index bef8e890ddbe0..915ba7a21a67e 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx @@ -70,9 +70,10 @@ export class VisEditor extends Component { + await PageObjects.visualBuilder.clickPanelOptions('gauge'); + await PageObjects.visualBuilder.checkSelectedDataTimerangeMode('Entire time range'); + await PageObjects.visualBuilder.clickDataTab('gauge'); + }); + it('should verify gauge label and count display', async () => { await PageObjects.visChart.waitForVisualizationRenderingStabilized(); const labelString = await PageObjects.visualBuilder.getGaugeLabel(); diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 0bb38a6eeb289..e83fcb2768dc7 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -624,6 +624,11 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro const dataTimeRangeMode = await testSubjects.find('dataTimeRangeMode'); return await comboBox.setElement(dataTimeRangeMode, value); } + + public async checkSelectedDataTimerangeMode(value: string) { + const dataTimeRangeMode = await testSubjects.find('dataTimeRangeMode'); + return await comboBox.isOptionSelected(dataTimeRangeMode, value); + } } return new VisualBuilderPage(); From b62b757b540f4e1fefd75deddd01f4b966d77976 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 25 Mar 2021 18:25:33 +0300 Subject: [PATCH 15/17] Added check for hits --- .../usage_collector/get_usage_collector.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts index f6667a4f6bb98..d125b746a632c 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts @@ -59,16 +59,18 @@ export const getStats = async ( } } - for (const hit of esResponse.hits.hits) { - const visualization = hit._source?.visualization; - let visState: VisState = {}; - try { - visState = JSON.parse(visualization?.visState ?? '{}'); - } catch (e) { - // invalid visState - } + if (esResponse?.hits?.hits?.length) { + for (const hit of esResponse.hits.hits) { + const visualization = hit._source?.visualization; + let visState: VisState = {}; + try { + visState = JSON.parse(visualization?.visState ?? '{}'); + } catch (e) { + // invalid visState + } - telemetryUseLastValueMode(visState); + telemetryUseLastValueMode(visState); + } } const byValueVisualizations = await findByValueEmbeddables(soClient, 'visualization'); From 26b750b793c2b6f2411ec8a808c074a8f436a1a0 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 26 Mar 2021 16:46:52 +0300 Subject: [PATCH 16/17] fix CI --- .../server/usage_collector/get_usage_collector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts index d125b746a632c..0bd518beb47dc 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts @@ -61,7 +61,7 @@ export const getStats = async ( if (esResponse?.hits?.hits?.length) { for (const hit of esResponse.hits.hits) { - const visualization = hit._source?.visualization; + const { visualization } = hit._source; let visState: VisState = {}; try { visState = JSON.parse(visualization?.visState ?? '{}'); From a39e0ac901327345a5d967478ad02867e02c397b Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 29 Mar 2021 12:21:16 +0300 Subject: [PATCH 17/17] fix CI --- .../usage_collector/get_usage_collector.ts | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts index 0bd518beb47dc..c1a8715f72227 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts @@ -6,14 +6,11 @@ * Side Public License, v 1. */ -import { SearchResponse } from 'elasticsearch'; import { ElasticsearchClient } from 'src/core/server'; import { SavedObjectsClientContract, ISavedObjectsRepository } from 'kibana/server'; import { TIME_RANGE_DATA_MODES } from '../../common/timerange_data_modes'; import { findByValueEmbeddables } from '../../../dashboard/server'; -type ESResponse = SearchResponse<{ visualization: { visState: string }; updated_at: string }>; - export interface TimeseriesUsage { timeseries_use_last_value_mode_total: number; } @@ -46,7 +43,10 @@ export const getStats = async ( }, }; - const { body: esResponse } = await esClient.search(searchParams); + const { body: esResponse } = await esClient.search<{ + visualization: { visState: string }; + updated_at: string; + }>(searchParams); function telemetryUseLastValueMode(visState: VisState) { if ( @@ -61,15 +61,18 @@ export const getStats = async ( if (esResponse?.hits?.hits?.length) { for (const hit of esResponse.hits.hits) { - const { visualization } = hit._source; - let visState: VisState = {}; - try { - visState = JSON.parse(visualization?.visState ?? '{}'); - } catch (e) { - // invalid visState - } + if (hit._source && 'visualization' in hit._source) { + const { visualization } = hit._source!; - telemetryUseLastValueMode(visState); + let visState: VisState = {}; + try { + visState = JSON.parse(visualization?.visState ?? '{}'); + } catch (e) { + // invalid visState + } + + telemetryUseLastValueMode(visState); + } } }