diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f5d5ee061f..5f9e5e4d2ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multiple Datasource] Test connection schema validation for registered auth types ([#6109](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6109)) - [Workspace] Consume workspace id in saved object client ([#6014](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6014)) - [Multiple Datasource] Export DataSourcePluginRequestContext at top level for plugins to use ([#6108](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6108)) +- [Multiple Datasource] Expose filterfn in datasource menu component to allow filter data sources before rendering in navigation bar ([#6113](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6113)) - [Workspace] Add delete saved objects by workspace functionality([#6013](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6013)) - [Workspace] Add a workspace client in workspace plugin ([#6094](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6094)) diff --git a/src/plugins/data_source_management/public/components/data_source_menu/__snapshots__/data_source_selectable.test.tsx.snap b/src/plugins/data_source_management/public/components/data_source_menu/__snapshots__/data_source_selectable.test.tsx.snap index ff494ad932e..944750bde43 100644 --- a/src/plugins/data_source_management/public/components/data_source_menu/__snapshots__/data_source_selectable.test.tsx.snap +++ b/src/plugins/data_source_management/public/components/data_source_menu/__snapshots__/data_source_selectable.test.tsx.snap @@ -1,5 +1,81 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`DataSourceSelectable should filter options if configured 1`] = ` + + + + Local cluster + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="dataSourceSelectableContextMenuPopover" + isOpen={false} + ownFocus={true} + panelPaddingSize="none" +> + + + + + + + + + +`; + exports[`DataSourceSelectable should render normally with local cluster is hidden 1`] = ` { const component = render(); expect(component).toMatchSnapshot(); expect(client.find).toBeCalledWith({ - fields: ['id', 'description', 'title'], + fields: ['id', 'title', 'auth.type'], perPage: 10000, type: 'data-source', }); diff --git a/src/plugins/data_source_management/public/components/data_source_menu/data_source_menu.tsx b/src/plugins/data_source_management/public/components/data_source_menu/data_source_menu.tsx index 57e3590f5bc..54c7642207d 100644 --- a/src/plugins/data_source_management/public/components/data_source_menu/data_source_menu.tsx +++ b/src/plugins/data_source_management/public/components/data_source_menu/data_source_menu.tsx @@ -28,6 +28,7 @@ export interface DataSourceMenuProps { className?: string; selectedOption?: DataSourceOption[]; setMenuMountPoint?: (menuMount: MountPoint | undefined) => void; + filterFn?: (dataSource: any) => boolean; } export function DataSourceMenu(props: DataSourceMenuProps): ReactElement | null { @@ -40,6 +41,7 @@ export function DataSourceMenu(props: DataSourceMenuProps): ReactElement | null fullWidth, hideLocalCluster, selectedOption, + filterFn, } = props; if (!showDataSourceSelectable) { @@ -66,6 +68,7 @@ export function DataSourceMenu(props: DataSourceMenuProps): ReactElement | null onSelectedDataSource={dataSourceCallBackFunc} disabled={disableDataSourceSelectable || false} selectedOption={selectedOption && selectedOption.length > 0 ? selectedOption : undefined} + filterFn={filterFn} /> ); } diff --git a/src/plugins/data_source_management/public/components/data_source_menu/data_source_selectable.test.tsx b/src/plugins/data_source_management/public/components/data_source_menu/data_source_selectable.test.tsx index 192aea1d642..7f3a182cf23 100644 --- a/src/plugins/data_source_management/public/components/data_source_menu/data_source_selectable.test.tsx +++ b/src/plugins/data_source_management/public/components/data_source_menu/data_source_selectable.test.tsx @@ -8,17 +8,21 @@ import { SavedObjectsClientContract } from '../../../../../core/public'; import { notificationServiceMock } from '../../../../../core/public/mocks'; import React from 'react'; import { DataSourceSelectable } from './data_source_selectable'; +import { AuthType } from '../../types'; +import { getDataSourcesWithFieldsResponse, mockResponseForSavedObjectsCalls } from '../../mocks'; describe('DataSourceSelectable', () => { let component: ShallowWrapper, React.Component<{}, {}, any>>; let client: SavedObjectsClientContract; const { toasts } = notificationServiceMock.createStartContract(); + const nextTick = () => new Promise((res) => process.nextTick(res)); beforeEach(() => { client = { find: jest.fn().mockResolvedValue([]), } as any; + mockResponseForSavedObjectsCalls(client, 'find', getDataSourcesWithFieldsResponse); }); it('should render normally with local cluster not hidden', () => { @@ -34,7 +38,7 @@ describe('DataSourceSelectable', () => { ); expect(component).toMatchSnapshot(); expect(client.find).toBeCalledWith({ - fields: ['id', 'description', 'title'], + fields: ['id', 'title', 'auth.type'], perPage: 10000, type: 'data-source', }); @@ -54,10 +58,28 @@ describe('DataSourceSelectable', () => { ); expect(component).toMatchSnapshot(); expect(client.find).toBeCalledWith({ - fields: ['id', 'description', 'title'], + fields: ['id', 'title', 'auth.type'], perPage: 10000, type: 'data-source', }); expect(toasts.addWarning).toBeCalledTimes(0); }); + + it('should filter options if configured', async () => { + component = shallow( + ds.attributes.auth.type !== AuthType.NoAuth} + /> + ); + component.instance().componentDidMount!(); + await nextTick(); + expect(component).toMatchSnapshot(); + expect(toasts.addWarning).toBeCalledTimes(0); + }); }); diff --git a/src/plugins/data_source_management/public/components/data_source_menu/data_source_selectable.tsx b/src/plugins/data_source_management/public/components/data_source_menu/data_source_selectable.tsx index 1c8c6bd2921..2aab73b0dd7 100644 --- a/src/plugins/data_source_management/public/components/data_source_menu/data_source_selectable.tsx +++ b/src/plugins/data_source_management/public/components/data_source_menu/data_source_selectable.tsx @@ -15,7 +15,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { SavedObjectsClientContract, ToastsStart } from 'opensearch-dashboards/public'; -import { getDataSources } from '../utils'; +import { getDataSourcesWithFields } from '../utils'; import { DataSourceOption, LocalCluster } from '../data_source_selector/data_source_selector'; interface DataSourceSelectableProps { @@ -26,6 +26,7 @@ interface DataSourceSelectableProps { hideLocalCluster: boolean; fullWidth: boolean; selectedOption?: DataSourceOption[]; + filterFn?: (dataSource: any) => boolean; } interface DataSourceSelectableState { @@ -69,18 +70,24 @@ export class DataSourceSelectable extends React.Component< async componentDidMount() { this._isMounted = true; - getDataSources(this.props.savedObjectsClient) + getDataSourcesWithFields(this.props.savedObjectsClient, ['id', 'title', 'auth.type']) .then((fetchedDataSources) => { if (fetchedDataSources?.length) { - let dataSourceOptions = fetchedDataSources.map((dataSource) => ({ - id: dataSource.id, - label: dataSource.title, - })); + let filteredDataSources = []; + if (this.props.filterFn) { + filteredDataSources = fetchedDataSources.filter((ds) => this.props.filterFn!(ds)); + } - dataSourceOptions = dataSourceOptions.sort((a, b) => - a.label.toLowerCase().localeCompare(b.label.toLowerCase()) - ); + if (filteredDataSources.length === 0) { + filteredDataSources = fetchedDataSources; + } + const dataSourceOptions = filteredDataSources + .map((dataSource) => ({ + id: dataSource.id, + label: dataSource.attributes?.title || '', + })) + .sort((a, b) => a.label.toLowerCase().localeCompare(b.label.toLowerCase())); if (!this.props.hideLocalCluster) { dataSourceOptions.unshift(LocalCluster); }