diff --git a/CHANGELOG.md b/CHANGELOG.md
index 66d8455859aa..e17bb7d1a207 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -55,6 +55,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [Chrome] Introduce registerCollapsibleNavHeader to allow plugins to customize the rendering of nav menu header ([#5244](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5244))
- [Dynamic Configurations] Pass request headers when making application config calls ([#6164](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6164))
- [Discover] Options button to configure legacy mode and remove the top navigation option ([#6170](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6170))
+- [Multiple Datasource] Add default functionality for customer to choose default datasource ([#6058](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/6058))
### 🐛 Bug Fixes
diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx
index 06d0af486bba..e5f73c503f6e 100644
--- a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx
+++ b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx
@@ -76,8 +76,10 @@ describe('Datasource Management: Edit Datasource Form', () => {
@@ -245,7 +247,9 @@ describe('Datasource Management: Edit Datasource Form', () => {
{
expect(mockFn).toHaveBeenCalled();
});
+ test('should set as the default datasource from header', () => {
+ // @ts-ignore
+ component.find('Header').prop('onClickSetDefault')();
+ expect(mockFn).toHaveBeenCalled();
+ });
+
/* Save Changes */
test('should update the form with NoAuth on click save changes', async () => {
await new Promise((resolve) =>
@@ -383,8 +393,10 @@ describe('With Registered Authentication', () => {
@@ -422,8 +434,10 @@ describe('With Registered Authentication', () => {
diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx
index 2cb1db4515dd..6714881c4a11 100644
--- a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx
+++ b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx
@@ -50,9 +50,11 @@ import { extractRegisteredAuthTypeCredentials, getDefaultAuthMethod } from '../.
export interface EditDataSourceProps {
existingDataSource: DataSourceAttributes;
existingDatasourceNamesList: string[];
+ isDefault: boolean;
handleSubmit: (formValues: DataSourceAttributes) => Promise;
handleTestConnection: (formValues: DataSourceAttributes) => Promise;
onDeleteDataSource?: () => Promise;
+ onSetDefaultDataSource: () => Promise;
displayToastMessage: (info: ToastMessageItem) => void;
}
export interface EditDataSourceState {
@@ -400,6 +402,12 @@ export class EditDataSourceForm extends React.Component {
+ if (this.props.onSetDefaultDataSource) {
+ await this.props.onSetDefaultDataSource();
+ }
+ };
+
onClickTestConnection = async () => {
this.setState({ isLoading: true });
const isNewCredential = !!(this.state.auth.type !== this.props.existingDataSource.auth.type);
@@ -634,6 +642,8 @@ export class EditDataSourceForm extends React.Component
);
};
diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.test.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.test.tsx
index f679a7db6e67..5a23b72881a1 100644
--- a/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.test.tsx
+++ b/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.test.tsx
@@ -14,6 +14,7 @@ import { act } from 'react-dom/test-utils';
const headerTitleIdentifier = '[data-test-subj="editDataSourceTitle"]';
const deleteIconIdentifier = '[data-test-subj="editDatasourceDeleteIcon"]';
const confirmModalIdentifier = '[data-test-subj="editDatasourceDeleteConfirmModal"]';
+const setDefaultButtonIdentifier = '[data-test-subj="editSetDefaultDataSource"]';
describe('Datasource Management: Edit Datasource Header', () => {
const mockedContext = mockManagementPlugin.createDataSourceManagementContext();
@@ -31,6 +32,8 @@ describe('Datasource Management: Edit Datasource Header', () => {
onClickDeleteIcon={mockFn}
onClickTestConnection={mockFn}
dataSourceName={dataSourceName}
+ onClickSetDefault={mockFn}
+ isDefault={false}
/>
),
{
@@ -82,6 +85,8 @@ describe('Datasource Management: Edit Datasource Header', () => {
onClickDeleteIcon={mockFn}
onClickTestConnection={mockFn}
dataSourceName={dataSourceName}
+ onClickSetDefault={mockFn}
+ isDefault={false}
/>
),
{
@@ -97,4 +102,76 @@ describe('Datasource Management: Edit Datasource Header', () => {
expect(component.find(deleteIconIdentifier).exists()).toBe(false);
});
});
+ describe('should render default icon as "Set as default" when isDefaultDataSourceState is false', () => {
+ const onClickSetDefault = jest.fn();
+ const isDefaultDataSourceState = false;
+ beforeEach(() => {
+ component = mount(
+ wrapWithIntl(
+
+ ),
+ {
+ wrappingComponent: OpenSearchDashboardsContextProvider,
+ wrappingComponentProps: {
+ services: mockedContext,
+ },
+ }
+ );
+ });
+
+ test('should render normally', () => {
+ expect(component.find(setDefaultButtonIdentifier).exists()).toBe(true);
+ });
+ test('default button should show as "Set as default" and should be clickable', () => {
+ expect(component.find(setDefaultButtonIdentifier).first().text()).toBe('Set as default');
+ expect(component.find(setDefaultButtonIdentifier).first().prop('disabled')).toBe(false);
+ expect(component.find(setDefaultButtonIdentifier).first().prop('iconType')).toBe('starEmpty');
+ component.find(setDefaultButtonIdentifier).first().simulate('click');
+ expect(onClickSetDefault).toHaveBeenCalled();
+ });
+ });
+ describe('should render default icon as "Default" when isDefaultDataSourceState is true', () => {
+ const onClickSetDefault = jest.fn();
+ const isDefaultDataSourceState = true;
+ beforeEach(() => {
+ component = mount(
+ wrapWithIntl(
+
+ ),
+ {
+ wrappingComponent: OpenSearchDashboardsContextProvider,
+ wrappingComponentProps: {
+ services: mockedContext,
+ },
+ }
+ );
+ });
+
+ test('should render normally', () => {
+ expect(component.find(setDefaultButtonIdentifier).exists()).toBe(true);
+ });
+ test('default button should show as "Default" and should be disabled.', () => {
+ expect(component.find(setDefaultButtonIdentifier).first().text()).toBe('Default');
+ expect(component.find(setDefaultButtonIdentifier).first().prop('disabled')).toBe(true);
+ expect(component.find(setDefaultButtonIdentifier).first().prop('iconType')).toBe(
+ 'starFilled'
+ );
+ });
+ });
});
diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.tsx
index 49c100b7ec9a..264647882574 100644
--- a/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.tsx
+++ b/src/plugins/data_source_management/public/components/edit_data_source/components/header/header.tsx
@@ -14,6 +14,7 @@ import {
EuiButtonIcon,
EuiConfirmModal,
EuiButton,
+ EuiButtonEmpty,
} from '@elastic/eui';
import { i18n } from '@osd/i18n';
import { FormattedMessage } from '@osd/i18n/react';
@@ -25,22 +26,51 @@ export const Header = ({
isFormValid,
onClickDeleteIcon,
onClickTestConnection,
+ onClickSetDefault,
dataSourceName,
+ isDefault,
}: {
showDeleteIcon: boolean;
isFormValid: boolean;
onClickDeleteIcon: () => void;
onClickTestConnection: () => void;
+ onClickSetDefault: () => void;
dataSourceName: string;
+ isDefault: boolean;
}) => {
/* State Variables */
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
+ const [isDefaultDataSourceState, setIsDefaultDataSourceState] = useState(isDefault);
const changeTitle = useOpenSearchDashboards().services.chrome
.docTitle.change;
changeTitle(dataSourceName);
+ const setDefaultAriaLabel = i18n.translate(
+ 'dataSourcesManagement.editDataSource.setDefaultDataSource',
+ {
+ defaultMessage: 'Set as a default Data Source.',
+ }
+ );
+
+ const renderDefaultIcon = () => {
+ return (
+ {
+ onClickSetDefault();
+ setIsDefaultDataSourceState(!isDefaultDataSourceState);
+ }}
+ disabled={isDefaultDataSourceState}
+ iconType={isDefaultDataSourceState ? 'starFilled' : 'starEmpty'}
+ aria-label={setDefaultAriaLabel}
+ data-test-subj="editSetDefaultDataSource"
+ >
+ {isDefaultDataSourceState ? 'Default' : 'Set as default'}
+
+ );
+ };
+
const renderDeleteButton = () => {
return (
<>
@@ -144,6 +174,8 @@ export const Header = ({
{/* Right side buttons */}
+ {/* Test default button */}
+ {renderDefaultIcon()}
{/* Test connection button */}
{renderTestConnectionButton()}
{/* Delete icon button */}
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 c1516a507d4a..6cab62b49c8c 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
@@ -29,6 +29,7 @@ const notFoundIdentifier = '[data-test-subj="dataSourceNotFound"]';
describe('Datasource Management: Edit Datasource Wizard', () => {
const mockedContext = mockManagementPlugin.createDataSourceManagementContext();
+ const uiSettings = mockedContext.uiSettings;
mockedContext.authenticationMethodRegistery.registerAuthenticationMethod(
noAuthCredentialAuthMethod
);
@@ -125,6 +126,16 @@ describe('Datasource Management: Edit Datasource Wizard', () => {
component.update();
expect(utils.updateDataSourceById).toHaveBeenCalled();
});
+ test('should set default data source', async () => {
+ spyOn(uiSettings, 'set').and.returnValue({});
+ await act(async () => {
+ // @ts-ignore
+ await component.find(formIdentifier).first().prop('onSetDefaultDataSource')(
+ mockDataSourceAttributesWithAuth
+ );
+ });
+ expect(uiSettings.set).toHaveBeenCalled();
+ });
test('should delete datasource successfully', async () => {
spyOn(utils, 'deleteDataSourceById').and.returnValue({});
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 bc2bac5b66b8..46e253b2b85b 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
@@ -38,6 +38,7 @@ export const EditDataSource: React.FunctionComponent {
/* Initialization */
const {
+ uiSettings,
savedObjects,
setBreadcrumbs,
http,
@@ -83,6 +84,12 @@ export const EditDataSource: React.FunctionComponent {
+ await uiSettings.set('defaultDataSource', dataSourceID);
+ };
+
+ const isDefaultDataSource = uiSettings.get('defaultDataSource', null) === dataSourceID;
+
/* Handle submit - create data source*/
const handleSubmit = async (attributes: DataSourceAttributes) => {
await updateDataSourceById(savedObjects.client, dataSourceID, attributes);
@@ -128,7 +135,9 @@ export const EditDataSource: React.FunctionComponent