Skip to content

Commit 4ab0ca8

Browse files
authored
[MD] Concatenate data source name with index pattern name and change delimiter to double colon (#5907)
* concatenate data source name with index pattern name Signed-off-by: Lu Yu <[email protected]> * add changelog Signed-off-by: Lu Yu <[email protected]> * add tests Signed-off-by: Lu Yu <[email protected]> --------- Signed-off-by: Lu Yu <[email protected]>
1 parent 4081154 commit 4ab0ca8

File tree

6 files changed

+162
-14
lines changed

6 files changed

+162
-14
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
4141
- [Multiple Datasource] Add interfaces to register add-on authentication method from plug-in module ([#5851](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5851))
4242
- [Multiple Datasource] Able to Hide "Local Cluster" option from datasource DropDown ([#5827](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5827))
4343
- [Multiple Datasource] Add api registry and allow it to be added into client config in data source plugin ([#5895](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5895))
44+
- [Multiple Datasource] Concatenate data source name with index pattern name and change delimiter to double colon ([#5907](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5907))
4445

4546
### 🐛 Bug Fixes
4647

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { SavedObjectsClientContract } from '../../../../../core/public';
7+
import { getTitle } from './get_title';
8+
9+
describe('test getTitle', () => {
10+
let client: SavedObjectsClientContract;
11+
12+
it('with dataSourceId match', async () => {
13+
const dataSourceIdToTitle = new Map();
14+
dataSourceIdToTitle.set('dataSourceId', 'dataSourceTitle');
15+
client = {
16+
get: jest.fn().mockResolvedValue({
17+
attributes: { title: 'indexTitle' },
18+
references: [{ type: 'data-source', id: 'dataSourceId' }],
19+
}),
20+
} as any;
21+
const title = await getTitle(client, 'indexPatternId', dataSourceIdToTitle);
22+
expect(title).toEqual('dataSourceTitle::indexTitle');
23+
});
24+
25+
it('with no dataSourceId match and error to get data source', async () => {
26+
const dataSourceIdToTitle = new Map();
27+
client = {
28+
get: jest
29+
.fn()
30+
.mockResolvedValueOnce({
31+
attributes: { title: 'indexTitle' },
32+
references: [{ type: 'data-source', id: 'dataSourceId' }],
33+
})
34+
.mockRejectedValue(new Error('error')),
35+
} as any;
36+
const title = await getTitle(client, 'indexPatternId', dataSourceIdToTitle);
37+
expect(title).toEqual('dataSourceId::indexTitle');
38+
});
39+
40+
it('with no dataSourceId match and success to get data source', async () => {
41+
const dataSourceIdToTitle = new Map();
42+
client = {
43+
get: jest
44+
.fn()
45+
.mockResolvedValueOnce({
46+
attributes: { title: 'indexTitle' },
47+
references: [{ type: 'data-source', id: 'dataSourceId' }],
48+
})
49+
.mockResolvedValue({ attributes: { title: 'acquiredDataSourceTitle' } }),
50+
} as any;
51+
const title = await getTitle(client, 'indexPatternId', dataSourceIdToTitle);
52+
expect(title).toEqual('acquiredDataSourceTitle::indexTitle');
53+
});
54+
});

src/plugins/data/common/index_patterns/lib/get_title.ts

+25-3
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,39 @@
2828
* under the License.
2929
*/
3030

31+
import { DataSourceAttributes } from 'src/plugins/data_source/common/data_sources';
3132
import { SavedObjectsClientContract, SimpleSavedObject } from '../../../../../core/public';
33+
import {
34+
concatDataSourceWithIndexPattern,
35+
getIndexPatternTitle,
36+
getDataSourceReference,
37+
} from '../utils';
3238

3339
export async function getTitle(
3440
client: SavedObjectsClientContract,
35-
indexPatternId: string
36-
): Promise<SimpleSavedObject<any>> {
41+
indexPatternId: string,
42+
dataSourceIdToTitle: Map<string, string>
43+
): Promise<string> {
3744
const savedObject = (await client.get('index-pattern', indexPatternId)) as SimpleSavedObject<any>;
3845

3946
if (savedObject.error) {
4047
throw new Error(`Unable to get index-pattern title: ${savedObject.error.message}`);
4148
}
4249

43-
return savedObject.attributes.title;
50+
const dataSourceReference = getDataSourceReference(savedObject.references);
51+
52+
if (dataSourceReference) {
53+
const dataSourceId = dataSourceReference.id;
54+
if (dataSourceIdToTitle.has(dataSourceId)) {
55+
return concatDataSourceWithIndexPattern(
56+
dataSourceIdToTitle.get(dataSourceId)!,
57+
savedObject.attributes.title
58+
);
59+
}
60+
}
61+
62+
const getDataSource = async (id: string) =>
63+
await client.get<DataSourceAttributes>('data-source', id);
64+
65+
return getIndexPatternTitle(savedObject.attributes.title, savedObject.references, getDataSource);
4466
}

src/plugins/data/common/index_patterns/utils.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ describe('test getIndexPatternTitle', () => {
7272
referencesMock,
7373
getDataSourceMock
7474
);
75-
expect(res).toEqual('dataSourceMockTitle.indexPatternMockTitle');
75+
expect(res).toEqual('dataSourceMockTitle::indexPatternMockTitle');
7676
});
7777

7878
test('getIndexPatternTitle should return index pattern title, when index-pattern is not referenced to any datasource', async () => {
@@ -87,6 +87,6 @@ describe('test getIndexPatternTitle', () => {
8787
referencesMock,
8888
getDataSourceMock
8989
);
90-
expect(res).toEqual('dataSourceId.indexPatternMockTitle');
90+
expect(res).toEqual('dataSourceId::indexPatternMockTitle');
9191
});
9292
});

src/plugins/data/common/index_patterns/utils.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,8 @@ export const getIndexPatternTitle = async (
8282
references: SavedObjectReference[],
8383
getDataSource: (id: string) => Promise<SavedObject<DataSourceAttributes>>
8484
): Promise<string> => {
85-
const DATA_SOURCE_INDEX_PATTERN_DELIMITER = '.';
8685
let dataSourceTitle;
87-
const dataSourceReference = references.find((ref) => ref.type === 'data-source');
86+
const dataSourceReference = getDataSourceReference(references);
8887

8988
// If an index-pattern references datasource, prepend data source name with index pattern name for display purpose
9089
if (dataSourceReference) {
@@ -99,10 +98,22 @@ export const getIndexPatternTitle = async (
9998
// use datasource id as title when failing to fetch datasource
10099
dataSourceTitle = dataSourceId;
101100
}
102-
103-
return dataSourceTitle.concat(DATA_SOURCE_INDEX_PATTERN_DELIMITER).concat(indexPatternTitle);
101+
return concatDataSourceWithIndexPattern(dataSourceTitle, indexPatternTitle);
104102
} else {
105103
// if index pattern doesn't reference datasource, return as it is.
106104
return indexPatternTitle;
107105
}
108106
};
107+
108+
export const concatDataSourceWithIndexPattern = (
109+
dataSourceTitle: string,
110+
indexPatternTitle: string
111+
) => {
112+
const DATA_SOURCE_INDEX_PATTERN_DELIMITER = '::';
113+
114+
return dataSourceTitle.concat(DATA_SOURCE_INDEX_PATTERN_DELIMITER).concat(indexPatternTitle);
115+
};
116+
117+
export const getDataSourceReference = (references: SavedObjectReference[]) => {
118+
return references.find((ref) => ref.type === 'data-source');
119+
};

src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx

+65-5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ import { EuiComboBox, EuiComboBoxProps } from '@elastic/eui';
3636

3737
import { SavedObjectsClientContract, SimpleSavedObject } from 'src/core/public';
3838
import { getTitle } from '../../../common/index_patterns/lib';
39+
import {
40+
getDataSourceReference,
41+
concatDataSourceWithIndexPattern,
42+
} from '../../../common/index_patterns/utils';
3943

4044
export type IndexPatternSelectProps = Required<
4145
Omit<EuiComboBoxProps<any>, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions'>,
@@ -52,6 +56,7 @@ interface IndexPatternSelectState {
5256
options: [];
5357
selectedIndexPattern: { value: string; label: string } | undefined;
5458
searchValue: string | undefined;
59+
dataSourceIdToTitle: Map<string, string>;
5560
}
5661

5762
const getIndexPatterns = async (
@@ -80,6 +85,7 @@ export default class IndexPatternSelect extends Component<IndexPatternSelectProp
8085

8186
this.state = {
8287
isLoading: false,
88+
dataSourceIdToTitle: new Map(),
8389
options: [],
8490
selectedIndexPattern: undefined,
8591
searchValue: undefined,
@@ -113,7 +119,11 @@ export default class IndexPatternSelect extends Component<IndexPatternSelectProp
113119

114120
let indexPatternTitle;
115121
try {
116-
indexPatternTitle = await getTitle(this.props.savedObjectsClient, indexPatternId);
122+
indexPatternTitle = await getTitle(
123+
this.props.savedObjectsClient,
124+
indexPatternId,
125+
this.state.dataSourceIdToTitle
126+
);
117127
} catch (err) {
118128
// index pattern no longer exists
119129
return;
@@ -161,16 +171,66 @@ export default class IndexPatternSelect extends Component<IndexPatternSelectProp
161171
// We need this check to handle the case where search results come back in a different
162172
// order than they were sent out. Only load results for the most recent search.
163173
if (searchValue === this.state.searchValue) {
174+
const dataSourcesToFetch: Array<{ type: string; id: string }> = [];
175+
savedObjects.map((indexPatternSavedObject: SimpleSavedObject<any>) => {
176+
const dataSourceReference = getDataSourceReference(indexPatternSavedObject.references);
177+
if (dataSourceReference && !this.state.dataSourceIdToTitle.has(dataSourceReference.id)) {
178+
dataSourcesToFetch.push({ type: 'data-source', id: dataSourceReference.id });
179+
}
180+
});
181+
182+
const dataSourceIdToTitleToUpdate = new Map();
183+
184+
if (dataSourcesToFetch.length > 0) {
185+
const resp = await savedObjectsClient.bulkGet(dataSourcesToFetch);
186+
resp.savedObjects.map((dataSourceSavedObject: SimpleSavedObject<any>) => {
187+
dataSourceIdToTitleToUpdate.set(
188+
dataSourceSavedObject.id,
189+
dataSourceSavedObject.attributes.title
190+
);
191+
});
192+
}
193+
164194
const options = savedObjects.map((indexPatternSavedObject: SimpleSavedObject<any>) => {
195+
const dataSourceReference = getDataSourceReference(indexPatternSavedObject.references);
196+
if (dataSourceReference) {
197+
const dataSourceTitle =
198+
this.state.dataSourceIdToTitle.get(dataSourceReference.id) ||
199+
dataSourceIdToTitleToUpdate.get(dataSourceReference.id) ||
200+
dataSourceReference.id;
201+
return {
202+
label: `${concatDataSourceWithIndexPattern(
203+
dataSourceTitle,
204+
indexPatternSavedObject.attributes.title
205+
)}`,
206+
value: indexPatternSavedObject.id,
207+
};
208+
}
165209
return {
166210
label: indexPatternSavedObject.attributes.title,
167211
value: indexPatternSavedObject.id,
168212
};
169213
});
170-
this.setState({
171-
isLoading: false,
172-
options,
173-
});
214+
215+
if (dataSourceIdToTitleToUpdate.size > 0) {
216+
const mergedDataSourceIdToTitle = new Map();
217+
this.state.dataSourceIdToTitle.forEach((k, v) => {
218+
mergedDataSourceIdToTitle.set(k, v);
219+
});
220+
dataSourceIdToTitleToUpdate.forEach((k, v) => {
221+
mergedDataSourceIdToTitle.set(k, v);
222+
});
223+
this.setState({
224+
dataSourceIdToTitle: mergedDataSourceIdToTitle,
225+
isLoading: false,
226+
options,
227+
});
228+
} else {
229+
this.setState({
230+
isLoading: false,
231+
options,
232+
});
233+
}
174234

175235
if (onNoIndexPatterns && searchValue === '' && options.length === 0) {
176236
onNoIndexPatterns();

0 commit comments

Comments
 (0)