From be5a16af3af736805885425c1076b0844155dee6 Mon Sep 17 00:00:00 2001 From: "Yuanqi(Ella) Zhu" Date: Thu, 21 Mar 2024 18:09:54 +0000 Subject: [PATCH 1/2] Make sure customer always have a default datasource Signed-off-by: Yuanqi(Ella) Zhu --- CHANGELOG.md | 1 + .../create_data_source_wizard.test.tsx | 3 +- .../create_data_source_wizard.tsx | 4 +++ .../data_source_table.test.tsx | 5 +++- .../data_source_table/data_source_table.tsx | 26 +++++++++++++++- .../edit_data_source.test.tsx | 7 ++++- .../edit_data_source/edit_data_source.tsx | 20 +++++++++++++ .../public/components/utils.test.ts | 28 +++++++++++++++++ .../public/components/utils.ts | 30 ++++++++++++++++++- .../data_source_management/public/mocks.ts | 24 +++++++++++++++ 10 files changed, 143 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1f58d242853..171442d696ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multiple Datasource] Add TLS configuration for multiple data sources ([#6171](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6171)) - [Multiple Datasource] Refactor data source menu and interface to allow cleaner selection of component and related configurations ([#6256](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6256)) - [Workspace] Add create workspace page ([#6179](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6179)) +- [Multiple Datasource] Make sure customer always have a default datasource ([#6237](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6237)) ### 🐛 Bug Fixes 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 1fe6e4f5d499..bc47fbaa12e3 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 @@ -53,7 +53,7 @@ describe('Datasource Management: Create Datasource Wizard', () => { test('should create datasource successfully', async () => { spyOn(utils, 'createSingleDataSource').and.returnValue({}); - + spyOn(utils, 'handleSetDefaultDatasourceDuringCreation').and.returnValue({}); await act(async () => { // @ts-ignore await component.find(formIdentifier).first().prop('handleSubmit')( @@ -62,6 +62,7 @@ describe('Datasource Management: Create Datasource Wizard', () => { }); expect(utils.createSingleDataSource).toHaveBeenCalled(); expect(history.push).toBeCalledWith(''); + expect(utils.handleSetDefaultDatasourceDuringCreation).toHaveBeenCalled(); }); test('should fail to create datasource', async () => { 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 06b77efd9b94..9409f9ef9c15 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 @@ -21,6 +21,7 @@ import { getDataSources, testConnection, fetchDataSourceVersion, + handleSetDefaultDatasourceDuringCreation, } from '../utils'; import { LoadingMask } from '../loading_mask'; @@ -35,6 +36,7 @@ export const CreateDataSourceWizard: React.FunctionComponent().services; /* State Variables */ @@ -76,6 +78,8 @@ export const CreateDataSourceWizard: React.FunctionComponent { it('should delete confirm modal confirm button work normally', async () => { spyOn(utils, 'deleteMultipleDataSources').and.returnValue(Promise.resolve({})); - + spyOn(utils, 'handleSetDefaultDatasourceAfterDeletion').and.returnValue({}); act(() => { // @ts-ignore component.find(tableIdentifier).props().selection.onSelectionChange(getMappedDataSources); @@ -143,10 +143,12 @@ describe('DataSourceTable', () => { }); component.update(); expect(component.find(confirmModalIdentifier).exists()).toBe(false); + expect(utils.handleSetDefaultDatasourceAfterDeletion).toHaveBeenCalled(); }); it('should delete datasources & fail', async () => { spyOn(utils, 'deleteMultipleDataSources').and.returnValue(Promise.reject({})); + spyOn(utils, 'handleSetDefaultDatasourceAfterDeletion').and.returnValue({}); act(() => { // @ts-ignore component.find(tableIdentifier).props().selection.onSelectionChange(getMappedDataSources); @@ -162,6 +164,7 @@ describe('DataSourceTable', () => { }); component.update(); expect(utils.deleteMultipleDataSources).toHaveBeenCalled(); + expect(utils.handleSetDefaultDatasourceAfterDeletion).not.toHaveBeenCalled(); // @ts-ignore expect(component.find(confirmModalIdentifier).exists()).toBe(false); }); diff --git a/src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx b/src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx index b27f957fe142..d1564aa8e05b 100644 --- a/src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx +++ b/src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx @@ -29,7 +29,11 @@ import { } from '../../../../opensearch_dashboards_react/public'; import { DataSourceManagementContext, DataSourceTableItem, ToastMessageItem } from '../../types'; import { CreateButton } from '../create_button'; -import { deleteMultipleDataSources, getDataSources } from '../utils'; +import { + deleteMultipleDataSources, + getDataSources, + handleSetDefaultDatasourceAfterDeletion, +} from '../utils'; import { LoadingMask } from '../loading_mask'; /* Table config */ @@ -232,6 +236,9 @@ export const DataSourceTable = ({ history }: RouteComponentProps) => { // Fetch data sources fetchDataSources(); setConfirmDeleteVisible(false); + // Check if default data source is deleted or not. + // if yes, then set the first existing datasource as default datasource. + setDefaultDataSource(); }) .catch(() => { handleDisplayToastMessage({ @@ -245,6 +252,23 @@ export const DataSourceTable = ({ history }: RouteComponentProps) => { }); }; + const setDefaultDataSource = async () => { + try { + for (const dataSource of selectedDataSources) { + if (uiSettings.get('defaultDataSource') === dataSource.id) { + await handleSetDefaultDatasourceAfterDeletion(savedObjects.client, uiSettings); + } + } + } catch (e) { + handleDisplayToastMessage({ + id: 'dataSourcesManagement.editDataSource.setDefaultDataSourceFailMsg', + defaultMessage: 'Unable to find a default datasource. Please set a new default datasource.', + }); + } finally { + setIsDeleting(false); + } + }; + /* Table selection handlers */ const onSelectionChange = (selected: DataSourceTableItem[]) => { setSelectedDataSources(selected); diff --git a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx index 833886fff7f3..99e8fa9a67b4 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx @@ -138,7 +138,8 @@ describe('Datasource Management: Edit Datasource Wizard', () => { }); test('should delete datasource successfully', async () => { spyOn(utils, 'deleteDataSourceById').and.returnValue({}); - + spyOn(utils, 'handleSetDefaultDatasourceAfterDeletion').and.returnValue({}); + spyOn(uiSettings, 'get').and.returnValue('test1'); await act(async () => { // @ts-ignore await component.find(formIdentifier).first().prop('onDeleteDataSource')( @@ -147,9 +148,12 @@ describe('Datasource Management: Edit Datasource Wizard', () => { }); expect(utils.deleteDataSourceById).toHaveBeenCalled(); expect(history.push).toBeCalledWith(''); + expect(utils.handleSetDefaultDatasourceAfterDeletion).toHaveBeenCalled(); }); test('should fail to delete datasource', async () => { spyOn(utils, 'deleteDataSourceById').and.throwError('error'); + spyOn(utils, 'handleSetDefaultDatasourceAfterDeletion').and.returnValue({}); + spyOn(uiSettings, 'get').and.returnValue('test1'); await act(async () => { // @ts-ignore await component.find(formIdentifier).first().prop('onDeleteDataSource')( @@ -158,6 +162,7 @@ describe('Datasource Management: Edit Datasource Wizard', () => { }); component.update(); expect(utils.deleteDataSourceById).toHaveBeenCalled(); + expect(utils.handleSetDefaultDatasourceAfterDeletion).not.toHaveBeenCalled(); }); test('should test connection', () => { spyOn(utils, 'testConnection'); diff --git a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx index 46e253b2b85b..84290671c434 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx @@ -17,6 +17,7 @@ import { getDataSources, testConnection, updateDataSourceById, + handleSetDefaultDatasourceAfterDeletion, } from '../utils'; import { getEditBreadcrumbs } from '../breadcrumbs'; import { EditDataSourceForm } from './components/edit_form/edit_data_source_form'; @@ -109,6 +110,10 @@ export const EditDataSource: React.FunctionComponent { + try { + if (uiSettings.get('defaultDataSource') === dataSourceID) { + await handleSetDefaultDatasourceAfterDeletion(savedObjects.client, uiSettings); + } + } catch (e) { + handleDisplayToastMessage({ + id: 'dataSourcesManagement.editDataSource.setDefaultDataSourceFailMsg', + defaultMessage: 'Unable to find a default datasource. Please set a new default datasource.', + }); + } finally { + setIsLoading(false); + } + }; + /* Handle Test connection */ const handleTestConnection = async (attributes: DataSourceAttributes) => { await testConnection(http, attributes, dataSourceID); diff --git a/src/plugins/data_source_management/public/components/utils.test.ts b/src/plugins/data_source_management/public/components/utils.test.ts index a4069f01907b..052ed5026860 100644 --- a/src/plugins/data_source_management/public/components/utils.test.ts +++ b/src/plugins/data_source_management/public/components/utils.test.ts @@ -14,6 +14,8 @@ import { isValidUrl, testConnection, updateDataSourceById, + handleSetDefaultDatasourceAfterDeletion, + handleSetDefaultDatasourceDuringCreation, } from './utils'; import { coreMock } from '../../../../core/public/mocks'; import { @@ -24,6 +26,8 @@ import { mockDataSourceAttributesWithAuth, mockErrorResponseForSavedObjectsCalls, mockResponseForSavedObjectsCalls, + mockUiSettingsCalls, + getSingleDataSourceResponse, } from '../mocks'; import { AuthType, @@ -36,6 +40,7 @@ import { AuthenticationMethod, AuthenticationMethodRegistry } from '../auth_regi import { deepEqual } from 'assert'; const { savedObjects } = coreMock.createStart(); +const { uiSettings } = coreMock.createStart(); describe('DataSourceManagement: Utils.ts', () => { describe('Get data source', () => { @@ -274,7 +279,30 @@ describe('DataSourceManagement: Utils.ts', () => { expect(getDefaultAuthMethod(authenticationMethodRegistry)?.name).toBe(AuthType.NoAuth); }); }); + describe('handleSetDefaultDatasourceAfterDeletion', () => { + test('should remove defaultDataSource setting and set new defaultDataSource if data sources exist', async () => { + mockResponseForSavedObjectsCalls(savedObjects.client, 'find', getDataSourcesResponse); + mockUiSettingsCalls(uiSettings, 'get', 'test'); + await handleSetDefaultDatasourceAfterDeletion(savedObjects.client, uiSettings); + expect(uiSettings.set).toHaveBeenCalledWith('defaultDataSource', 'test'); + }); + }); + describe('handleSetDefaultDatasourceDuringCreation', () => { + beforeEach(() => { + jest.clearAllMocks(); // Reset all mock calls before each test + }); + test('should not set defaultDataSource if more than one data source exists', async () => { + mockResponseForSavedObjectsCalls(savedObjects.client, 'find', getDataSourcesResponse); + await handleSetDefaultDatasourceDuringCreation(savedObjects.client, uiSettings); + expect(uiSettings.set).not.toHaveBeenCalled(); + }); + test('should set defaultDataSource if only one data source exists', async () => { + mockResponseForSavedObjectsCalls(savedObjects.client, 'find', getSingleDataSourceResponse); + await handleSetDefaultDatasourceDuringCreation(savedObjects.client, uiSettings); + expect(uiSettings.set).toHaveBeenCalled(); + }); + }); describe('Check extractRegisteredAuthTypeCredentials method', () => { test('Should extract credential field successfully', () => { const authTypeToBeTested = 'Some Auth Type'; diff --git a/src/plugins/data_source_management/public/components/utils.ts b/src/plugins/data_source_management/public/components/utils.ts index d7d6e94265e2..ef2173ce96db 100644 --- a/src/plugins/data_source_management/public/components/utils.ts +++ b/src/plugins/data_source_management/public/components/utils.ts @@ -3,7 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { HttpStart, SavedObjectsClientContract, SavedObject } from 'src/core/public'; +import { + HttpStart, + SavedObjectsClientContract, + SavedObject, + IUiSettingsClient, +} from 'src/core/public'; import { DataSourceAttributes, DataSourceTableItem, @@ -49,6 +54,29 @@ export async function getDataSourcesWithFields( return response?.savedObjects; } +export async function handleSetDefaultDatasourceAfterDeletion( + savedObjectsClient: SavedObjectsClientContract, + uiSettings: IUiSettingsClient +) { + uiSettings.remove('defaultDataSource'); + const listOfDataSources: DataSourceTableItem[] = await getDataSources(savedObjectsClient); + if (Array.isArray(listOfDataSources) && listOfDataSources.length >= 1) { + const datasourceId = listOfDataSources[0].id; + await uiSettings.set('defaultDataSource', datasourceId); + } +} + +export async function handleSetDefaultDatasourceDuringCreation( + savedObjectsClient: SavedObjectsClientContract, + uiSettings: IUiSettingsClient +) { + const listOfDataSources: DataSourceTableItem[] = await getDataSources(savedObjectsClient); + if (Array.isArray(listOfDataSources) && listOfDataSources.length === 1) { + const datasourceId = listOfDataSources[0].id; + await uiSettings.set('defaultDataSource', datasourceId); + } +} + export async function getDataSourceById( id: string, savedObjectsClient: SavedObjectsClientContract diff --git a/src/plugins/data_source_management/public/mocks.ts b/src/plugins/data_source_management/public/mocks.ts index 90368762698f..d04fc2a362d3 100644 --- a/src/plugins/data_source_management/public/mocks.ts +++ b/src/plugins/data_source_management/public/mocks.ts @@ -6,6 +6,7 @@ import React from 'react'; import { throwError } from 'rxjs'; import { SavedObjectsClientContract } from 'opensearch-dashboards/public'; +import { IUiSettingsClient } from 'src/core/public'; import { AuthType, DataSourceAttributes } from './types'; import { coreMock } from '../../../core/public/mocks'; import { @@ -62,6 +63,21 @@ export const mockManagementPlugin = { docLinks, }; +export const getSingleDataSourceResponse = { + savedObjects: [ + { + id: 'test', + type: 'data-source', + description: 'test datasource', + title: 'test', + get(field: string) { + const me: any = this || {}; + return me[field]; + }, + }, + ], +}; + /* Mock data responses - JSON*/ export const getDataSourcesResponse = { savedObjects: [ @@ -263,6 +279,14 @@ export const mockErrorResponseForSavedObjectsCalls = ( ); }; +export const mockUiSettingsCalls = ( + uiSettings: IUiSettingsClient, + uiSettingsMethodName: 'get' | 'set', + response: any +) => { + (uiSettings[uiSettingsMethodName] as jest.Mock).mockReturnValue(response); +}; + export interface TestPluginReturn { setup: DataSourceManagementPluginSetup; doStart: () => DataSourceManagementPluginStart; From 0c44ea9d03536275eccb7fb9e31634b4d0692910 Mon Sep 17 00:00:00 2001 From: "Yuanqi(Ella) Zhu" Date: Tue, 26 Mar 2024 05:57:16 +0000 Subject: [PATCH 2/2] Handle set as default Signed-off-by: Yuanqi(Ella) Zhu --- .../create_data_source_wizard.test.tsx | 4 +- .../create_data_source_wizard.tsx | 4 +- .../data_source_table.test.tsx | 8 ++-- .../data_source_table/data_source_table.tsx | 8 +--- .../edit_data_source.test.tsx | 8 ++-- .../edit_data_source/edit_data_source.tsx | 7 ++- .../public/components/utils.test.ts | 44 ++++++++++++++----- .../public/components/utils.ts | 21 ++++----- 8 files changed, 60 insertions(+), 44 deletions(-) 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 bc47fbaa12e3..aa4e128094c7 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 @@ -53,7 +53,7 @@ describe('Datasource Management: Create Datasource Wizard', () => { test('should create datasource successfully', async () => { spyOn(utils, 'createSingleDataSource').and.returnValue({}); - spyOn(utils, 'handleSetDefaultDatasourceDuringCreation').and.returnValue({}); + spyOn(utils, 'handleSetDefaultDatasource').and.returnValue({}); await act(async () => { // @ts-ignore await component.find(formIdentifier).first().prop('handleSubmit')( @@ -62,7 +62,7 @@ describe('Datasource Management: Create Datasource Wizard', () => { }); expect(utils.createSingleDataSource).toHaveBeenCalled(); expect(history.push).toBeCalledWith(''); - expect(utils.handleSetDefaultDatasourceDuringCreation).toHaveBeenCalled(); + expect(utils.handleSetDefaultDatasource).toHaveBeenCalled(); }); test('should fail to create datasource', async () => { 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 9409f9ef9c15..58f9ae108083 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 @@ -21,7 +21,7 @@ import { getDataSources, testConnection, fetchDataSourceVersion, - handleSetDefaultDatasourceDuringCreation, + handleSetDefaultDatasource, } from '../utils'; import { LoadingMask } from '../loading_mask'; @@ -79,7 +79,7 @@ export const CreateDataSourceWizard: React.FunctionComponent { it('should delete confirm modal confirm button work normally', async () => { spyOn(utils, 'deleteMultipleDataSources').and.returnValue(Promise.resolve({})); - spyOn(utils, 'handleSetDefaultDatasourceAfterDeletion').and.returnValue({}); + spyOn(utils, 'setFirstDataSourceAsDefault').and.returnValue({}); act(() => { // @ts-ignore component.find(tableIdentifier).props().selection.onSelectionChange(getMappedDataSources); @@ -143,12 +143,12 @@ describe('DataSourceTable', () => { }); component.update(); expect(component.find(confirmModalIdentifier).exists()).toBe(false); - expect(utils.handleSetDefaultDatasourceAfterDeletion).toHaveBeenCalled(); + expect(utils.setFirstDataSourceAsDefault).toHaveBeenCalled(); }); it('should delete datasources & fail', async () => { spyOn(utils, 'deleteMultipleDataSources').and.returnValue(Promise.reject({})); - spyOn(utils, 'handleSetDefaultDatasourceAfterDeletion').and.returnValue({}); + spyOn(utils, 'setFirstDataSourceAsDefault').and.returnValue({}); act(() => { // @ts-ignore component.find(tableIdentifier).props().selection.onSelectionChange(getMappedDataSources); @@ -164,7 +164,7 @@ describe('DataSourceTable', () => { }); component.update(); expect(utils.deleteMultipleDataSources).toHaveBeenCalled(); - expect(utils.handleSetDefaultDatasourceAfterDeletion).not.toHaveBeenCalled(); + expect(utils.setFirstDataSourceAsDefault).not.toHaveBeenCalled(); // @ts-ignore expect(component.find(confirmModalIdentifier).exists()).toBe(false); }); diff --git a/src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx b/src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx index d1564aa8e05b..9b9fd9488290 100644 --- a/src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx +++ b/src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx @@ -29,11 +29,7 @@ import { } from '../../../../opensearch_dashboards_react/public'; import { DataSourceManagementContext, DataSourceTableItem, ToastMessageItem } from '../../types'; import { CreateButton } from '../create_button'; -import { - deleteMultipleDataSources, - getDataSources, - handleSetDefaultDatasourceAfterDeletion, -} from '../utils'; +import { deleteMultipleDataSources, getDataSources, setFirstDataSourceAsDefault } from '../utils'; import { LoadingMask } from '../loading_mask'; /* Table config */ @@ -256,7 +252,7 @@ export const DataSourceTable = ({ history }: RouteComponentProps) => { try { for (const dataSource of selectedDataSources) { if (uiSettings.get('defaultDataSource') === dataSource.id) { - await handleSetDefaultDatasourceAfterDeletion(savedObjects.client, uiSettings); + await setFirstDataSourceAsDefault(savedObjects.client, uiSettings, true); } } } catch (e) { diff --git a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx index 99e8fa9a67b4..d8d175a920d9 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx @@ -138,7 +138,7 @@ describe('Datasource Management: Edit Datasource Wizard', () => { }); test('should delete datasource successfully', async () => { spyOn(utils, 'deleteDataSourceById').and.returnValue({}); - spyOn(utils, 'handleSetDefaultDatasourceAfterDeletion').and.returnValue({}); + spyOn(utils, 'setFirstDataSourceAsDefault').and.returnValue({}); spyOn(uiSettings, 'get').and.returnValue('test1'); await act(async () => { // @ts-ignore @@ -148,11 +148,11 @@ describe('Datasource Management: Edit Datasource Wizard', () => { }); expect(utils.deleteDataSourceById).toHaveBeenCalled(); expect(history.push).toBeCalledWith(''); - expect(utils.handleSetDefaultDatasourceAfterDeletion).toHaveBeenCalled(); + expect(utils.setFirstDataSourceAsDefault).toHaveBeenCalled(); }); test('should fail to delete datasource', async () => { spyOn(utils, 'deleteDataSourceById').and.throwError('error'); - spyOn(utils, 'handleSetDefaultDatasourceAfterDeletion').and.returnValue({}); + spyOn(utils, 'setFirstDataSourceAsDefault').and.returnValue({}); spyOn(uiSettings, 'get').and.returnValue('test1'); await act(async () => { // @ts-ignore @@ -162,7 +162,7 @@ describe('Datasource Management: Edit Datasource Wizard', () => { }); component.update(); expect(utils.deleteDataSourceById).toHaveBeenCalled(); - expect(utils.handleSetDefaultDatasourceAfterDeletion).not.toHaveBeenCalled(); + expect(utils.setFirstDataSourceAsDefault).not.toHaveBeenCalled(); }); test('should test connection', () => { spyOn(utils, 'testConnection'); diff --git a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx index 84290671c434..ab1e8531c801 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.tsx @@ -17,7 +17,7 @@ import { getDataSources, testConnection, updateDataSourceById, - handleSetDefaultDatasourceAfterDeletion, + setFirstDataSourceAsDefault, } from '../utils'; import { getEditBreadcrumbs } from '../breadcrumbs'; import { EditDataSourceForm } from './components/edit_form/edit_data_source_form'; @@ -127,15 +127,14 @@ export const EditDataSource: React.FunctionComponent { try { if (uiSettings.get('defaultDataSource') === dataSourceID) { - await handleSetDefaultDatasourceAfterDeletion(savedObjects.client, uiSettings); + await setFirstDataSourceAsDefault(savedObjects.client, uiSettings, true); } } catch (e) { + setIsLoading(false); handleDisplayToastMessage({ id: 'dataSourcesManagement.editDataSource.setDefaultDataSourceFailMsg', defaultMessage: 'Unable to find a default datasource. Please set a new default datasource.', }); - } finally { - setIsLoading(false); } }; diff --git a/src/plugins/data_source_management/public/components/utils.test.ts b/src/plugins/data_source_management/public/components/utils.test.ts index 052ed5026860..f2b1f709cb18 100644 --- a/src/plugins/data_source_management/public/components/utils.test.ts +++ b/src/plugins/data_source_management/public/components/utils.test.ts @@ -14,8 +14,8 @@ import { isValidUrl, testConnection, updateDataSourceById, - handleSetDefaultDatasourceAfterDeletion, - handleSetDefaultDatasourceDuringCreation, + handleSetDefaultDatasource, + setFirstDataSourceAsDefault, } from './utils'; import { coreMock } from '../../../../core/public/mocks'; import { @@ -279,29 +279,49 @@ describe('DataSourceManagement: Utils.ts', () => { expect(getDefaultAuthMethod(authenticationMethodRegistry)?.name).toBe(AuthType.NoAuth); }); }); - describe('handleSetDefaultDatasourceAfterDeletion', () => { - test('should remove defaultDataSource setting and set new defaultDataSource if data sources exist', async () => { + describe('handle set default datasource', () => { + beforeEach(() => { + jest.clearAllMocks(); // Reset all mock calls before each test + }); + test('should set default datasource when it does not have default datasource ', async () => { + mockUiSettingsCalls(uiSettings, 'get', null); mockResponseForSavedObjectsCalls(savedObjects.client, 'find', getDataSourcesResponse); + await handleSetDefaultDatasource(savedObjects.client, uiSettings); + expect(uiSettings.set).toHaveBeenCalled(); + }); + test('should not set default datasource when it has default datasouce', async () => { mockUiSettingsCalls(uiSettings, 'get', 'test'); - - await handleSetDefaultDatasourceAfterDeletion(savedObjects.client, uiSettings); - expect(uiSettings.set).toHaveBeenCalledWith('defaultDataSource', 'test'); + mockResponseForSavedObjectsCalls(savedObjects.client, 'find', getDataSourcesResponse); + await handleSetDefaultDatasource(savedObjects.client, uiSettings); + expect(uiSettings.set).not.toHaveBeenCalled(); }); }); - describe('handleSetDefaultDatasourceDuringCreation', () => { + describe('set first aataSource as default', () => { beforeEach(() => { jest.clearAllMocks(); // Reset all mock calls before each test }); - test('should not set defaultDataSource if more than one data source exists', async () => { + test('should set defaultDataSource if more than one data source exists', async () => { mockResponseForSavedObjectsCalls(savedObjects.client, 'find', getDataSourcesResponse); - await handleSetDefaultDatasourceDuringCreation(savedObjects.client, uiSettings); - expect(uiSettings.set).not.toHaveBeenCalled(); + await setFirstDataSourceAsDefault(savedObjects.client, uiSettings, true); + expect(uiSettings.set).toHaveBeenCalled(); }); test('should set defaultDataSource if only one data source exists', async () => { mockResponseForSavedObjectsCalls(savedObjects.client, 'find', getSingleDataSourceResponse); - await handleSetDefaultDatasourceDuringCreation(savedObjects.client, uiSettings); + await setFirstDataSourceAsDefault(savedObjects.client, uiSettings, true); expect(uiSettings.set).toHaveBeenCalled(); }); + test('should not set defaultDataSource if no data source exists', async () => { + mockResponseForSavedObjectsCalls(savedObjects.client, 'find', { savedObjects: [] }); + await setFirstDataSourceAsDefault(savedObjects.client, uiSettings, true); + expect(uiSettings.remove).toHaveBeenCalled(); + expect(uiSettings.set).not.toHaveBeenCalled(); + }); + test('should not set defaultDataSource if no data source exists and no default datasouce', async () => { + mockResponseForSavedObjectsCalls(savedObjects.client, 'find', { savedObjects: [] }); + await setFirstDataSourceAsDefault(savedObjects.client, uiSettings, false); + expect(uiSettings.remove).not.toHaveBeenCalled(); + expect(uiSettings.set).not.toHaveBeenCalled(); + }); }); describe('Check extractRegisteredAuthTypeCredentials method', () => { test('Should extract credential field successfully', () => { diff --git a/src/plugins/data_source_management/public/components/utils.ts b/src/plugins/data_source_management/public/components/utils.ts index ef2173ce96db..b911203cd288 100644 --- a/src/plugins/data_source_management/public/components/utils.ts +++ b/src/plugins/data_source_management/public/components/utils.ts @@ -54,26 +54,27 @@ export async function getDataSourcesWithFields( return response?.savedObjects; } -export async function handleSetDefaultDatasourceAfterDeletion( +export async function handleSetDefaultDatasource( savedObjectsClient: SavedObjectsClientContract, uiSettings: IUiSettingsClient ) { - uiSettings.remove('defaultDataSource'); - const listOfDataSources: DataSourceTableItem[] = await getDataSources(savedObjectsClient); - if (Array.isArray(listOfDataSources) && listOfDataSources.length >= 1) { - const datasourceId = listOfDataSources[0].id; - await uiSettings.set('defaultDataSource', datasourceId); + if (uiSettings.get('defaultDataSource', null) === null) { + return await setFirstDataSourceAsDefault(savedObjectsClient, uiSettings, false); } } -export async function handleSetDefaultDatasourceDuringCreation( +export async function setFirstDataSourceAsDefault( savedObjectsClient: SavedObjectsClientContract, - uiSettings: IUiSettingsClient + uiSettings: IUiSettingsClient, + exists: boolean ) { + if (exists) { + uiSettings.remove('defaultDataSource'); + } const listOfDataSources: DataSourceTableItem[] = await getDataSources(savedObjectsClient); - if (Array.isArray(listOfDataSources) && listOfDataSources.length === 1) { + if (Array.isArray(listOfDataSources) && listOfDataSources.length >= 1) { const datasourceId = listOfDataSources[0].id; - await uiSettings.set('defaultDataSource', datasourceId); + return await uiSettings.set('defaultDataSource', datasourceId); } }