Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add set default datasource #6186

Merged
merged 10 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add tests for this component?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added!

existingDataSource={dataSource}
existingDatasourceNamesList={existingDatasourceNamesList}
isDefault={isDefaultDataSource}
onDeleteDataSource={handleDelete}
onSetDefaultDataSource={handleSetDefault}
handleSubmit={handleSubmit}
displayToastMessage={handleDisplayToastMessage}
handleTestConnection={handleTestConnection}
Expand Down
Loading