From 87d779319cd7682e15b23b8497ee27f980f24129 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 5 Apr 2024 02:51:24 +0000 Subject: [PATCH] =?UTF-8?q?[Multiple=20Datasource]=20Add=20installedPlugin?= =?UTF-8?q?s=20list=20to=20data=20source=20saved=20=E2=80=A6=20(#6348)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Multiple Datasource] Add installedPlugins list to data source saved object Signed-off-by: Zilong Xia * [Multiple Datasource] Fix create data source wizard test Signed-off-by: Zilong Xia --------- Signed-off-by: Zilong Xia (cherry picked from commit 740620551b80c39466706fe4f29ae77f5e2cea8f) Signed-off-by: github-actions[bot] # Conflicts: # CHANGELOG.md --- src/plugins/data_source/server/plugin.ts | 4 +- .../data_source_connection_validator.test.ts | 32 +++++++++++++ .../data_source_connection_validator.ts | 31 +++++++++++- ....ts => fetch_data_source_metadata.test.ts} | 48 +++++++++++++++---- ...rsion.ts => fetch_data_source_metadata.ts} | 9 ++-- .../create_data_source_wizard.test.tsx | 6 +-- .../create_data_source_wizard.tsx | 8 ++-- .../public/components/utils.ts | 4 +- .../data_source_management/public/mocks.ts | 3 +- .../data_source_management/public/types.ts | 1 + 10 files changed, 120 insertions(+), 26 deletions(-) rename src/plugins/data_source/server/routes/{fetch_data_source_version.test.ts => fetch_data_source_metadata.test.ts} (86%) rename src/plugins/data_source/server/routes/{fetch_data_source_version.ts => fetch_data_source_metadata.ts} (91%) diff --git a/src/plugins/data_source/server/plugin.ts b/src/plugins/data_source/server/plugin.ts index 6eabe5431689..bbf5a89d1b53 100644 --- a/src/plugins/data_source/server/plugin.ts +++ b/src/plugins/data_source/server/plugin.ts @@ -30,7 +30,7 @@ import { DATA_SOURCE_SAVED_OBJECT_TYPE } from '../common'; import { ensureRawRequest } from '../../../../src/core/server/http/router'; import { createDataSourceError } from './lib/error'; import { registerTestConnectionRoute } from './routes/test_connection'; -import { registerFetchDataSourceVersionRoute } from './routes/fetch_data_source_version'; +import { registerFetchDataSourceMetaDataRoute } from './routes/fetch_data_source_metadata'; import { AuthenticationMethodRegistry, IAuthenticationMethodRegistry } from './auth_registry'; import { CustomApiSchemaRegistry } from './schema_registry'; @@ -134,7 +134,7 @@ export class DataSourcePlugin implements Plugin { expect(fetchDataSourcesVersionResponse).toBe('2.11.0'); }); + test('fetchInstalledPlugins - Success: opensearch client response code is 200 and response body have installed plugin list', async () => { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.info.mockResolvedValue( + opensearchServiceMock.createApiResponse({ + statusCode: 200, + body: [ + { + name: 'b40f6833d895d3a95333e325e8bea79b', + component: ' analysis-icu', + version: '2.11.0', + }, + { + name: 'b40f6833d895d3a95333e325e8bea79b', + component: 'analysis-ik', + version: '2.11.0', + }, + { + name: 'b40f6833d895d3a95333e325e8bea79b', + component: 'analysis-seunjeon', + version: '2.11.0', + }, + ], + }) + ); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, {}); + const fetchInstalledPluginsReponse = Array.from( + await dataSourceValidator.fetchInstalledPlugins() + ); + const installedPlugins = ['analysis-icu', 'analysis-ik', 'analysis-seunjeon']; + fetchInstalledPluginsReponse.map((plugin) => expect(installedPlugins).toContain(plugin)); + }); + test('failure: opensearch client response code is 200 but response body not have cluster name', async () => { try { const opensearchClient = opensearchServiceMock.createOpenSearchClient(); 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 60e00d855658..0d94eaa9bb56 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 @@ -54,8 +54,37 @@ export class DataSourceConnectionValidator { return dataSourceVersion; } catch (e) { - // return empty dataSoyrce version instead of throwing exception in case info() api call fails + // return empty dataSource version instead of throwing exception in case info() api call fails return dataSourceVersion; } } + + async fetchInstalledPlugins() { + const installedPlugins = new Set(); + try { + // TODO : retrieve installed plugins from OpenSearch Serverless datasource + if ( + this.dataSourceAttr.auth?.credentials?.service === SigV4ServiceName.OpenSearchServerless + ) { + return installedPlugins; + } + + await this.callDataCluster.cat + .plugins({ + format: 'JSON', + v: true, + }) + .then((response) => response.body) + .then((body) => { + body.forEach((plugin) => { + installedPlugins.add(plugin.component); + }); + }); + + return installedPlugins; + } catch (e) { + // return empty installedPlugins instead of throwing exception in case cat.plugins() api call fails + return installedPlugins; + } + } } diff --git a/src/plugins/data_source/server/routes/fetch_data_source_version.test.ts b/src/plugins/data_source/server/routes/fetch_data_source_metadata.test.ts similarity index 86% rename from src/plugins/data_source/server/routes/fetch_data_source_version.test.ts rename to src/plugins/data_source/server/routes/fetch_data_source_metadata.test.ts index 5514cbdc40f6..d361b89ddff7 100644 --- a/src/plugins/data_source/server/routes/fetch_data_source_version.test.ts +++ b/src/plugins/data_source/server/routes/fetch_data_source_metadata.test.ts @@ -12,16 +12,17 @@ import { authenticationMethodRegistryMock } from '../auth_registry/authenticatio import { CustomApiSchemaRegistry } from '../schema_registry'; import { DataSourceServiceSetup } from '../../server/data_source_service'; import { CryptographyServiceSetup } from '../cryptography_service'; -import { registerFetchDataSourceVersionRoute } from './fetch_data_source_version'; +import { registerFetchDataSourceMetaDataRoute } from './fetch_data_source_metadata'; import { AuthType } from '../../common/data_sources'; // eslint-disable-next-line @osd/eslint/no-restricted-paths import { opensearchClientMock } from '../../../../../src/core/server/opensearch/client/mocks'; +import { index } from 'mathjs'; type SetupServerReturn = UnwrapPromise>; -const URL = '/internal/data-source-management/fetchDataSourceVersion'; +const URL = '/internal/data-source-management/fetchDataSourceMetaData'; -describe(`Fetch DataSource Version ${URL}`, () => { +describe(`Fetch DataSource MetaData ${URL}`, () => { let server: SetupServerReturn['server']; let httpSetup: SetupServerReturn['httpSetup']; let handlerContext: SetupServerReturn['handlerContext']; @@ -167,7 +168,16 @@ describe(`Fetch DataSource Version ${URL}`, () => { dataSourceClient.info.mockImplementationOnce(() => opensearchClientMock.createSuccessTransportRequestPromise({ version: { number: '2.11.0' } }) ); - registerFetchDataSourceVersionRoute( + + const installedPlugins = [ + { name: 'b40f6833d895d3a95333e325e8bea79b', component: 'opensearch-ml', version: '2.11.0' }, + { name: 'b40f6833d895d3a95333e325e8bea79b', component: 'opensearch-sql', version: '2.11.0' }, + ]; + dataSourceClient.cat.plugins.mockImplementationOnce(() => + opensearchClientMock.createSuccessTransportRequestPromise(installedPlugins) + ); + + registerFetchDataSourceMetaDataRoute( router, dataSourceServiceSetupMock, cryptographyMock, @@ -190,7 +200,10 @@ describe(`Fetch DataSource Version ${URL}`, () => { dataSourceAttr, }) .expect(200); - expect(result.body).toEqual({ dataSourceVersion: '2.11.0' }); + expect(result.body).toEqual({ + dataSourceVersion: '2.11.0', + installedPlugins: ['opensearch-ml', 'opensearch-sql'], + }); expect(dataSourceServiceSetupMock.getDataSourceClient).toHaveBeenCalledWith( expect.objectContaining({ savedObjects: handlerContext.savedObjects.client, @@ -210,7 +223,10 @@ describe(`Fetch DataSource Version ${URL}`, () => { dataSourceAttr: dataSourceAttrMissingCredentialForNoAuth, }) .expect(200); - expect(result.body).toEqual({ dataSourceVersion: '2.11.0' }); + expect(result.body).toEqual({ + dataSourceVersion: '2.11.0', + installedPlugins: ['opensearch-ml', 'opensearch-sql'], + }); }); it('no credential with basic auth should fail', async () => { @@ -307,7 +323,10 @@ describe(`Fetch DataSource Version ${URL}`, () => { dataSourceAttr: dataSourceAttrForSigV4Auth, }) .expect(200); - expect(result.body).toEqual({ dataSourceVersion: '2.11.0' }); + expect(result.body).toEqual({ + dataSourceVersion: '2.11.0', + installedPlugins: ['opensearch-ml', 'opensearch-sql'], + }); }); it('credential with registered auth type should success', async () => { @@ -318,7 +337,10 @@ describe(`Fetch DataSource Version ${URL}`, () => { dataSourceAttr: dataSourceAttrForRegisteredAuthWithCredentials, }) .expect(200); - expect(result.body).toEqual({ dataSourceVersion: '2.11.0' }); + expect(result.body).toEqual({ + dataSourceVersion: '2.11.0', + installedPlugins: ['opensearch-ml', 'opensearch-sql'], + }); }); it('empty credential with registered auth type should success', async () => { @@ -329,7 +351,10 @@ describe(`Fetch DataSource Version ${URL}`, () => { dataSourceAttr: dataSourceAttrForRegisteredAuthWithEmptyCredentials, }) .expect(200); - expect(result.body).toEqual({ dataSourceVersion: '2.11.0' }); + expect(result.body).toEqual({ + dataSourceVersion: '2.11.0', + installedPlugins: ['opensearch-ml', 'opensearch-sql'], + }); }); it('no credential with registered auth type should success', async () => { @@ -340,6 +365,9 @@ describe(`Fetch DataSource Version ${URL}`, () => { dataSourceAttr: dataSourceAttrForRegisteredAuthWithoutCredentials, }) .expect(200); - expect(result.body).toEqual({ dataSourceVersion: '2.11.0' }); + expect(result.body).toEqual({ + dataSourceVersion: '2.11.0', + installedPlugins: ['opensearch-ml', 'opensearch-sql'], + }); }); }); diff --git a/src/plugins/data_source/server/routes/fetch_data_source_version.ts b/src/plugins/data_source/server/routes/fetch_data_source_metadata.ts similarity index 91% rename from src/plugins/data_source/server/routes/fetch_data_source_version.ts rename to src/plugins/data_source/server/routes/fetch_data_source_metadata.ts index b2f03f7fddc0..14563d0a23cc 100644 --- a/src/plugins/data_source/server/routes/fetch_data_source_version.ts +++ b/src/plugins/data_source/server/routes/fetch_data_source_metadata.ts @@ -12,7 +12,7 @@ import { CryptographyServiceSetup } from '../cryptography_service'; import { IAuthenticationMethodRegistry } from '../auth_registry'; import { CustomApiSchemaRegistry } from '../schema_registry/custom_api_schema_registry'; -export const registerFetchDataSourceVersionRoute = async ( +export const registerFetchDataSourceMetaDataRoute = async ( router: IRouter, dataSourceServiceSetup: DataSourceServiceSetup, cryptography: CryptographyServiceSetup, @@ -22,7 +22,7 @@ export const registerFetchDataSourceVersionRoute = async ( const authRegistry = await authRegistryPromise; router.post( { - path: '/internal/data-source-management/fetchDataSourceVersion', + path: '/internal/data-source-management/fetchDataSourceMetaData', validate: { body: schema.object({ id: schema.maybe(schema.string()), @@ -75,7 +75,6 @@ export const registerFetchDataSourceVersionRoute = async ( }, async (context, request, response) => { const { dataSourceAttr, id: dataSourceId } = request.body; - let dataSourceVersion = ''; try { const dataSourceClient: OpenSearchClient = await dataSourceServiceSetup.getDataSourceClient( @@ -95,11 +94,13 @@ export const registerFetchDataSourceVersionRoute = async ( dataSourceAttr ); - dataSourceVersion = await dataSourceValidator.fetchDataSourceVersion(); + const dataSourceVersion = await dataSourceValidator.fetchDataSourceVersion(); + const installedPlugins = Array.from(await dataSourceValidator.fetchInstalledPlugins()); return response.ok({ body: { dataSourceVersion, + installedPlugins, }, }); } catch (err) { diff --git a/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.test.tsx b/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.test.tsx index aa4e128094c7..5bffaee5d3d9 100644 --- a/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.test.tsx +++ b/src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.test.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { - fetchDataSourceVersion, + fetchDataSourceMetaData, getMappedDataSources, mockDataSourceAttributesWithAuth, mockManagementPlugin, @@ -28,8 +28,8 @@ describe('Datasource Management: Create Datasource Wizard', () => { describe('case1: should load resources successfully', () => { beforeEach(async () => { spyOn(utils, 'getDataSources').and.returnValue(Promise.resolve(getMappedDataSources)); - spyOn(utils, 'fetchDataSourceVersion').and.returnValue( - Promise.resolve(fetchDataSourceVersion) + spyOn(utils, 'fetchDataSourceMetaData').and.returnValue( + Promise.resolve(fetchDataSourceMetaData) ); await act(async () => { component = mount( 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 58f9ae108083..2d7c8ace0e5c 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 @@ -20,7 +20,7 @@ import { createSingleDataSource, getDataSources, testConnection, - fetchDataSourceVersion, + fetchDataSourceMetaData, handleSetDefaultDatasource, } from '../utils'; import { LoadingMask } from '../loading_mask'; @@ -75,8 +75,10 @@ export const CreateDataSourceWizard: React.FunctionComponent { setIsLoading(true); try { - const version = await fetchDataSourceVersion(http, attributes); - attributes.dataSourceVersion = version.dataSourceVersion; + // Fetch data source metadata from added OS/ES domain/cluster + const metadata = await fetchDataSourceMetaData(http, attributes); + attributes.dataSourceVersion = metadata.dataSourceVersion; + attributes.installedPlugins = metadata.installedPlugins; await createSingleDataSource(savedObjects.client, attributes); // Set the first create data source as default data source. await handleSetDefaultDatasource(savedObjects.client, uiSettings); diff --git a/src/plugins/data_source_management/public/components/utils.ts b/src/plugins/data_source_management/public/components/utils.ts index 6cfaaa9a081a..be004c989943 100644 --- a/src/plugins/data_source_management/public/components/utils.ts +++ b/src/plugins/data_source_management/public/components/utils.ts @@ -203,7 +203,7 @@ export async function testConnection( }); } -export async function fetchDataSourceVersion( +export async function fetchDataSourceMetaData( http: HttpStart, { endpoint, auth: { type, credentials } }: DataSourceAttributes, dataSourceID?: string @@ -219,7 +219,7 @@ export async function fetchDataSourceVersion( }, }; - return await http.post(`/internal/data-source-management/fetchDataSourceVersion`, { + return await http.post(`/internal/data-source-management/fetchDataSourceMetaData`, { body: JSON.stringify(query), }); } diff --git a/src/plugins/data_source_management/public/mocks.ts b/src/plugins/data_source_management/public/mocks.ts index 257ea956ec5d..acab48df52db 100644 --- a/src/plugins/data_source_management/public/mocks.ts +++ b/src/plugins/data_source_management/public/mocks.ts @@ -232,8 +232,9 @@ export const getMappedDataSources = [ }, ]; -export const fetchDataSourceVersion = { +export const fetchDataSourceMetaData = { dataSourceVersion: '2.11.0', + installedPlugins: ['opensearch-ml', 'opensearch-sql'], }; export const mockDataSourceAttributesWithAuth = { diff --git a/src/plugins/data_source_management/public/types.ts b/src/plugins/data_source_management/public/types.ts index cdbc6aa50fb6..6aa94a982a20 100644 --- a/src/plugins/data_source_management/public/types.ts +++ b/src/plugins/data_source_management/public/types.ts @@ -141,6 +141,7 @@ export interface DataSourceAttributes extends SavedObjectAttributes { description?: string; endpoint?: string; dataSourceVersion?: string; + installedPlugins?: string[]; auth: { type: AuthType | string; credentials: