From 9613011cfddea3c696d39c3f04683931c8707e6c Mon Sep 17 00:00:00 2001 From: Kristen Tian Date: Tue, 27 Jun 2023 14:04:10 -0700 Subject: [PATCH] Enable sample data with Multiple datasource frontend Signed-off-by: Kristen Tian --- CHANGELOG.md | 1 + src/plugins/home/opensearch_dashboards.json | 2 +- .../components/sample_data_set_card.js | 4 +- .../components/sample_data_set_cards.js | 24 +++- .../components/tutorial_directory.js | 104 ++++++++++++++++-- .../opensearch_dashboards_services.ts | 2 + src/plugins/home/public/plugin.ts | 5 +- 7 files changed, 120 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a9d7813e275..81a8e55d4f56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multiple DataSource] Backend support for adding sample data ([#4268](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4268)) - Add configurable defaults and overrides to uiSettings ([#4344](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4344)) - Bump OUI to `1.1.2` to make `anomalyDetection` icon available ([#4408](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4408)) +- [Multiple DataSource] Frontend support for adding sample data ([#4412](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4412)) ### 🐛 Bug Fixes diff --git a/src/plugins/home/opensearch_dashboards.json b/src/plugins/home/opensearch_dashboards.json index 35a81bc7adb9..40351c0dd83c 100644 --- a/src/plugins/home/opensearch_dashboards.json +++ b/src/plugins/home/opensearch_dashboards.json @@ -6,6 +6,6 @@ "requiredPlugins": ["data", "urlForwarding"], "optionalPlugins": ["usageCollection", "telemetry", "dataSource"], "requiredBundles": [ - "opensearchDashboardsReact" + "opensearchDashboardsReact", "dataSourceManagement" ] } diff --git a/src/plugins/home/public/application/components/sample_data_set_card.js b/src/plugins/home/public/application/components/sample_data_set_card.js index 3ec86826639a..7d8b97a1c982 100644 --- a/src/plugins/home/public/application/components/sample_data_set_card.js +++ b/src/plugins/home/public/application/components/sample_data_set_card.js @@ -57,11 +57,11 @@ export class SampleDataSetCard extends React.Component { }; install = () => { - this.props.onInstall(this.props.id); + this.props.onInstall(this.props.id, this.props.dataSourceId); }; uninstall = () => { - this.props.onUninstall(this.props.id); + this.props.onUninstall(this.props.id, this.props.dataSourceId); }; renderBtn = () => { diff --git a/src/plugins/home/public/application/components/sample_data_set_cards.js b/src/plugins/home/public/application/components/sample_data_set_cards.js index 31735d203dda..0234f5f4a6e0 100644 --- a/src/plugins/home/public/application/components/sample_data_set_cards.js +++ b/src/plugins/home/public/application/components/sample_data_set_cards.js @@ -67,10 +67,21 @@ export class SampleDataSetCards extends React.Component { this.loadSampleDataSets(); } - loadSampleDataSets = async () => { + componentDidUpdate(prevProps) { + if (this.props.isDataSourceEnabled) { + this._isMounted = true; + if (prevProps && prevProps.dataSourceId !== this.props.dataSourceId) { + this.setState({ dataSourceId: this.props.dataSourceId }, () => + this.loadSampleDataSets(this.state.dataSourceId) + ); + } + } + } + + loadSampleDataSets = async (dataSourceId) => { let sampleDataSets; try { - sampleDataSets = await listSampleDataSets(); + sampleDataSets = await listSampleDataSets(dataSourceId); } catch (fetchError) { this.toastNotifications.addDanger({ title: i18n.translate('home.sampleDataSet.unableToLoadListErrorMessage', { @@ -93,7 +104,7 @@ export class SampleDataSetCards extends React.Component { }); }; - install = async (id) => { + install = async (id, dataSourceId) => { const targetSampleDataSet = this.state.sampleDataSets.find((sampleDataSet) => { return sampleDataSet.id === id; }); @@ -103,7 +114,7 @@ export class SampleDataSetCards extends React.Component { })); try { - await installSampleDataSet(id, targetSampleDataSet.defaultIndex); + await installSampleDataSet(id, targetSampleDataSet.defaultIndex, dataSourceId); } catch (fetchError) { if (this._isMounted) { this.setState((prevState) => ({ @@ -141,7 +152,7 @@ export class SampleDataSetCards extends React.Component { }); }; - uninstall = async (id) => { + uninstall = async (id, dataSourceId) => { const targetSampleDataSet = this.state.sampleDataSets.find((sampleDataSet) => { return sampleDataSet.id === id; }); @@ -151,7 +162,7 @@ export class SampleDataSetCards extends React.Component { })); try { - await uninstallSampleDataSet(id, targetSampleDataSet.defaultIndex); + await uninstallSampleDataSet(id, targetSampleDataSet.defaultIndex, dataSourceId); } catch (fetchError) { if (this._isMounted) { this.setState((prevState) => ({ @@ -213,6 +224,7 @@ export class SampleDataSetCards extends React.Component { previewUrl={this.props.addBasePath(this.lightOrDarkImage(sampleDataSet))} onInstall={this.install} onUninstall={this.uninstall} + dataSourceId={this.state.dataSourceId} /> ); diff --git a/src/plugins/home/public/application/components/tutorial_directory.js b/src/plugins/home/public/application/components/tutorial_directory.js index ce9cf071d3a0..0dcdc3ec775f 100644 --- a/src/plugins/home/public/application/components/tutorial_directory.js +++ b/src/plugins/home/public/application/components/tutorial_directory.js @@ -34,9 +34,12 @@ import PropTypes from 'prop-types'; import { Synopsis } from './synopsis'; import { SampleDataSetCards } from './sample_data_set_cards'; import { getServices } from '../opensearch_dashboards_services'; +// eslint-disable-next-line @osd/eslint/no-restricted-paths +import { getDataSources } from '../../../../data_source_management/public/components/utils'; import { EuiPage, + EuiPanel, EuiTabs, EuiTab, EuiFlexItem, @@ -45,10 +48,10 @@ import { EuiSpacer, EuiTitle, EuiPageBody, + EuiComboBox, } from '@elastic/eui'; import { getTutorials } from '../load_tutorials'; - import { injectI18n, FormattedMessage } from '@osd/i18n/react'; import { i18n } from '@osd/i18n'; @@ -59,6 +62,9 @@ const homeTitle = i18n.translate('home.breadcrumbs.homeTitle', { defaultMessage: const addDataTitle = i18n.translate('home.breadcrumbs.addDataTitle', { defaultMessage: 'Add data', }); +const localCluster = i18n.translate('home.dataSource.localCluster', { + defaultMessage: 'Local Cluster', +}); class TutorialDirectoryUi extends React.Component { constructor(props) { @@ -81,6 +87,8 @@ class TutorialDirectoryUi extends React.Component { selectedTabId: openTab, tutorialCards: [], notices: getServices().tutorialService.getDirectoryNotices(), + isDataSourceEnabled: !!getServices().dataSource, + selectedOption: [{ label: localCluster }], }; } @@ -152,6 +160,31 @@ class TutorialDirectoryUi extends React.Component { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); }); + if (this.state.isDataSourceEnabled) { + getDataSources(getServices().savedObjectsClient) + .then((fetchedDataSources) => { + if (fetchedDataSources?.length) { + const dataSourceOptions = fetchedDataSources.map((dataSource) => ({ + id: dataSource.id, + label: dataSource.title, + })); + + dataSourceOptions.push({ label: localCluster }); + this.setState({ + // eslint-disable-line react/no-did-mount-set-state + dataSources: dataSourceOptions, + }); + } + }) + .catch(() => { + getServices().toastNotifications.addWarning( + i18n.translate('home.dataSource.fetchDataSourceError', { + defaultMessage: 'Unable to fetch existing data sources', + }) + ); + }); + } + this.setState({ // eslint-disable-line react/no-did-mount-set-state tutorialCards: tutorialCards, @@ -164,6 +197,12 @@ class TutorialDirectoryUi extends React.Component { }); }; + onSelectedDataSourceChange = (e) => { + this.setState({ selectedOption: e }); + const dataSourceId = e[0] ? e[0].id : undefined; + this.setState({ selectedDataSourceId: dataSourceId }); + }; + renderTabs = () => { return this.tabs.map((tab, index) => ( { if (this.state.selectedTabId === SAMPLE_DATA_TAB_ID) { - return ; + return ( + + ); } return ( @@ -210,6 +255,30 @@ class TutorialDirectoryUi extends React.Component { ); }; + renderDataSourceSelector = () => { + const { isDataSourceEnabled, dataSources, selectedOption } = this.state; + + return isDataSourceEnabled ? ( +
+ +
+ ) : null; + }; + renderNotices = () => { const notices = getServices().tutorialService.getDirectoryNotices(); return notices.length ? ( @@ -260,17 +329,28 @@ class TutorialDirectoryUi extends React.Component { ); }; - render() { + renderPageBody = () => { return ( - - - {this.renderHeader()} - - {this.renderTabs()} - - {this.renderTabContent()} - - + + {this.renderHeader()} + + {this.renderDataSourceSelector()} + {this.renderTabs()} + + {this.renderTabContent()} + + ); + }; + + render() { + const { isDataSourceEnabled } = this.state; + + return isDataSourceEnabled ? ( + + {this.renderPageBody()} + + ) : ( + {this.renderPageBody()} ); } } diff --git a/src/plugins/home/public/application/opensearch_dashboards_services.ts b/src/plugins/home/public/application/opensearch_dashboards_services.ts index 84d174984fac..60f9e70621ff 100644 --- a/src/plugins/home/public/application/opensearch_dashboards_services.ts +++ b/src/plugins/home/public/application/opensearch_dashboards_services.ts @@ -46,6 +46,7 @@ import { FeatureCatalogueRegistry } from '../services/feature_catalogue'; import { EnvironmentService } from '../services/environment'; import { ConfigSchema } from '../../config'; import { HomePluginBranding } from '..'; +import { DataSourcePluginStart } from '../../../data_source/public'; export interface HomeOpenSearchDashboardsServices { indexPatternService: any; @@ -71,6 +72,7 @@ export interface HomeOpenSearchDashboardsServices { getInjectedVar: (name: string, defaultValue?: any) => unknown; getBranding: () => HomePluginBranding; }; + dataSource?: DataSourcePluginStart; } let services: HomeOpenSearchDashboardsServices | null = null; diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index 03823c5da179..1538156a801e 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -56,11 +56,13 @@ import { UsageCollectionSetup } from '../../usage_collection/public'; import { UrlForwardingSetup, UrlForwardingStart } from '../../url_forwarding/public'; import { AppNavLinkStatus } from '../../../core/public'; import { PLUGIN_ID, HOME_APP_BASE_PATH } from '../common/constants'; +import { DataSourcePluginStart } from '../../data_source/public'; export interface HomePluginStartDependencies { data: DataPublicPluginStart; telemetry?: TelemetryPluginStart; urlForwarding: UrlForwardingStart; + dataSource?: DataSourcePluginStart; } export interface HomePluginSetupDependencies { @@ -96,7 +98,7 @@ export class HomePublicPlugin : () => {}; const [ coreStart, - { telemetry, data, urlForwarding: urlForwardingStart }, + { telemetry, data, urlForwarding: urlForwardingStart, dataSource }, ] = await core.getStartServices(); setServices({ trackUiMetric, @@ -119,6 +121,7 @@ export class HomePublicPlugin tutorialService: this.tutorialService, featureCatalogue: this.featuresCatalogueRegistry, injectedMetadata: coreStart.injectedMetadata, + dataSource, }); coreStart.chrome.docTitle.change( i18n.translate('home.pageTitle', { defaultMessage: 'Home' })