From 4713fd057025b05bdaee16ee86f8f2837df9638f Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 1 Dec 2020 12:10:38 +0300 Subject: [PATCH] [Vega] Filter bar in Vega is not usable with non default index pattern. (#84090) * [Vega] Filtering is not working Closes: #81738 * fix CI * some work * some work * add tests for extract_index_pattern * simplify extract_index_pattern * fix type error * cleanup * Update vega_base_view.js * Update extract_index_pattern.test.ts * fix PR comments * fix some comments * findByTitle -> getByTitle * remove getByTitle * fix vega_base_view * fix jest * allow to set multiple indexes from top_nav Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../data/common/index_patterns/utils.ts | 34 ++--- src/plugins/data/public/mocks.ts | 1 + .../public/metrics_type.ts | 14 +- .../public/data_model/es_query_parser.ts | 1 + .../vis_type_vega/public/data_model/types.ts | 5 +- .../public/data_model/vega_parser.test.js | 6 +- .../public/lib/extract_index_pattern.test.ts | 125 ++++++++++++++++++ .../public/lib/extract_index_pattern.ts | 47 +++++++ src/plugins/vis_type_vega/public/plugin.ts | 2 - src/plugins/vis_type_vega/public/services.ts | 11 +- src/plugins/vis_type_vega/public/vega_type.ts | 21 ++- .../public/vega_view/vega_base_view.js | 50 ++++++- .../public/vega_visualization.test.js | 3 +- .../public/vega_visualization.ts | 37 +----- .../components/visualize_top_nav.tsx | 35 +++-- .../translations/translations/ja-JP.json | 4 +- .../translations/translations/zh-CN.json | 4 +- 17 files changed, 290 insertions(+), 110 deletions(-) create mode 100644 src/plugins/vis_type_vega/public/lib/extract_index_pattern.test.ts create mode 100644 src/plugins/vis_type_vega/public/lib/extract_index_pattern.ts diff --git a/src/plugins/data/common/index_patterns/utils.ts b/src/plugins/data/common/index_patterns/utils.ts index d9e1cfa0d952af..b7e1f28d5d60f3 100644 --- a/src/plugins/data/common/index_patterns/utils.ts +++ b/src/plugins/data/common/index_patterns/utils.ts @@ -17,8 +17,8 @@ * under the License. */ -import { find } from 'lodash'; -import { SavedObjectsClientCommon, SavedObject } from '..'; +import type { IndexPatternSavedObjectAttrs } from './index_patterns'; +import type { SavedObjectsClientCommon } from '../types'; /** * Returns an object matching a given title @@ -27,24 +27,16 @@ import { SavedObjectsClientCommon, SavedObject } from '..'; * @param title {string} * @returns {Promise} */ -export async function findByTitle( - client: SavedObjectsClientCommon, - title: string -): Promise | void> { - if (!title) { - return Promise.resolve(); - } - - const savedObjects = await client.find({ - type: 'index-pattern', - perPage: 10, - search: `"${title}"`, - searchFields: ['title'], - fields: ['title'], - }); +export async function findByTitle(client: SavedObjectsClientCommon, title: string) { + if (title) { + const savedObjects = await client.find({ + type: 'index-pattern', + perPage: 10, + search: `"${title}"`, + searchFields: ['title'], + fields: ['title'], + }); - return find( - savedObjects, - (obj: SavedObject) => obj.attributes.title.toLowerCase() === title.toLowerCase() - ); + return savedObjects.find((obj) => obj.attributes.title.toLowerCase() === title.toLowerCase()); + } } diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 1b83eb569b1a1c..67c1ff7e09dd74 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -64,6 +64,7 @@ const createStartContract = (): Start => { SearchBar: jest.fn().mockReturnValue(null), }, indexPatterns: ({ + find: jest.fn((search) => [{ id: search, title: search }]), createField: jest.fn(() => {}), createFieldList: jest.fn(() => []), ensureDefaultIndexPattern: jest.fn(), diff --git a/src/plugins/vis_type_timeseries/public/metrics_type.ts b/src/plugins/vis_type_timeseries/public/metrics_type.ts index 2b75f696206293..41dc26c8c130d6 100644 --- a/src/plugins/vis_type_timeseries/public/metrics_type.ts +++ b/src/plugins/vis_type_timeseries/public/metrics_type.ts @@ -24,7 +24,6 @@ import { PANEL_TYPES } from '../common/panel_types'; import { toExpressionAst } from './to_ast'; import { VIS_EVENT_TO_TRIGGER, VisGroups, VisParams } from '../../visualizations/public'; import { getDataStart } from './services'; -import { INDEXES_SEPARATOR } from '../common/constants'; export const metricsVisDefinition = { name: 'metrics', @@ -84,18 +83,7 @@ export const metricsVisDefinition = { inspectorAdapters: {}, getUsedIndexPattern: async (params: VisParams) => { const { indexPatterns } = getDataStart(); - const indexes: string = params.index_pattern; - if (indexes) { - const cachedIndexes = await indexPatterns.getIdsWithTitle(); - const ids = indexes - .split(INDEXES_SEPARATOR) - .map((title) => cachedIndexes.find((i) => i.title === title)?.id) - .filter((id) => id); - - return Promise.all(ids.map((id) => indexPatterns.get(id!))); - } - - return []; + return params.index_pattern ? await indexPatterns.find(params.index_pattern) : []; }, }; diff --git a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts index 1aac8e25d5c738..79eb049fb6dcce 100644 --- a/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/es_query_parser.ts @@ -226,6 +226,7 @@ export class EsQueryParser { const requestObject = requests.find((item) => getRequestName(item, index) === data.name); if (requestObject) { + requestObject.dataObject.url = requestObject.url; requestObject.dataObject.values = data.rawResponse; } }); diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index acd35e17476242..3bfe218099577d 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -82,8 +82,9 @@ interface Projection { name: string; } -interface RequestDataObject { +interface RequestDataObject { name?: string; + url?: TUrlData; values: SearchResponse; } @@ -186,7 +187,7 @@ export interface CacheBounds { max: number; } -interface Requests { +interface Requests> { url: TUrlData; name: string; dataObject: TRequestDataObject; diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js b/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js index 9fb80c6a1b19dc..eb666d65b86706 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js @@ -185,21 +185,21 @@ describe('VegaParser._resolveEsQueries', () => { 'es', check( { data: { name: 'requestId', url: { index: 'a' }, x: 1 } }, - { data: { name: 'requestId', values: [42], x: 1 } } + { data: { name: 'requestId', url: { index: 'a', body: {} }, values: [42], x: 1 } } ) ); test( 'es 2', check( { data: { name: 'requestId', url: { '%type%': 'elasticsearch', index: 'a' } } }, - { data: { name: 'requestId', values: [42] } } + { data: { name: 'requestId', url: { index: 'a', body: {} }, values: [42] } } ) ); test( 'es arr', check( { arr: [{ data: { name: 'requestId', url: { index: 'a' }, x: 1 } }] }, - { arr: [{ data: { name: 'requestId', values: [42], x: 1 } }] } + { arr: [{ data: { name: 'requestId', url: { index: 'a', body: {} }, values: [42], x: 1 } }] } ) ); test( diff --git a/src/plugins/vis_type_vega/public/lib/extract_index_pattern.test.ts b/src/plugins/vis_type_vega/public/lib/extract_index_pattern.test.ts new file mode 100644 index 00000000000000..a13428d539ad98 --- /dev/null +++ b/src/plugins/vis_type_vega/public/lib/extract_index_pattern.test.ts @@ -0,0 +1,125 @@ +/* + * 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 { dataPluginMock } from '../../../data/public/mocks'; +import { extractIndexPatternsFromSpec } from './extract_index_pattern'; +import { setData } from '../services'; + +import type { VegaSpec } from '../data_model/types'; + +const getMockedSpec = (mockedObj: any) => (mockedObj as unknown) as VegaSpec; + +describe('extractIndexPatternsFromSpec', () => { + const dataStart = dataPluginMock.createStartContract(); + + beforeAll(() => { + setData(dataStart); + }); + + test('should not throw errors if no index is specified', async () => { + const spec = getMockedSpec({ + data: {}, + }); + + const indexes = await extractIndexPatternsFromSpec(spec); + + expect(indexes).toMatchInlineSnapshot(`Array []`); + }); + + test('should extract single index pattern', async () => { + const spec = getMockedSpec({ + data: { + url: { + index: 'test', + }, + }, + }); + + const indexes = await extractIndexPatternsFromSpec(spec); + + expect(indexes).toMatchInlineSnapshot(` + Array [ + Object { + "id": "test", + "title": "test", + }, + ] + `); + }); + + test('should extract multiple index patterns', async () => { + const spec = getMockedSpec({ + data: [ + { + url: { + index: 'test1', + }, + }, + { + url: { + index: 'test2', + }, + }, + ], + }); + + const indexes = await extractIndexPatternsFromSpec(spec); + + expect(indexes).toMatchInlineSnapshot(` + Array [ + Object { + "id": "test1", + "title": "test1", + }, + Object { + "id": "test2", + "title": "test2", + }, + ] + `); + }); + + test('should filter empty values', async () => { + const spec = getMockedSpec({ + data: [ + { + url: { + wrong: 'wrong', + }, + }, + { + url: { + index: 'ok', + }, + }, + ], + }); + + const indexes = await extractIndexPatternsFromSpec(spec); + + expect(indexes).toMatchInlineSnapshot(` + Array [ + Object { + "id": "ok", + "title": "ok", + }, + ] + `); + }); +}); diff --git a/src/plugins/vis_type_vega/public/lib/extract_index_pattern.ts b/src/plugins/vis_type_vega/public/lib/extract_index_pattern.ts new file mode 100644 index 00000000000000..12cbd6f7ebbfaa --- /dev/null +++ b/src/plugins/vis_type_vega/public/lib/extract_index_pattern.ts @@ -0,0 +1,47 @@ +/* + * 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 { flatten } from 'lodash'; +import { getData } from '../services'; + +import type { Data, VegaSpec } from '../data_model/types'; +import type { IndexPattern } from '../../../data/public'; + +export const extractIndexPatternsFromSpec = async (spec: VegaSpec) => { + const { indexPatterns } = getData(); + let data: Data[] = []; + + if (Array.isArray(spec.data)) { + data = spec.data; + } else if (spec.data) { + data = [spec.data]; + } + + return flatten( + await Promise.all( + data.reduce>>((accumulator, currentValue) => { + if (currentValue.url?.index) { + accumulator.push(indexPatterns.find(currentValue.url.index)); + } + + return accumulator; + }, []) + ) + ); +}; diff --git a/src/plugins/vis_type_vega/public/plugin.ts b/src/plugins/vis_type_vega/public/plugin.ts index 04481685c841ba..55a69ab11966c6 100644 --- a/src/plugins/vis_type_vega/public/plugin.ts +++ b/src/plugins/vis_type_vega/public/plugin.ts @@ -25,7 +25,6 @@ import { Setup as InspectorSetup } from '../../inspector/public'; import { setNotifications, setData, - setSavedObjects, setInjectedVars, setUISettings, setMapsLegacyConfig, @@ -100,7 +99,6 @@ export class VegaPlugin implements Plugin, void> { public start(core: CoreStart, { data }: VegaPluginStartDependencies) { setNotifications(core.notifications); - setSavedObjects(core.savedObjects); setData(data); setInjectedMetadata(core.injectedMetadata); } diff --git a/src/plugins/vis_type_vega/public/services.ts b/src/plugins/vis_type_vega/public/services.ts index 455fe67dbc4233..43856c83248477 100644 --- a/src/plugins/vis_type_vega/public/services.ts +++ b/src/plugins/vis_type_vega/public/services.ts @@ -17,12 +17,7 @@ * under the License. */ -import { - CoreStart, - SavedObjectsStart, - NotificationsStart, - IUiSettingsClient, -} from 'src/core/public'; +import { CoreStart, NotificationsStart, IUiSettingsClient } from 'src/core/public'; import { DataPublicPluginStart } from '../../data/public'; import { createGetterSetter } from '../../kibana_utils/public'; @@ -40,10 +35,6 @@ export const [getInjectedMetadata, setInjectedMetadata] = createGetterSetter< CoreStart['injectedMetadata'] >('InjectedMetadata'); -export const [getSavedObjects, setSavedObjects] = createGetterSetter( - 'SavedObjects' -); - export const [getInjectedVars, setInjectedVars] = createGetterSetter<{ enableExternalUrls: boolean; emsTileLayerId: unknown; diff --git a/src/plugins/vis_type_vega/public/vega_type.ts b/src/plugins/vis_type_vega/public/vega_type.ts index 2211abb54aa93f..d81bfe02389e2f 100644 --- a/src/plugins/vis_type_vega/public/vega_type.ts +++ b/src/plugins/vis_type_vega/public/vega_type.ts @@ -18,19 +18,24 @@ */ import { i18n } from '@kbn/i18n'; -import { BaseVisTypeOptions } from 'src/plugins/visualizations/public'; +import { parse } from 'hjson'; +import type { BaseVisTypeOptions } from 'src/plugins/visualizations/public'; + import { DefaultEditorSize } from '../../vis_default_editor/public'; -import { VegaVisualizationDependencies } from './plugin'; +import type { VegaVisualizationDependencies } from './plugin'; import { createVegaRequestHandler } from './vega_request_handler'; import { getDefaultSpec } from './default_spec'; +import { extractIndexPatternsFromSpec } from './lib/extract_index_pattern'; import { createInspectorAdapters } from './vega_inspector'; import { VIS_EVENT_TO_TRIGGER, VisGroups } from '../../visualizations/public'; import { toExpressionAst } from './to_ast'; -import { VisParams } from './vega_fn'; import { getInfoMessage } from './components/experimental_map_vis_info'; import { VegaVisEditorComponent } from './components/vega_vis_editor_lazy'; +import type { VegaSpec } from './data_model/types'; +import type { VisParams } from './vega_fn'; + export const createVegaTypeDefinition = ( dependencies: VegaVisualizationDependencies ): BaseVisTypeOptions => { @@ -68,6 +73,16 @@ export const createVegaTypeDefinition = ( getSupportedTriggers: () => { return [VIS_EVENT_TO_TRIGGER.applyFilter]; }, + getUsedIndexPattern: async (visParams) => { + try { + const spec = parse(visParams.spec, { legacyRoot: false, keepWsc: true }); + + return extractIndexPatternsFromSpec(spec as VegaSpec); + } catch (e) { + // spec is invalid + } + return []; + }, inspectorAdapters: createInspectorAdapters, }; }; diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js index 25ea77ddbccb42..10f08edef1aa61 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -27,7 +27,8 @@ import { i18n } from '@kbn/i18n'; import { TooltipHandler } from './vega_tooltip'; import { esFilters } from '../../../data/public'; -import { getEnableExternalUrls } from '../services'; +import { getEnableExternalUrls, getData } from '../services'; +import { extractIndexPatternsFromSpec } from '../lib/extract_index_pattern'; vega.scheme('elastic', euiPaletteColorBlind()); @@ -65,7 +66,6 @@ export class VegaBaseView { this._filterManager = opts.filterManager; this._fireEvent = opts.fireEvent; this._timefilter = opts.timefilter; - this._findIndex = opts.findIndex; this._view = null; this._vegaViewConfig = null; this._$messages = null; @@ -127,6 +127,48 @@ export class VegaBaseView { } } + /** + * Find index pattern by its title, if not given, gets it from spec or a defaults one + * @param {string} [index] + * @returns {Promise} index id + */ + async findIndex(index) { + const { indexPatterns } = getData(); + let idxObj; + + if (index) { + [idxObj] = await indexPatterns.find(index); + if (!idxObj) { + throw new Error( + i18n.translate('visTypeVega.vegaParser.baseView.indexNotFoundErrorMessage', { + defaultMessage: 'Index {index} not found', + values: { index: `"${index}"` }, + }) + ); + } + } else { + [idxObj] = await extractIndexPatternsFromSpec( + this._parser.isVegaLite ? this._parser.vlspec : this._parser.spec + ); + + if (!idxObj) { + const defaultIdx = await indexPatterns.getDefault(); + + if (defaultIdx) { + idxObj = defaultIdx; + } else { + throw new Error( + i18n.translate('visTypeVega.vegaParser.baseView.unableToFindDefaultIndexErrorMessage', { + defaultMessage: 'Unable to find default index', + }) + ); + } + } + } + + return idxObj.id; + } + createViewConfig() { const config = { // eslint-disable-next-line import/namespace @@ -261,7 +303,7 @@ export class VegaBaseView { * @param {string} [index] as defined in Kibana, or default if missing */ async addFilterHandler(query, index) { - const indexId = await this._findIndex(index); + const indexId = await this.findIndex(index); const filter = esFilters.buildQueryFilter(query, indexId); this._fireEvent({ name: 'applyFilter', data: { filters: [filter] } }); @@ -272,7 +314,7 @@ export class VegaBaseView { * @param {string} [index] as defined in Kibana, or default if missing */ async removeFilterHandler(query, index) { - const indexId = await this._findIndex(index); + const indexId = await this.findIndex(index); const filterToRemove = esFilters.buildQueryFilter(query, indexId); const currentFilters = this._filterManager.getFilters(); diff --git a/src/plugins/vis_type_vega/public/vega_visualization.test.js b/src/plugins/vis_type_vega/public/vega_visualization.test.js index a2214e139a296a..8a073ca32b94a2 100644 --- a/src/plugins/vis_type_vega/public/vega_visualization.test.js +++ b/src/plugins/vis_type_vega/public/vega_visualization.test.js @@ -30,7 +30,7 @@ import vegaMapGraph from './test_utils/vega_map_test.json'; import { VegaParser } from './data_model/vega_parser'; import { SearchAPI } from './data_model/search_api'; -import { setInjectedVars, setData, setSavedObjects, setNotifications } from './services'; +import { setInjectedVars, setData, setNotifications } from './services'; import { coreMock } from '../../../core/public/mocks'; import { dataPluginMock } from '../../data/public/mocks'; @@ -76,7 +76,6 @@ describe('VegaVisualizations', () => { enableExternalUrls: true, }); setData(dataPluginStart); - setSavedObjects(coreStart.savedObjects); setNotifications(coreStart.notifications); vegaVisualizationDependencies = { diff --git a/src/plugins/vis_type_vega/public/vega_visualization.ts b/src/plugins/vis_type_vega/public/vega_visualization.ts index 58c436bcd4be4c..554ac8962df463 100644 --- a/src/plugins/vis_type_vega/public/vega_visualization.ts +++ b/src/plugins/vis_type_vega/public/vega_visualization.ts @@ -20,13 +20,12 @@ import { i18n } from '@kbn/i18n'; import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; import { VegaParser } from './data_model/vega_parser'; import { VegaVisualizationDependencies } from './plugin'; -import { getNotifications, getData, getSavedObjects } from './services'; +import { getNotifications, getData } from './services'; import type { VegaView } from './vega_view/vega_view'; export const createVegaVisualization = ({ getServiceSettings }: VegaVisualizationDependencies) => class VegaVisualization { private readonly dataPlugin = getData(); - private readonly savedObjectsClient = getSavedObjects(); private vegaView: InstanceType | null = null; constructor( @@ -34,39 +33,6 @@ export const createVegaVisualization = ({ getServiceSettings }: VegaVisualizatio private fireEvent: IInterpreterRenderHandlers['event'] ) {} - /** - * Find index pattern by its title, of if not given, gets default - * @param {string} [index] - * @returns {Promise} index id - */ - async findIndex(index: string) { - const { indexPatterns } = this.dataPlugin; - let idxObj; - - if (index) { - // @ts-expect-error - idxObj = indexPatterns.findByTitle(this.savedObjectsClient, index); - if (!idxObj) { - throw new Error( - i18n.translate('visTypeVega.visualization.indexNotFoundErrorMessage', { - defaultMessage: 'Index {index} not found', - values: { index: `"${index}"` }, - }) - ); - } - } else { - idxObj = await indexPatterns.getDefault(); - if (!idxObj) { - throw new Error( - i18n.translate('visTypeVega.visualization.unableToFindDefaultIndexErrorMessage', { - defaultMessage: 'Unable to find default index', - }) - ); - } - } - return idxObj.id; - } - async render(visData: VegaParser) { const { toasts } = getNotifications(); @@ -112,7 +78,6 @@ export const createVegaVisualization = ({ getServiceSettings }: VegaVisualizatio serviceSettings, filterManager, timefilter, - findIndex: this.findIndex.bind(this), }; if (vegaParser.useMap) { diff --git a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx index b207529c456a14..4b328801361465 100644 --- a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx +++ b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx @@ -20,7 +20,6 @@ import React, { memo, useCallback, useMemo, useState, useEffect } from 'react'; import { AppMountParameters, OverlayRef } from 'kibana/public'; -import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../../../../kibana_react/public'; import { @@ -31,6 +30,7 @@ import { } from '../types'; import { APP_NAME } from '../visualize_constants'; import { getTopNavConfig } from '../utils'; +import type { IndexPattern } from '../../../../data/public'; interface VisualizeTopNavProps { currentAppState: VisualizeAppState; @@ -118,7 +118,9 @@ const TopNav = ({ stateTransfer, onAppLeave, ]); - const [indexPattern, setIndexPattern] = useState(vis.data.indexPattern); + const [indexPatterns, setIndexPatterns] = useState( + vis.data.indexPattern ? [vis.data.indexPattern] : [] + ); const showDatePicker = () => { // tsvb loads without an indexPattern initially (TODO investigate). // hide timefilter only if timeFieldName is explicitly undefined. @@ -165,14 +167,27 @@ const TopNav = ({ ]); useEffect(() => { - if (!vis.data.indexPattern) { - services.data.indexPatterns.getDefault().then((index) => { - if (index) { - setIndexPattern(index); + const asyncSetIndexPattern = async () => { + let indexes: IndexPattern[] | undefined; + + if (vis.type.getUsedIndexPattern) { + indexes = await vis.type.getUsedIndexPattern(vis.params); + } + if (!indexes || !indexes.length) { + const defaultIndex = await services.data.indexPatterns.getDefault(); + if (defaultIndex) { + indexes = [defaultIndex]; } - }); + } + if (indexes) { + setIndexPatterns(indexes); + } + }; + + if (!vis.data.indexPattern) { + asyncSetIndexPattern(); } - }, [services.data.indexPatterns, vis.data.indexPattern]); + }, [vis.params, vis.type, services.data.indexPatterns, vis.data.indexPattern]); return isChromeVisible ? ( /** @@ -189,7 +204,7 @@ const TopNav = ({ onQuerySubmit={handleRefresh} savedQueryId={currentAppState.savedQuery} onSavedQueryIdChange={stateContainer.transitions.updateSavedQuery} - indexPatterns={indexPattern ? [indexPattern] : undefined} + indexPatterns={indexPatterns} screenTitle={vis.title} showAutoRefreshOnly={!showDatePicker()} showDatePicker={showDatePicker()} @@ -207,7 +222,7 @@ const TopNav = ({