diff --git a/src/plugins/data/common/index_patterns/lib/get_title.test.ts b/src/plugins/data/common/index_patterns/lib/get_title.test.ts new file mode 100644 index 00000000000..e826bdb5a5e --- /dev/null +++ b/src/plugins/data/common/index_patterns/lib/get_title.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObjectsClientContract } from '../../../../../core/public'; +import { getTitle } from './get_title'; + +describe('test getTitle', () => { + let client: SavedObjectsClientContract; + + it('with dataSourceId match', async () => { + const dataSourceIdToTitle = new Map(); + dataSourceIdToTitle.set('dataSourceId', 'dataSourceTitle'); + client = { + get: jest.fn().mockResolvedValue({ + attributes: { title: 'indexTitle' }, + references: [{ type: 'data-source', id: 'dataSourceId' }], + }), + } as any; + const title = await getTitle(client, 'indexPatternId', dataSourceIdToTitle); + expect(title).toEqual('dataSourceTitle::indexTitle'); + }); + + it('with no dataSourceId match and error to get data source', async () => { + const dataSourceIdToTitle = new Map(); + client = { + get: jest + .fn() + .mockResolvedValueOnce({ + attributes: { title: 'indexTitle' }, + references: [{ type: 'data-source', id: 'dataSourceId' }], + }) + .mockRejectedValue(new Error('error')), + } as any; + const title = await getTitle(client, 'indexPatternId', dataSourceIdToTitle); + expect(title).toEqual('dataSourceId::indexTitle'); + }); + + it('with no dataSourceId match and success to get data source', async () => { + const dataSourceIdToTitle = new Map(); + client = { + get: jest + .fn() + .mockResolvedValueOnce({ + attributes: { title: 'indexTitle' }, + references: [{ type: 'data-source', id: 'dataSourceId' }], + }) + .mockResolvedValue({ attributes: { title: 'acquiredDataSourceTitle' } }), + } as any; + const title = await getTitle(client, 'indexPatternId', dataSourceIdToTitle); + expect(title).toEqual('acquiredDataSourceTitle::indexTitle'); + }); +}); diff --git a/src/plugins/data/common/index_patterns/lib/get_title.ts b/src/plugins/data/common/index_patterns/lib/get_title.ts index c8677a9a798..15e84eee582 100644 --- a/src/plugins/data/common/index_patterns/lib/get_title.ts +++ b/src/plugins/data/common/index_patterns/lib/get_title.ts @@ -28,17 +28,39 @@ * under the License. */ +import { DataSourceAttributes } from 'src/plugins/data_source/common/data_sources'; import { SavedObjectsClientContract, SimpleSavedObject } from '../../../../../core/public'; +import { + concatDataSourceWithIndexPattern, + getIndexPatternTitle, + getDataSourceReference, +} from '../utils'; export async function getTitle( client: SavedObjectsClientContract, - indexPatternId: string -): Promise> { + indexPatternId: string, + dataSourceIdToTitle: Map +): Promise { const savedObject = (await client.get('index-pattern', indexPatternId)) as SimpleSavedObject; if (savedObject.error) { throw new Error(`Unable to get index-pattern title: ${savedObject.error.message}`); } - return savedObject.attributes.title; + const dataSourceReference = getDataSourceReference(savedObject.references); + + if (dataSourceReference) { + const dataSourceId = dataSourceReference.id; + if (dataSourceIdToTitle.has(dataSourceId)) { + return concatDataSourceWithIndexPattern( + dataSourceIdToTitle.get(dataSourceId)!, + savedObject.attributes.title + ); + } + } + + const getDataSource = async (id: string) => + await client.get('data-source', id); + + return getIndexPatternTitle(savedObject.attributes.title, savedObject.references, getDataSource); } diff --git a/src/plugins/data/common/index_patterns/utils.test.ts b/src/plugins/data/common/index_patterns/utils.test.ts index 1c1b56df5ba..1157bfb1ac5 100644 --- a/src/plugins/data/common/index_patterns/utils.test.ts +++ b/src/plugins/data/common/index_patterns/utils.test.ts @@ -72,7 +72,7 @@ describe('test getIndexPatternTitle', () => { referencesMock, getDataSourceMock ); - expect(res).toEqual('dataSourceMockTitle.indexPatternMockTitle'); + expect(res).toEqual('dataSourceMockTitle::indexPatternMockTitle'); }); test('getIndexPatternTitle should return index pattern title, when index-pattern is not referenced to any datasource', async () => { @@ -87,6 +87,6 @@ describe('test getIndexPatternTitle', () => { referencesMock, getDataSourceMock ); - expect(res).toEqual('dataSourceId.indexPatternMockTitle'); + expect(res).toEqual('dataSourceId::indexPatternMockTitle'); }); }); diff --git a/src/plugins/data/common/index_patterns/utils.ts b/src/plugins/data/common/index_patterns/utils.ts index bba25bfd6df..0da34ebb7fa 100644 --- a/src/plugins/data/common/index_patterns/utils.ts +++ b/src/plugins/data/common/index_patterns/utils.ts @@ -82,9 +82,8 @@ export const getIndexPatternTitle = async ( references: SavedObjectReference[], getDataSource: (id: string) => Promise> ): Promise => { - const DATA_SOURCE_INDEX_PATTERN_DELIMITER = '.'; let dataSourceTitle; - const dataSourceReference = references.find((ref) => ref.type === 'data-source'); + const dataSourceReference = getDataSourceReference(references); // If an index-pattern references datasource, prepend data source name with index pattern name for display purpose if (dataSourceReference) { @@ -99,10 +98,22 @@ export const getIndexPatternTitle = async ( // use datasource id as title when failing to fetch datasource dataSourceTitle = dataSourceId; } - - return dataSourceTitle.concat(DATA_SOURCE_INDEX_PATTERN_DELIMITER).concat(indexPatternTitle); + return concatDataSourceWithIndexPattern(dataSourceTitle, indexPatternTitle); } else { // if index pattern doesn't reference datasource, return as it is. return indexPatternTitle; } }; + +export const concatDataSourceWithIndexPattern = ( + dataSourceTitle: string, + indexPatternTitle: string +) => { + const DATA_SOURCE_INDEX_PATTERN_DELIMITER = '::'; + + return dataSourceTitle.concat(DATA_SOURCE_INDEX_PATTERN_DELIMITER).concat(indexPatternTitle); +}; + +export const getDataSourceReference = (references: SavedObjectReference[]) => { + return references.find((ref) => ref.type === 'data-source'); +}; diff --git a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx index bcc56713f0d..228c803b0f9 100644 --- a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx +++ b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx @@ -36,6 +36,10 @@ import { EuiComboBox, EuiComboBoxProps } from '@elastic/eui'; import { SavedObjectsClientContract, SimpleSavedObject } from 'src/core/public'; import { getTitle } from '../../../common/index_patterns/lib'; +import { + getDataSourceReference, + concatDataSourceWithIndexPattern, +} from '../../../common/index_patterns/utils'; export type IndexPatternSelectProps = Required< Omit, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions'>, @@ -52,6 +56,7 @@ interface IndexPatternSelectState { options: []; selectedIndexPattern: { value: string; label: string } | undefined; searchValue: string | undefined; + dataSourceIdToTitle: Map; } const getIndexPatterns = async ( @@ -80,6 +85,7 @@ export default class IndexPatternSelect extends Component = []; + savedObjects.map((indexPatternSavedObject: SimpleSavedObject) => { + const dataSourceReference = getDataSourceReference(indexPatternSavedObject.references); + if (dataSourceReference && !this.state.dataSourceIdToTitle.has(dataSourceReference.id)) { + dataSourcesToFetch.push({ type: 'data-source', id: dataSourceReference.id }); + } + }); + + const dataSourceIdToTitleToUpdate = new Map(); + + if (dataSourcesToFetch.length > 0) { + const resp = await savedObjectsClient.bulkGet(dataSourcesToFetch); + resp.savedObjects.map((dataSourceSavedObject: SimpleSavedObject) => { + dataSourceIdToTitleToUpdate.set( + dataSourceSavedObject.id, + dataSourceSavedObject.attributes.title + ); + }); + } + const options = savedObjects.map((indexPatternSavedObject: SimpleSavedObject) => { + const dataSourceReference = getDataSourceReference(indexPatternSavedObject.references); + if (dataSourceReference) { + const dataSourceTitle = + this.state.dataSourceIdToTitle.get(dataSourceReference.id) || + dataSourceIdToTitleToUpdate.get(dataSourceReference.id) || + dataSourceReference.id; + return { + label: `${concatDataSourceWithIndexPattern( + dataSourceTitle, + indexPatternSavedObject.attributes.title + )}`, + value: indexPatternSavedObject.id, + }; + } return { label: indexPatternSavedObject.attributes.title, value: indexPatternSavedObject.id, }; }); - this.setState({ - isLoading: false, - options, - }); + + if (dataSourceIdToTitleToUpdate.size > 0) { + const mergedDataSourceIdToTitle = new Map(); + this.state.dataSourceIdToTitle.forEach((k, v) => { + mergedDataSourceIdToTitle.set(k, v); + }); + dataSourceIdToTitleToUpdate.forEach((k, v) => { + mergedDataSourceIdToTitle.set(k, v); + }); + this.setState({ + dataSourceIdToTitle: mergedDataSourceIdToTitle, + isLoading: false, + options, + }); + } else { + this.setState({ + isLoading: false, + options, + }); + } if (onNoIndexPatterns && searchValue === '' && options.length === 0) { onNoIndexPatterns();