Skip to content

Commit

Permalink
Add set default datasource (#6186)
Browse files Browse the repository at this point in the history
* Add set default datasource

Signed-off-by: Yuanqi(Ella) Zhu <[email protected]>

* Fix typo

Signed-off-by: Yuanqi(Ella) Zhu <[email protected]>

* change on this.props.isDefault

Signed-off-by: Yuanqi(Ella) Zhu <[email protected]>

* add unit test

Signed-off-by: Yuanqi(Ella) Zhu <[email protected]>

* set data_source to false

Signed-off-by: Yuanqi(Ella) Zhu <[email protected]>

* add more unit test

Signed-off-by: Yuanqi(Ella) Zhu <[email protected]>

* fix lint error

Signed-off-by: Yuanqi(Ella) Zhu <[email protected]>

* edit one more unit test

Signed-off-by: Yuanqi(Ella) Zhu <[email protected]>

* Fix another typo

Signed-off-by: Yuanqi(Ella) Zhu <[email protected]>

---------

Signed-off-by: Yuanqi(Ella) Zhu <[email protected]>
Co-authored-by: ZilongX <[email protected]>
  • Loading branch information
zhyuanqi and ZilongX authored Mar 19, 2024
1 parent daccae7 commit d2347ca
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ describe('Datasource Management: Edit Datasource Form', () => {
<EditDataSourceForm
existingDataSource={mockDataSourceAttributesWithAuth}
existingDatasourceNamesList={existingDatasourceNamesList}
isDefault={false}
onDeleteDataSource={mockFn}
handleSubmit={mockFn}
onSetDefaultDataSource={mockFn}
handleTestConnection={mockFn}
displayToastMessage={mockFn}
/>
Expand Down Expand Up @@ -245,7 +247,9 @@ describe('Datasource Management: Edit Datasource Form', () => {
<EditDataSourceForm
existingDataSource={mockDataSourceAttributesWithNoAuth}
existingDatasourceNamesList={existingDatasourceNamesList}
isDefault={false}
onDeleteDataSource={mockFn}
onSetDefaultDataSource={mockFn}
handleSubmit={mockFn}
handleTestConnection={mockFn}
displayToastMessage={mockFn}
Expand Down Expand Up @@ -301,6 +305,12 @@ 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) =>
Expand Down Expand Up @@ -383,8 +393,10 @@ describe('With Registered Authentication', () => {
<EditDataSourceForm
existingDataSource={mockDataSourceAttributesWithNoAuth}
existingDatasourceNamesList={existingDatasourceNamesList}
isDefault={false}
onDeleteDataSource={jest.fn()}
handleSubmit={jest.fn()}
onSetDefaultDataSource={jest.fn()}
handleTestConnection={jest.fn()}
displayToastMessage={jest.fn()}
/>
Expand Down Expand Up @@ -422,8 +434,10 @@ describe('With Registered Authentication', () => {
<EditDataSourceForm
existingDataSource={mockDataSourceAttributesWithRegisteredAuth}
existingDatasourceNamesList={existingDatasourceNamesList}
isDefault={false}
onDeleteDataSource={jest.fn()}
handleSubmit={mockedSubmitHandler}
onSetDefaultDataSource={jest.fn()}
handleTestConnection={jest.fn()}
displayToastMessage={jest.fn()}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ import { extractRegisteredAuthTypeCredentials, getDefaultAuthMethod } from '../.
export interface EditDataSourceProps {
existingDataSource: DataSourceAttributes;
existingDatasourceNamesList: string[];
isDefault: boolean;
handleSubmit: (formValues: DataSourceAttributes) => Promise<void>;
handleTestConnection: (formValues: DataSourceAttributes) => Promise<void>;
onDeleteDataSource?: () => Promise<void>;
onSetDefaultDataSource: () => Promise<void>;
displayToastMessage: (info: ToastMessageItem) => void;
}
export interface EditDataSourceState {
Expand Down Expand Up @@ -400,6 +402,12 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi
}
};

setDefaultDataSource = async () => {
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);
Expand Down Expand Up @@ -634,6 +642,8 @@ export class EditDataSourceForm extends React.Component<EditDataSourceProps, Edi
onClickDeleteIcon={this.onClickDeleteDataSource}
dataSourceName={this.props.existingDataSource.title}
onClickTestConnection={this.onClickTestConnection}
onClickSetDefault={this.setDefaultDataSource}
isDefault={this.props.isDefault}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -31,6 +32,8 @@ describe('Datasource Management: Edit Datasource Header', () => {
onClickDeleteIcon={mockFn}
onClickTestConnection={mockFn}
dataSourceName={dataSourceName}
onClickSetDefault={mockFn}
isDefault={false}
/>
),
{
Expand Down Expand Up @@ -82,6 +85,8 @@ describe('Datasource Management: Edit Datasource Header', () => {
onClickDeleteIcon={mockFn}
onClickTestConnection={mockFn}
dataSourceName={dataSourceName}
onClickSetDefault={mockFn}
isDefault={false}
/>
),
{
Expand All @@ -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(
<Header
isFormValid={true}
showDeleteIcon={true}
onClickDeleteIcon={mockFn}
onClickTestConnection={mockFn}
dataSourceName={dataSourceName}
onClickSetDefault={onClickSetDefault}
isDefault={isDefaultDataSourceState}
/>
),
{
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(
<Header
isFormValid={true}
showDeleteIcon={true}
onClickDeleteIcon={mockFn}
onClickTestConnection={mockFn}
dataSourceName={dataSourceName}
onClickSetDefault={onClickSetDefault}
isDefault={isDefaultDataSourceState}
/>
),
{
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'
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
EuiButtonIcon,
EuiConfirmModal,
EuiButton,
EuiButtonEmpty,
} from '@elastic/eui';
import { i18n } from '@osd/i18n';
import { FormattedMessage } from '@osd/i18n/react';
Expand All @@ -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<DataSourceManagementContext>().services.chrome
.docTitle.change;

changeTitle(dataSourceName);

const setDefaultAriaLabel = i18n.translate(
'dataSourcesManagement.editDataSource.setDefaultDataSource',
{
defaultMessage: 'Set as a default Data Source.',
}
);

const renderDefaultIcon = () => {
return (
<EuiButtonEmpty
onClick={() => {
onClickSetDefault();
setIsDefaultDataSourceState(!isDefaultDataSourceState);
}}
disabled={isDefaultDataSourceState}
iconType={isDefaultDataSourceState ? 'starFilled' : 'starEmpty'}
aria-label={setDefaultAriaLabel}
data-test-subj="editSetDefaultDataSource"
>
{isDefaultDataSourceState ? 'Default' : 'Set as default'}
</EuiButtonEmpty>
);
};

const renderDeleteButton = () => {
return (
<>
Expand Down Expand Up @@ -144,6 +174,8 @@ export const Header = ({
{/* Right side buttons */}
<EuiFlexItem grow={false}>
<EuiFlexGroup alignItems="baseline" gutterSize="m" responsive={false}>
{/* Test default button */}
<EuiFlexItem grow={false}>{renderDefaultIcon()}</EuiFlexItem>
{/* Test connection button */}
<EuiFlexItem grow={false}>{renderTestConnectionButton()}</EuiFlexItem>
{/* Delete icon button */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
Expand Down Expand Up @@ -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({});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const EditDataSource: React.FunctionComponent<RouteComponentProps<{ id: s
) => {
/* Initialization */
const {
uiSettings,
savedObjects,
setBreadcrumbs,
http,
Expand Down Expand Up @@ -83,6 +84,12 @@ export const EditDataSource: React.FunctionComponent<RouteComponentProps<{ id: s
}
};

const handleSetDefault = async () => {
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);
Expand Down Expand Up @@ -128,7 +135,9 @@ export const EditDataSource: React.FunctionComponent<RouteComponentProps<{ id: s
<EditDataSourceForm
existingDataSource={dataSource}
existingDatasourceNamesList={existingDatasourceNamesList}
isDefault={isDefaultDataSource}
onDeleteDataSource={handleDelete}
onSetDefaultDataSource={handleSetDefault}
handleSubmit={handleSubmit}
displayToastMessage={handleDisplayToastMessage}
handleTestConnection={handleTestConnection}
Expand Down

0 comments on commit d2347ca

Please sign in to comment.