-
Notifications
You must be signed in to change notification settings - Fork 870
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Multiple Datasource] Add data source aggregated view to show all com…
…patible data sources or only show used data sources (#6129) (#6264) * add data source aggregated view to show all compatible data sources or only used data sources Signed-off-by: Lu Yu <nluyu@amazon.com> * add change log Signed-off-by: Lu Yu <nluyu@amazon.com> * address comments and add more tests Signed-off-by: Lu Yu <nluyu@amazon.com> --------- Signed-off-by: Lu Yu <nluyu@amazon.com> (cherry picked from commit 05abf5e) Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> # Conflicts: # CHANGELOG.md Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
- Loading branch information
1 parent
1f42a6c
commit 2971dec
Showing
9 changed files
with
864 additions
and
9 deletions.
There are no files selected for viewing
449 changes: 449 additions & 0 deletions
449
...nents/data_source_aggregated_view/__snapshots__/data_source_aggregated_view.test.tsx.snap
Large diffs are not rendered by default.
Oops, something went wrong.
119 changes: 119 additions & 0 deletions
119
...gement/public/components/data_source_aggregated_view/data_source_aggregated_view.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { ShallowWrapper, shallow } from 'enzyme'; | ||
import React from 'react'; | ||
import { DataSourceAggregatedView } from './data_source_aggregated_view'; | ||
import { SavedObjectsClientContract } from '../../../../../core/public'; | ||
import { notificationServiceMock } from '../../../../../core/public/mocks'; | ||
import { getDataSourcesWithFieldsResponse, mockResponseForSavedObjectsCalls } from '../../mocks'; | ||
import { render } from '@testing-library/react'; | ||
|
||
describe('DataSourceAggregatedView', () => { | ||
let component: ShallowWrapper<any, Readonly<{}>, React.Component<{}, {}, any>>; | ||
let client: SavedObjectsClientContract; | ||
const { toasts } = notificationServiceMock.createStartContract(); | ||
|
||
beforeEach(() => { | ||
client = { | ||
find: jest.fn().mockResolvedValue([]), | ||
} as any; | ||
mockResponseForSavedObjectsCalls(client, 'find', getDataSourcesWithFieldsResponse); | ||
}); | ||
|
||
it('should render normally with local cluster not hidden and all options', () => { | ||
component = shallow( | ||
<DataSourceAggregatedView | ||
fullWidth={false} | ||
hideLocalCluster={false} | ||
savedObjectsClient={client} | ||
notifications={toasts} | ||
displayAllCompatibleDataSources={true} | ||
/> | ||
); | ||
expect(component).toMatchSnapshot(); | ||
expect(client.find).toBeCalledWith({ | ||
fields: ['id', 'title', 'auth.type'], | ||
perPage: 10000, | ||
type: 'data-source', | ||
}); | ||
expect(toasts.addWarning).toBeCalledTimes(0); | ||
}); | ||
|
||
it('should render normally with local cluster hidden and all options', () => { | ||
component = shallow( | ||
<DataSourceAggregatedView | ||
fullWidth={false} | ||
hideLocalCluster={true} | ||
savedObjectsClient={client} | ||
notifications={toasts} | ||
displayAllCompatibleDataSources={true} | ||
/> | ||
); | ||
expect(component).toMatchSnapshot(); | ||
expect(client.find).toBeCalledWith({ | ||
fields: ['id', 'title', 'auth.type'], | ||
perPage: 10000, | ||
type: 'data-source', | ||
}); | ||
expect(toasts.addWarning).toBeCalledTimes(0); | ||
}); | ||
|
||
it('should render normally with local cluster and actice selections', () => { | ||
component = shallow( | ||
<DataSourceAggregatedView | ||
fullWidth={false} | ||
hideLocalCluster={false} | ||
savedObjectsClient={client} | ||
notifications={toasts} | ||
displayAllCompatibleDataSources={false} | ||
activeDataSourceIds={['test1']} | ||
/> | ||
); | ||
expect(component).toMatchSnapshot(); | ||
expect(client.find).toBeCalledWith({ | ||
fields: ['id', 'title', 'auth.type'], | ||
perPage: 10000, | ||
type: 'data-source', | ||
}); | ||
expect(toasts.addWarning).toBeCalledTimes(0); | ||
}); | ||
|
||
it('should render normally with data source filter', () => { | ||
component = shallow( | ||
<DataSourceAggregatedView | ||
fullWidth={false} | ||
hideLocalCluster={false} | ||
savedObjectsClient={client} | ||
notifications={toasts} | ||
displayAllCompatibleDataSources={false} | ||
dataSourceFilter={(ds) => ds.attributes.auth.type !== 'no_auth'} | ||
/> | ||
); | ||
expect(component).toMatchSnapshot(); | ||
expect(client.find).toBeCalledWith({ | ||
fields: ['id', 'title', 'auth.type'], | ||
perPage: 10000, | ||
type: 'data-source', | ||
}); | ||
expect(toasts.addWarning).toBeCalledTimes(0); | ||
}); | ||
|
||
it('should render popup when clicking on info icon', async () => { | ||
const container = render( | ||
<DataSourceAggregatedView | ||
fullWidth={false} | ||
hideLocalCluster={false} | ||
savedObjectsClient={client} | ||
notifications={toasts} | ||
displayAllCompatibleDataSources={false} | ||
activeDataSourceIds={['test1']} | ||
/> | ||
); | ||
const infoIcon = await container.findByTestId('dataSourceAggregatedViewInfoButton'); | ||
infoIcon.click(); | ||
expect(container).toMatchSnapshot(); | ||
}); | ||
}); |
173 changes: 173 additions & 0 deletions
173
..._management/public/components/data_source_aggregated_view/data_source_aggregated_view.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import React from 'react'; | ||
import { | ||
EuiButtonEmpty, | ||
EuiButtonIcon, | ||
EuiContextMenu, | ||
EuiNotificationBadge, | ||
EuiPopover, | ||
} from '@elastic/eui'; | ||
import { i18n } from '@osd/i18n'; | ||
import { SavedObjectsClientContract, ToastsStart } from 'opensearch-dashboards/public'; | ||
import { getDataSourcesWithFields } from '../utils'; | ||
import { SavedObject } from '../../../../../core/public'; | ||
import { DataSourceAttributes } from '../../types'; | ||
|
||
interface DataSourceAggregatedViewProps { | ||
savedObjectsClient: SavedObjectsClientContract; | ||
notifications: ToastsStart; | ||
hideLocalCluster: boolean; | ||
fullWidth: boolean; | ||
activeDataSourceIds?: string[]; | ||
dataSourceFilter?: (dataSource: SavedObject<DataSourceAttributes>) => boolean; | ||
displayAllCompatibleDataSources: boolean; | ||
} | ||
|
||
interface DataSourceAggregatedViewState { | ||
isPopoverOpen: boolean; | ||
allDataSourcesIdToTitleMap: Map<string, any>; | ||
} | ||
|
||
export class DataSourceAggregatedView extends React.Component< | ||
DataSourceAggregatedViewProps, | ||
DataSourceAggregatedViewState | ||
> { | ||
private _isMounted: boolean = false; | ||
|
||
constructor(props: DataSourceAggregatedViewProps) { | ||
super(props); | ||
|
||
this.state = { | ||
isPopoverOpen: false, | ||
allDataSourcesIdToTitleMap: new Map(), | ||
}; | ||
} | ||
|
||
componentWillUnmount() { | ||
this._isMounted = false; | ||
} | ||
|
||
onClick() { | ||
this.setState({ ...this.state, isPopoverOpen: !this.state.isPopoverOpen }); | ||
} | ||
|
||
closePopover() { | ||
this.setState({ ...this.state, isPopoverOpen: false }); | ||
} | ||
|
||
async componentDidMount() { | ||
this._isMounted = true; | ||
getDataSourcesWithFields(this.props.savedObjectsClient, ['id', 'title', 'auth.type']) | ||
.then((fetchedDataSources) => { | ||
if (fetchedDataSources?.length) { | ||
let filteredDataSources = fetchedDataSources; | ||
if (this.props.dataSourceFilter) { | ||
filteredDataSources = fetchedDataSources.filter((ds) => | ||
this.props.dataSourceFilter!(ds) | ||
); | ||
} | ||
|
||
const allDataSourcesIdToTitleMap = new Map(); | ||
|
||
filteredDataSources.forEach((ds) => { | ||
allDataSourcesIdToTitleMap.set(ds.id, ds.attributes!.title || ''); | ||
}); | ||
|
||
if (!this.props.hideLocalCluster) { | ||
allDataSourcesIdToTitleMap.set('', 'Local cluster'); | ||
} | ||
|
||
if (!this._isMounted) return; | ||
this.setState({ | ||
...this.state, | ||
allDataSourcesIdToTitleMap, | ||
}); | ||
} | ||
}) | ||
.catch(() => { | ||
this.props.notifications.addWarning( | ||
i18n.translate('dataSource.fetchDataSourceError', { | ||
defaultMessage: 'Unable to fetch existing data sources', | ||
}) | ||
); | ||
}); | ||
} | ||
|
||
render() { | ||
const button = ( | ||
<EuiButtonIcon | ||
data-test-subj="dataSourceAggregatedViewInfoButton" | ||
iconType="iInCircle" | ||
display="empty" | ||
aria-label="show data sources" | ||
onClick={this.onClick.bind(this)} | ||
/> | ||
); | ||
|
||
let items = []; | ||
|
||
// only display active data sources | ||
if (this.props.activeDataSourceIds && this.props.activeDataSourceIds.length > 0) { | ||
items = this.props.activeDataSourceIds.map((id) => { | ||
return { | ||
name: this.state.allDataSourcesIdToTitleMap.get(id), | ||
disabled: true, | ||
}; | ||
}); | ||
} else { | ||
items = [...this.state.allDataSourcesIdToTitleMap.values()].map((title) => { | ||
return { | ||
name: title, | ||
disabled: true, | ||
}; | ||
}); | ||
} | ||
|
||
const title = this.props.displayAllCompatibleDataSources | ||
? `Data sources (${this.state.allDataSourcesIdToTitleMap.size})` | ||
: 'Selected data sources'; | ||
|
||
const panels = [ | ||
{ | ||
id: 0, | ||
title, | ||
items, | ||
}, | ||
]; | ||
|
||
return ( | ||
<> | ||
<EuiButtonEmpty | ||
className="euiHeaderLink" | ||
data-test-subj="dataSourceAggregatedViewContextMenuHeaderLink" | ||
aria-label={i18n.translate('dataSourceAggregatedView.dataSourceOptionsButtonAriaLabel', { | ||
defaultMessage: 'dataSourceAggregatedViewMenuButton', | ||
})} | ||
iconType="database" | ||
iconSide="left" | ||
size="s" | ||
disabled={true} | ||
> | ||
{'Data sources'} | ||
</EuiButtonEmpty> | ||
<EuiNotificationBadge color={'subdued'}> | ||
{this.props.activeDataSourceIds?.length || 'All'} | ||
</EuiNotificationBadge> | ||
<EuiPopover | ||
id={'dataSourceSViewContextMenuPopover'} | ||
button={button} | ||
isOpen={this.state.isPopoverOpen} | ||
closePopover={this.closePopover.bind(this)} | ||
panelPaddingSize="none" | ||
anchorPosition="downLeft" | ||
> | ||
<EuiContextMenu initialPanelId={0} panels={panels} /> | ||
</EuiPopover> | ||
</> | ||
); | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
src/plugins/data_source_management/public/components/data_source_aggregated_view/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
export { DataSourceAggregatedView } from './data_source_aggregated_view'; |
61 changes: 61 additions & 0 deletions
61
...anagement/public/components/data_source_menu/__snapshots__/data_source_menu.test.tsx.snap
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.