Skip to content

Commit

Permalink
Merge branch 'main' into xinrui_DataSourceIdRequired_WithNoHostConfig
Browse files Browse the repository at this point in the history
  • Loading branch information
xinruiba committed Feb 23, 2024
2 parents bbe549b + 790c076 commit cbf1fec
Show file tree
Hide file tree
Showing 24 changed files with 418 additions and 51 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [Multiple Datasource] Add interfaces to register add-on authentication method from plug-in module ([#5851](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5851))
- [Multiple Datasource] Able to Hide "Local Cluster" option from datasource DropDown ([#5827](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5827))
- [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))
- [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))
- [Multiple Datasource] Refactor client and legacy client to use authentication registry ([#5881](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5881))

### 🐛 Bug Fixes

Expand Down Expand Up @@ -68,6 +70,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [BUG] Remove duplicate sample data as id 90943e30-9a47-11e8-b64d-95841ca0b247 ([5668](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5668))
- [BUG][Multiple Datasource] Fix datasource testing connection unexpectedly passed with wrong endpoint [#5663](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5663)
- [Table Visualization] Fix filter action buttons for split table aggregations ([#5619](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5619))
- [BUG][Discover] Allow saved sort from search embeddable to load in Dashboard ([#5934](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5934))
- [BUG] [Multiple Datasource] datasource id is required if there is no open-search hosts config ([#5882](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5882))

### 🚞 Infrastructure
Expand All @@ -80,6 +83,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [Chore] Add `--security` for `opensearch snapshot` and `opensearch_dashboards` to configure local setup with the security plugin ([#5451](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5451))
- [Tests] Add Github workflow for Test Orchestrator in FT Repo to run cypress tests within Dashboards repo ([#5725](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5725))
- [Chore] Updates default dev environment security credentials ([#5736](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5736))
- [Tests] Baseline screenshots for area and tsvb functional tests ([#5915](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5915))

### 📝 Documentation

Expand All @@ -100,6 +104,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Add @SuZhou-Joe as a maintainer ([#5594](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5594))
- Move @seanneumann to emeritus maintainer ([#5634](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5634))
- Remove `ui-select` dev dependency ([#5660](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5660))
- Bump `chromedriver` dependency to `121.0.1"` ([#5926](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5926))

### 🪛 Refactoring

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@
"chai": "3.5.0",
"chance": "1.0.18",
"cheerio": "1.0.0-rc.1",
"chromedriver": "^119.0.1",
"chromedriver": "^121.0.1",
"classnames": "2.3.1",
"compare-versions": "3.5.1",
"cypress": "9.5.4",
Expand Down
54 changes: 54 additions & 0 deletions src/plugins/data/common/index_patterns/lib/get_title.test.ts
Original file line number Diff line number Diff line change
@@ -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');
});
});
28 changes: 25 additions & 3 deletions src/plugins/data/common/index_patterns/lib/get_title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<SimpleSavedObject<any>> {
indexPatternId: string,
dataSourceIdToTitle: Map<string, string>
): Promise<string> {
const savedObject = (await client.get('index-pattern', indexPatternId)) as SimpleSavedObject<any>;

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<DataSourceAttributes>('data-source', id);

return getIndexPatternTitle(savedObject.attributes.title, savedObject.references, getDataSource);
}
4 changes: 2 additions & 2 deletions src/plugins/data/common/index_patterns/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand All @@ -87,6 +87,6 @@ describe('test getIndexPatternTitle', () => {
referencesMock,
getDataSourceMock
);
expect(res).toEqual('dataSourceId.indexPatternMockTitle');
expect(res).toEqual('dataSourceId::indexPatternMockTitle');
});
});
19 changes: 15 additions & 4 deletions src/plugins/data/common/index_patterns/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,8 @@ export const getIndexPatternTitle = async (
references: SavedObjectReference[],
getDataSource: (id: string) => Promise<SavedObject<DataSourceAttributes>>
): Promise<string> => {
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) {
Expand All @@ -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');
};
Original file line number Diff line number Diff line change
Expand Up @@ -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<EuiComboBoxProps<any>, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions'>,
Expand All @@ -52,6 +56,7 @@ interface IndexPatternSelectState {
options: [];
selectedIndexPattern: { value: string; label: string } | undefined;
searchValue: string | undefined;
dataSourceIdToTitle: Map<string, string>;
}

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

this.state = {
isLoading: false,
dataSourceIdToTitle: new Map(),
options: [],
selectedIndexPattern: undefined,
searchValue: undefined,
Expand Down Expand Up @@ -113,7 +119,11 @@ export default class IndexPatternSelect extends Component<IndexPatternSelectProp

let indexPatternTitle;
try {
indexPatternTitle = await getTitle(this.props.savedObjectsClient, indexPatternId);
indexPatternTitle = await getTitle(
this.props.savedObjectsClient,
indexPatternId,
this.state.dataSourceIdToTitle
);
} catch (err) {
// index pattern no longer exists
return;
Expand Down Expand Up @@ -161,16 +171,66 @@ export default class IndexPatternSelect extends Component<IndexPatternSelectProp
// We need this check to handle the case where search results come back in a different
// order than they were sent out. Only load results for the most recent search.
if (searchValue === this.state.searchValue) {
const dataSourcesToFetch: Array<{ type: string; id: string }> = [];
savedObjects.map((indexPatternSavedObject: SimpleSavedObject<any>) => {
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<any>) => {
dataSourceIdToTitleToUpdate.set(
dataSourceSavedObject.id,
dataSourceSavedObject.attributes.title
);
});
}

const options = savedObjects.map((indexPatternSavedObject: SimpleSavedObject<any>) => {
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();
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/data_source/common/data_sources/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface DataSourceAttributes extends SavedObjectAttributes {
credentials: UsernamePasswordTypedContent | SigV4Content | undefined | AuthTypeContent;
};
lastUpdatedTime?: string;
name: AuthType | string;
}

export interface AuthTypeContent {
Expand All @@ -30,6 +31,7 @@ export interface SigV4Content extends SavedObjectAttributes {
secretKey: string;
region: string;
service?: SigV4ServiceName;
sessionToken?: string;
}

export interface UsernamePasswordTypedContent extends SavedObjectAttributes {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { IAuthenticationMethodRegistery } from './authentication_methods_registry';

const create = () =>
(({
getAllAuthenticationMethods: jest.fn(),
getAuthenticationMethod: jest.fn(),
} as unknown) as jest.Mocked<IAuthenticationMethodRegistery>);

export const authenticationMethodRegisteryMock = { create };
2 changes: 2 additions & 0 deletions src/plugins/data_source/server/auth_registry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export {
IAuthenticationMethodRegistery,
AuthenticationMethodRegistery,
} from './authentication_methods_registry';

export { authenticationMethodRegisteryMock } from './authentication_methods_registry.mock';
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ export const parseClientOptionsMock = jest.fn();
jest.doMock('./client_config', () => ({
parseClientOptions: parseClientOptionsMock,
}));

export const authRegistryCredentialProviderMock = jest.fn();
jest.doMock('../util/credential_provider', () => ({
authRegistryCredentialProvider: authRegistryCredentialProviderMock,
}));
Loading

0 comments on commit cbf1fec

Please sign in to comment.