From 190dab0570929085dd20f1c009440e97001009d7 Mon Sep 17 00:00:00 2001 From: ZilongX <99905560+ZilongX@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:05:05 -0700 Subject: [PATCH] [Multiple Datasource][Version Decoupling] Add data source engine type to data source saved object (#7026) * [Multiple Datasource][Version Decoupling] Add data source engine type to data source saved object Signed-off-by: Zilong Xia * Changeset file for PR #7026 created/updated --------- Signed-off-by: Zilong Xia Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> --- changelogs/fragments/7026.yml | 2 ++ .../data_source/common/data_sources/types.ts | 8 +++++ .../data_source_connection_validator.test.ts | 28 +++++++++------ .../data_source_connection_validator.ts | 34 ++++++++++++++----- .../routes/fetch_data_source_metadata.test.ts | 10 +++++- .../routes/fetch_data_source_metadata.ts | 5 ++- src/plugins/data_source/server/types.ts | 6 ++++ .../create_data_source_wizard.tsx | 1 + 8 files changed, 73 insertions(+), 21 deletions(-) create mode 100644 changelogs/fragments/7026.yml diff --git a/changelogs/fragments/7026.yml b/changelogs/fragments/7026.yml new file mode 100644 index 000000000000..e8461fbe5af1 --- /dev/null +++ b/changelogs/fragments/7026.yml @@ -0,0 +1,2 @@ +fix: +- [MDS] Add data source engine type to data source saved object ([#7026](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7026)) \ No newline at end of file diff --git a/src/plugins/data_source/common/data_sources/types.ts b/src/plugins/data_source/common/data_sources/types.ts index 7c32264da0eb..9ae5d9652f1d 100644 --- a/src/plugins/data_source/common/data_sources/types.ts +++ b/src/plugins/data_source/common/data_sources/types.ts @@ -10,6 +10,7 @@ export interface DataSourceAttributes extends SavedObjectAttributes { description?: string; endpoint: string; dataSourceVersion?: string; + dataSourceEngineType?: DataSourceEngineType; installedPlugins?: string[]; auth: { type: AuthType | string; @@ -52,3 +53,10 @@ export enum SigV4ServiceName { } export { DataSourceError } from './error'; + +export enum DataSourceEngineType { + OpenSearch = 'OpenSearch', + OpenSearchServerless = 'OpenSearch Serverless', + Elasticsearch = 'Elasticsearch', + NA = 'No Engine Type Available', +} 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 index 129234d16ddc..f2784092bc68 100644 --- 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 @@ -24,7 +24,7 @@ describe('DataSourceManagement: data_source_connection_validator.ts', () => { expect(validateDataSourcesResponse.statusCode).toBe(200); }); - test('fetchDataSourceVersion - Success: opensearch client response code is 200 and response body have version number', async () => { + test('fetchDataSourceInfo - Success: opensearch client response code is 200 and response body have version number and distribution', async () => { const opensearchClient = opensearchServiceMock.createOpenSearchClient(); opensearchClient.info.mockResolvedValue( opensearchServiceMock.createApiResponse({ @@ -32,13 +32,15 @@ describe('DataSourceManagement: data_source_connection_validator.ts', () => { body: { version: { number: '2.11.0', + distribution: 'opensearch', }, }, }) ); const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, {}); - const fetchDataSourcesVersionResponse = await dataSourceValidator.fetchDataSourceVersion(); - expect(fetchDataSourcesVersionResponse).toBe('2.11.0'); + const fetchDataSourcesVersionResponse = await dataSourceValidator.fetchDataSourceInfo(); + expect(fetchDataSourcesVersionResponse.dataSourceVersion).toBe('2.11.0'); + expect(fetchDataSourcesVersionResponse.dataSourceEngineType).toBe('OpenSearch'); }); test('fetchInstalledPlugins - Success: opensearch client response code is 200 and response body have installed plugin list', async () => { @@ -92,8 +94,8 @@ describe('DataSourceManagement: data_source_connection_validator.ts', () => { } }); - // In case fetchDataSourceVersion call succeeded yet did not return version number, return an empty version instead of raising exceptions - test('fetchDataSourceVersion - Success:opensearch client response code is 200 but response body does not have version number', async () => { + // In case fetchDataSourceInfo call succeeded yet did not return version number and distribution, return an empty info instead of raising exceptions + test('fetchDataSourceInfo - Success:opensearch client response code is 200 but response body does not have version number', async () => { const opensearchClient = opensearchServiceMock.createOpenSearchClient(); opensearchClient.info.mockResolvedValue( opensearchServiceMock.createApiResponse({ @@ -104,8 +106,9 @@ describe('DataSourceManagement: data_source_connection_validator.ts', () => { }) ); const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, {}); - const fetchDataSourcesVersionResponse = await dataSourceValidator.fetchDataSourceVersion(); - expect(fetchDataSourcesVersionResponse).toBe(''); + const fetchDataSourcesVersionResponse = await dataSourceValidator.fetchDataSourceInfo(); + expect(fetchDataSourcesVersionResponse.dataSourceVersion).toBe(''); + expect(fetchDataSourcesVersionResponse.dataSourceEngineType).toBe('No Engine Type Available'); }); test('failure: opensearch client response code is other than 200', async () => { @@ -130,8 +133,8 @@ describe('DataSourceManagement: data_source_connection_validator.ts', () => { }); }); - // In case fetchDataSourceVersion call failed, return an empty version instead of raising exceptions - test('fetchDataSourceVersion - Failure: opensearch client response code is other than 200', async () => { + // In case fetchDataSourceInfo call failed, return an empty info instead of raising exceptions + test('fetchDataSourceInfo - Failure: opensearch client response code is other than 200', async () => { const statusCodeList = [100, 202, 300, 400, 500]; statusCodeList.forEach(async function (code) { const opensearchClient = opensearchServiceMock.createOpenSearchClient(); @@ -144,8 +147,11 @@ describe('DataSourceManagement: data_source_connection_validator.ts', () => { }) ); const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, {}); - const fetchDataSourcesVersionResponse = await dataSourceValidator.fetchDataSourceVersion(); - expect(fetchDataSourcesVersionResponse).toBe(''); + const fetchDataSourcesVersionResponse = await dataSourceValidator.fetchDataSourceInfo(); + expect(fetchDataSourcesVersionResponse.dataSourceVersion).toBe(''); + expect(fetchDataSourcesVersionResponse.dataSourceEngineType).toBe( + 'No Engine Type Available' + ); }); }); }); 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 8e2e34014595..a93c7fed991f 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 @@ -5,7 +5,9 @@ import { OpenSearchClient } from 'opensearch-dashboards/server'; import { createDataSourceError } from '../lib/error'; -import { SigV4ServiceName } from '../../common/data_sources'; +import { DataSourceEngineType, SigV4ServiceName } from '../../common/data_sources'; +import { DataSourceInfo } from '../types'; + export class DataSourceConnectionValidator { constructor( private readonly callDataCluster: OpenSearchClient, @@ -36,26 +38,42 @@ export class DataSourceConnectionValidator { } } - async fetchDataSourceVersion() { - let dataSourceVersion = ''; + async fetchDataSourceInfo() { + const dataSourceInfo: DataSourceInfo = { + dataSourceVersion: '', + dataSourceEngineType: DataSourceEngineType.NA, + }; + try { // OpenSearch Serverless does not have version concept if ( this.dataSourceAttr.auth?.credentials?.service === SigV4ServiceName.OpenSearchServerless ) { - return dataSourceVersion; + dataSourceInfo.dataSourceEngineType = DataSourceEngineType.OpenSearchServerless; + return dataSourceInfo; } + await this.callDataCluster .info() .then((response) => response.body) .then((body) => { - dataSourceVersion = body.version.number; + dataSourceInfo.dataSourceVersion = body.version.number; + + if ( + body.version.distribution !== null && + body.version.distribution !== undefined && + body.version.distribution === 'opensearch' + ) { + dataSourceInfo.dataSourceEngineType = DataSourceEngineType.OpenSearch; + } else { + dataSourceInfo.dataSourceEngineType = DataSourceEngineType.Elasticsearch; + } }); - return dataSourceVersion; + return dataSourceInfo; } catch (e) { - // return empty dataSource version instead of throwing exception in case info() api call fails - return dataSourceVersion; + // return default dataSourceInfo instead of throwing exception in case info() api call fails + return dataSourceInfo; } } diff --git a/src/plugins/data_source/server/routes/fetch_data_source_metadata.test.ts b/src/plugins/data_source/server/routes/fetch_data_source_metadata.test.ts index d018af82ffee..40d5c8f8ab02 100644 --- a/src/plugins/data_source/server/routes/fetch_data_source_metadata.test.ts +++ b/src/plugins/data_source/server/routes/fetch_data_source_metadata.test.ts @@ -165,7 +165,9 @@ describe(`Fetch DataSource MetaData ${URL}`, () => { const router = httpSetup.createRouter(''); dataSourceClient.info.mockImplementationOnce(() => - opensearchClientMock.createSuccessTransportRequestPromise({ version: { number: '2.11.0' } }) + opensearchClientMock.createSuccessTransportRequestPromise({ + version: { number: '2.11.0', distribution: 'opensearch' }, + }) ); const installedPlugins = [ @@ -201,6 +203,7 @@ describe(`Fetch DataSource MetaData ${URL}`, () => { .expect(200); expect(result.body).toEqual({ dataSourceVersion: '2.11.0', + dataSourceEngineType: 'OpenSearch', installedPlugins: ['opensearch-ml', 'opensearch-sql'], }); expect(dataSourceServiceSetupMock.getDataSourceClient).toHaveBeenCalledWith( @@ -224,6 +227,7 @@ describe(`Fetch DataSource MetaData ${URL}`, () => { .expect(200); expect(result.body).toEqual({ dataSourceVersion: '2.11.0', + dataSourceEngineType: 'OpenSearch', installedPlugins: ['opensearch-ml', 'opensearch-sql'], }); }); @@ -324,6 +328,7 @@ describe(`Fetch DataSource MetaData ${URL}`, () => { .expect(200); expect(result.body).toEqual({ dataSourceVersion: '2.11.0', + dataSourceEngineType: 'OpenSearch', installedPlugins: ['opensearch-ml', 'opensearch-sql'], }); }); @@ -338,6 +343,7 @@ describe(`Fetch DataSource MetaData ${URL}`, () => { .expect(200); expect(result.body).toEqual({ dataSourceVersion: '2.11.0', + dataSourceEngineType: 'OpenSearch', installedPlugins: ['opensearch-ml', 'opensearch-sql'], }); }); @@ -352,6 +358,7 @@ describe(`Fetch DataSource MetaData ${URL}`, () => { .expect(200); expect(result.body).toEqual({ dataSourceVersion: '2.11.0', + dataSourceEngineType: 'OpenSearch', installedPlugins: ['opensearch-ml', 'opensearch-sql'], }); }); @@ -366,6 +373,7 @@ describe(`Fetch DataSource MetaData ${URL}`, () => { .expect(200); expect(result.body).toEqual({ dataSourceVersion: '2.11.0', + dataSourceEngineType: 'OpenSearch', installedPlugins: ['opensearch-ml', 'opensearch-sql'], }); }); diff --git a/src/plugins/data_source/server/routes/fetch_data_source_metadata.ts b/src/plugins/data_source/server/routes/fetch_data_source_metadata.ts index 14563d0a23cc..05792d4f8296 100644 --- a/src/plugins/data_source/server/routes/fetch_data_source_metadata.ts +++ b/src/plugins/data_source/server/routes/fetch_data_source_metadata.ts @@ -94,12 +94,15 @@ export const registerFetchDataSourceMetaDataRoute = async ( dataSourceAttr ); - const dataSourceVersion = await dataSourceValidator.fetchDataSourceVersion(); + const dataSourceInfo = await dataSourceValidator.fetchDataSourceInfo(); + const dataSourceVersion = dataSourceInfo.dataSourceVersion; + const dataSourceEngineType = dataSourceInfo.dataSourceEngineType; const installedPlugins = Array.from(await dataSourceValidator.fetchInstalledPlugins()); return response.ok({ body: { dataSourceVersion, + dataSourceEngineType, installedPlugins, }, }); diff --git a/src/plugins/data_source/server/types.ts b/src/plugins/data_source/server/types.ts index e7dd0efd5d0b..f2c85dfa53d0 100644 --- a/src/plugins/data_source/server/types.ts +++ b/src/plugins/data_source/server/types.ts @@ -11,6 +11,7 @@ import { } from 'src/core/server'; import { DataSourceAttributes, + DataSourceEngineType, AuthType, UsernamePasswordTypedContent, SigV4Content, @@ -98,3 +99,8 @@ export interface DataSourcePluginStart { getAuthenticationMethodRegistry: () => IAuthenticationMethodRegistry; getCustomApiSchemaRegistry: () => CustomApiSchemaRegistry; } + +export interface DataSourceInfo { + dataSourceVersion?: string; + dataSourceEngineType?: DataSourceEngineType; +} diff --git a/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.tsx b/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.tsx index 2d7c8ace0e5c..671647c48f48 100644 --- a/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.tsx +++ b/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.tsx @@ -78,6 +78,7 @@ export const CreateDataSourceWizard: React.FunctionComponent