Skip to content

Commit

Permalink
Make sure customer always have a default datasource
Browse files Browse the repository at this point in the history
Signed-off-by: Yuanqi(Ella) Zhu <zhyuanqi@amazon.com>
  • Loading branch information
zhyuanqi committed Mar 21, 2024
1 parent 4a8e3e8 commit 9822ef0
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [Workspace] Validate if workspace exists when setup inside a workspace ([#6154](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6154))
- [Workspace] Register a workspace dropdown menu at the top of left nav bar ([#6150](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6150))
- [Multiple Datasource] Add icon in datasource table page to show the default datasource ([#6231](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6231))
- [Multiple Datasource] Make sure customer always have a default datasource

### 🐛 Bug Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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')(
Expand All @@ -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 () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
getDataSources,
testConnection,
fetchDataSourceVersion,
handleSetDefaultDatasourceDuringCreation,
} from '../utils';
import { LoadingMask } from '../loading_mask';

Expand All @@ -35,6 +36,7 @@ export const CreateDataSourceWizard: React.FunctionComponent<CreateDataSourceWiz
setBreadcrumbs,
http,
notifications: { toasts },
uiSettings,
} = useOpenSearchDashboards<DataSourceManagementContext>().services;

/* State Variables */
Expand Down Expand Up @@ -77,6 +79,8 @@ export const CreateDataSourceWizard: React.FunctionComponent<CreateDataSourceWiz
attributes.dataSourceVersion = version.dataSourceVersion;
await createSingleDataSource(savedObjects.client, attributes);
props.history.push('');
// Set the first create data source as default data source.

Check failure on line 82 in src/plugins/data_source_management/public/components/create_data_source_wizard/create_data_source_wizard.tsx

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

Delete `·`
await handleSetDefaultDatasourceDuringCreation(savedObjects.client, uiSettings);
} catch (e) {
setIsLoading(false);
handleDisplayToastMessage({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { ScopedHistory } from 'opensearch-dashboards/public';
import { scopedHistoryMock } from '../../../../../core/public/mocks';
import { OpenSearchDashboardsContextProvider } from '../../../../opensearch_dashboards_react/public';
import { getMappedDataSources, mockManagementPlugin } from '../../mocks';
import { ExpandPanelAction } from 'src/plugins/dashboard/public/application';
import { util } from 'node-forge';

const deleteButtonIdentifier = '[data-test-subj="deleteDataSourceConnections"]';
const tableIdentifier = 'EuiInMemoryTable';
Expand Down Expand Up @@ -128,7 +130,7 @@ describe('DataSourceTable', () => {

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);
Expand All @@ -143,10 +145,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);
Expand All @@ -162,6 +166,7 @@ describe('DataSourceTable', () => {
});
component.update();
expect(utils.deleteMultipleDataSources).toHaveBeenCalled();
expect(utils.handleSetDefaultDatasourceAfterDeletion).not.toHaveBeenCalled();
// @ts-ignore
expect(component.find(confirmModalIdentifier).exists()).toBe(false);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ 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';

Check failure on line 32 in src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

Replace `·deleteMultipleDataSources,·getDataSources,·handleSetDefaultDatasourceAfterDeletion·` with `⏎··deleteMultipleDataSources,⏎··getDataSources,⏎··handleSetDefaultDatasourceAfterDeletion,⏎`
import { LoadingMask } from '../loading_mask';

/* Table config */
Expand Down Expand Up @@ -228,6 +228,9 @@ export const DataSourceTable = ({ history }: RouteComponentProps) => {

deleteMultipleDataSources(savedObjects.client, selectedDataSources)
.then(() => {
//Check if default data source is deleted or not.

Check failure on line 231 in src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

Expected exception block, space or tab after '//' in comment

Check failure on line 231 in src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

Delete `·`
//if yes, then set the first existing datasource as default datasource.

Check failure on line 232 in src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

Expected exception block, space or tab after '//' in comment
setDefaultDataSource();
setSelectedDataSources([]);
// Fetch data sources
fetchDataSources();
Expand All @@ -245,6 +248,22 @@ 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) {
setIsLoading(false);
handleDisplayToastMessage({

Check warning on line 260 in src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/data_source_management/public/components/data_source_table/data_source_table.tsx#L259-L260

Added lines #L259 - L260 were not covered by tests
id: 'dataSourcesManagement.editDataSource.setDefaultDataSourceFailMsg',
defaultMessage: 'Unable to set the Data Source to default. Please try it again.',
});
}
};

/* Table selection handlers */
const onSelectionChange = (selected: DataSourceTableItem[]) => {
setSelectedDataSources(selected);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Check failure on line 142 in src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

Replace `"test1"` with `'test1'`
await act(async () => {
// @ts-ignore
await component.find(formIdentifier).first().prop('onDeleteDataSource')(
Expand All @@ -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");

Check failure on line 156 in src/plugins/data_source_management/public/components/edit_data_source/edit_data_source.test.tsx

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

Replace `"test1"` with `'test1'`
await act(async () => {
// @ts-ignore
await component.find(formIdentifier).first().prop('onDeleteDataSource')(
Expand All @@ -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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -110,6 +111,12 @@ export const EditDataSource: React.FunctionComponent<RouteComponentProps<{ id: s
try {
await deleteDataSourceById(props.match.params.id, savedObjects.client);
props.history.push('');

// If deleted datasource is the default datasource, then set the first existing
// datasource as default datasource.
if (uiSettings.get('defaultDataSource') === dataSourceID) {
await handleSetDefaultDatasourceAfterDeletion(savedObjects.client, uiSettings);
}
} catch (e) {
setIsLoading(false);
handleDisplayToastMessage({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
isValidUrl,
testConnection,
updateDataSourceById,
handleSetDefaultDatasourceAfterDeletion,
handleSetDefaultDatasourceDuringCreation,
} from './utils';
import { coreMock } from '../../../../core/public/mocks';
import {
Expand All @@ -24,6 +26,7 @@ import {
mockDataSourceAttributesWithAuth,
mockErrorResponseForSavedObjectsCalls,
mockResponseForSavedObjectsCalls,
mockUiSettingsCalls,
} from '../mocks';
import {
AuthType,
Expand All @@ -36,6 +39,7 @@ import { AuthenticationMethod, AuthenticationMethodRegistery } from '../auth_reg
import { deepEqual } from 'assert';

const { savedObjects } = coreMock.createStart();
const { uiSettings } = coreMock.createStart();

describe('DataSourceManagement: Utils.ts', () => {
describe('Get data source', () => {
Expand Down Expand Up @@ -274,7 +278,22 @@ describe('DataSourceManagement: Utils.ts', () => {
expect(getDefaultAuthMethod(authenticationMethodRegistery)?.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');

Check failure on line 285 in src/plugins/data_source_management/public/components/utils.test.ts

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

Delete `··`
await handleSetDefaultDatasourceAfterDeletion(savedObjects.client, uiSettings);
expect(uiSettings.set).toHaveBeenCalled();
});
});
describe('handleSetDefaultDatasourceDuringCreation', () => {
test('should set defaultDataSource if only one data source exists', async () => {
mockResponseForSavedObjectsCalls(savedObjects.client, 'find', getDataSourcesResponse.savedObjects.slice(0, 1));

Check failure on line 292 in src/plugins/data_source_management/public/components/utils.test.ts

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

Replace `savedObjects.client,·'find',·getDataSourcesResponse.savedObjects.slice(0,·1)` with `⏎········savedObjects.client,⏎········'find',⏎········getDataSourcesResponse.savedObjects.slice(0,·1)⏎······`
await handleSetDefaultDatasourceDuringCreation(savedObjects.client, uiSettings);
expect(uiSettings.set).toHaveBeenCalled();
});
});
describe('Check extractRegisteredAuthTypeCredentials method', () => {
test('Should extract credential field successfully', () => {
const authTypeToBeTested = 'Some Auth Type';
Expand Down
25 changes: 24 additions & 1 deletion src/plugins/data_source_management/public/components/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { HttpStart, SavedObjectsClientContract, SavedObject } from 'src/core/public';
import { HttpStart, SavedObjectsClientContract, SavedObject, IUiSettingsClient } from 'src/core/public';

Check failure on line 6 in src/plugins/data_source_management/public/components/utils.ts

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

Replace `·HttpStart,·SavedObjectsClientContract,·SavedObject,·IUiSettingsClient·` with `⏎··HttpStart,⏎··SavedObjectsClientContract,⏎··SavedObject,⏎··IUiSettingsClient,⏎`
import {
DataSourceAttributes,
DataSourceTableItem,
Expand Down Expand Up @@ -49,6 +49,29 @@ export async function getDataSourcesWithFields(
return response?.savedObjects;
}

export async function handleSetDefaultDatasourceAfterDeletion(
savedObjects: SavedObjectsClientContract,
uiSettings: IUiSettingsClient,
) {
uiSettings.remove('defaultDataSource');
const listOfDataSources: DataSourceTableItem[] = await getDataSources(savedObjects);
if ( Array.isArray(listOfDataSources) && listOfDataSources.length >= 1) {
const datasourceId = listOfDataSources[0].id;
await uiSettings.set('defaultDataSource', datasourceId);
}
}

export async function handleSetDefaultDatasourceDuringCreation (
savedObjects: SavedObjectsClientContract,
uiSettings: IUiSettingsClient,
){
const listOfDataSources: DataSourceTableItem[] = await getDataSources(savedObjects);
if( Array.isArray(listOfDataSources) && listOfDataSources.length == 1) {
const datasourceId = listOfDataSources[0].id;
await uiSettings.set('defaultDataSource', datasourceId);

Check warning on line 71 in src/plugins/data_source_management/public/components/utils.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/data_source_management/public/components/utils.ts#L70-L71

Added lines #L70 - L71 were not covered by tests
}
}

export async function getDataSourceById(
id: string,
savedObjectsClient: SavedObjectsClientContract
Expand Down
9 changes: 9 additions & 0 deletions src/plugins/data_source_management/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -263,6 +264,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;
Expand Down

0 comments on commit 9822ef0

Please sign in to comment.