diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d4ecbba3ea..fac5e39539e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [BUG][Discover] Fix advanced setting `discover:modifyColumnsOnSwitch` ([#5508](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5508)) - [Discover] Fix missing index pattern field from breaking Discover [#5626](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5626) - [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) ### 🚞 Infrastructure diff --git a/src/plugins/data_source/server/routes/data_source_connection_validator.test.ts b/src/plugins/data_source/server/routes/data_source_connection_validator.test.ts new file mode 100644 index 000000000000..219888199016 --- /dev/null +++ b/src/plugins/data_source/server/routes/data_source_connection_validator.test.ts @@ -0,0 +1,132 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { opensearchServiceMock } from '../../../../core/server/mocks'; +import { DataSourceConnectionValidator } from './data_source_connection_validator'; +import { SigV4ServiceName } from '../../common/data_sources'; + +describe('DataSourceManagement: data_source_connection_validator.ts', () => { + describe('Test datasource connection without SigV4 auth', () => { + test('Success: opensearch client response code is 200 and response body have cluster name', async () => { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.info.mockResolvedValue( + opensearchServiceMock.createApiResponse({ + statusCode: 200, + body: { + cluster_name: 'This is the cluster name', + }, + }) + ); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, {}); + const validateDataSourcesResponse = await dataSourceValidator.validate(); + expect(validateDataSourcesResponse.statusCode).toBe(200); + }); + + test('failure: opensearch client response code is 200 but response body not have cluster name', async () => { + try { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.info.mockResolvedValue( + opensearchServiceMock.createApiResponse({ + statusCode: 200, + body: { + Message: 'Response without cluster name.', + }, + }) + ); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, {}); + await dataSourceValidator.validate(); + } catch (e) { + expect(e).toBeTruthy(); + expect(e.message).toContain('Response without cluster name.'); + } + }); + + test('failure: opensearch client response code is other than 200', async () => { + const statusCodeList = [100, 202, 300, 400, 500]; + statusCodeList.forEach(async function (code) { + try { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.info.mockResolvedValue( + opensearchServiceMock.createApiResponse({ + statusCode: code, + body: { + Message: 'Your request is not correct.', + }, + }) + ); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, {}); + await dataSourceValidator.validate(); + } catch (e) { + expect(e).toBeTruthy(); + expect(e.message).toContain('Your request is not correct.'); + } + }); + }); + }); + + describe('Test datasource connection for SigV4 auth', () => { + test('Success: opensearch client response code is 200 and response body is not empty', async () => { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.cat.indices.mockResolvedValue(opensearchServiceMock.createApiResponse()); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, { + auth: { + credentials: { + service: SigV4ServiceName.OpenSearchServerless, + }, + }, + }); + const validateDataSourcesResponse = await dataSourceValidator.validate(); + expect(validateDataSourcesResponse.statusCode).toBe(200); + }); + + test('failure: opensearch client response code is 200 and response body is empty', async () => { + try { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.cat.indices.mockResolvedValue(opensearchServiceMock.createApiResponse()); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, { + auth: { + statusCode: 200, + body: '', + credentials: { + service: SigV4ServiceName.OpenSearchServerless, + }, + }, + }); + const validateDataSourcesResponse = await dataSourceValidator.validate(); + expect(validateDataSourcesResponse.statusCode).toBe(200); + } catch (e) { + expect(e).toBeTruthy(); + } + }); + + test('failure: opensearch client response code is other than 200', async () => { + const statusCodeList = [100, 202, 300, 400, 500]; + statusCodeList.forEach(async function (code) { + try { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.cat.indices.mockResolvedValue( + opensearchServiceMock.createApiResponse({ + statusCode: code, + body: { + Message: 'Your request is not correct.', + }, + }) + ); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, { + auth: { + credentials: { + service: SigV4ServiceName.OpenSearchServerless, + }, + }, + }); + await dataSourceValidator.validate(); + } catch (e) { + expect(e).toBeTruthy(); + expect(e.message).toContain('Your request is not correct.'); + } + }); + }); + }); +}); diff --git a/src/plugins/data_source/server/routes/data_source_connection_validator.ts b/src/plugins/data_source/server/routes/data_source_connection_validator.ts index f3233859e854..735d1429414c 100644 --- a/src/plugins/data_source/server/routes/data_source_connection_validator.ts +++ b/src/plugins/data_source/server/routes/data_source_connection_validator.ts @@ -14,10 +14,23 @@ export class DataSourceConnectionValidator { async validate() { try { + let validationResponse; // Amazon OpenSearch Serverless does not support .info() API - if (this.dataSourceAttr.auth?.credentials?.service === SigV4ServiceName.OpenSearchServerless) - return await this.callDataCluster.cat.indices(); - return await this.callDataCluster.info(); + if ( + this.dataSourceAttr.auth?.credentials?.service === SigV4ServiceName.OpenSearchServerless + ) { + validationResponse = await this.callDataCluster.cat.indices(); + if (validationResponse?.statusCode === 200 && validationResponse?.body) { + return validationResponse; + } + } else { + validationResponse = await this.callDataCluster.info(); + if (validationResponse?.statusCode === 200 && validationResponse?.body?.cluster_name) { + return validationResponse; + } + } + + throw new Error(JSON.stringify(validationResponse?.body)); } catch (e) { throw createDataSourceError(e); }