From 7aba1fd8b4ba174820ca09370012c3eeae3d67ec Mon Sep 17 00:00:00 2001 From: Adam Tackett <105462877+TackAdam@users.noreply.github.com> Date: Fri, 19 Jul 2024 13:30:50 -0700 Subject: [PATCH] Moving Query Workbench to Dev Tools (#349) * Migrate to devTools Signed-off-by: Adam Tackett * MDS working changed how ID passed Signed-off-by: Adam Tackett * Chagned name to dataSourceMDSId Signed-off-by: Adam Tackett * remove console log Signed-off-by: Adam Tackett * add support for backward URLs Signed-off-by: Shenoy Pratik * update nav coditionally and set breadcrumbs Signed-off-by: Shenoy Pratik * fix lint error Signed-off-by: Shenoy Pratik * update breadcrumb only when nav is enabled Signed-off-by: Shenoy Pratik * fix URL for new nav is disabled Signed-off-by: Shenoy Pratik --------- Signed-off-by: Adam Tackett Signed-off-by: Shenoy Pratik Co-authored-by: Adam Tackett Co-authored-by: Shenoy Pratik --- common/utils/legacy_route_helper.tsx | 31 ++++++++++ opensearch_dashboards.json | 2 +- public/application.tsx | 4 +- public/components/Main/main.tsx | 41 +++++++------ public/components/app.tsx | 15 ++++- public/plugin.ts | 86 +++++++++++++++++++++------- 6 files changed, 133 insertions(+), 46 deletions(-) create mode 100644 common/utils/legacy_route_helper.tsx diff --git a/common/utils/legacy_route_helper.tsx b/common/utils/legacy_route_helper.tsx new file mode 100644 index 0000000..e4e0cbd --- /dev/null +++ b/common/utils/legacy_route_helper.tsx @@ -0,0 +1,31 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const convertLegacyWorkbenchUrl = (location: Location) => { + // Update pathname to new structure + let pathname = location.pathname.replace( + 'app/opensearch-query-workbench', + 'app/dev_tools#/opensearch-query-workbench' + ); + + // If the pathname ends with '/', remove it before appending the hash + if (pathname.endsWith('/')) { + pathname = pathname.slice(0, -1); + } + + // Adjust the hash part of the URL + let hash = location.hash.replace('#/', '/'); + + // If hash contains "accelerate" or any random text, handle it properly + if (hash.includes('accelerate/')) { + hash = hash.replace('#/', '/'); + } else if (hash.startsWith('#/')) { + hash = hash.replace('#/', '/'); + } + + const finalUrl = `${pathname}${hash}`; + + return finalUrl; +}; diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 26acfcf..a7be2f9 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -6,6 +6,6 @@ "ui": true, "supportedOSDataSourceVersions": ">=1.0.0", "requiredOSDataSourcePlugins": ["opensearch-sql"], - "requiredPlugins": ["navigation", "opensearchDashboardsReact", "opensearchDashboardsUtils"], + "requiredPlugins": ["devTools", "navigation", "opensearchDashboardsReact", "opensearchDashboardsUtils"], "optionalPlugins": ["observabilityDashboards", "dataSource", "dataSourceManagement"] } \ No newline at end of file diff --git a/public/application.tsx b/public/application.tsx index 885e512..481cfe4 100644 --- a/public/application.tsx +++ b/public/application.tsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ - import React from 'react'; import ReactDOM from 'react-dom'; import { AppMountParameters, CoreStart } from '../../../src/core/public'; @@ -14,7 +13,7 @@ import { AppPluginStartDependencies } from './types'; export const renderApp = ( { notifications, http, chrome, savedObjects }: CoreStart, { navigation, dataSource }: AppPluginStartDependencies, - { appBasePath, element, setHeaderActionMenu }: AppMountParameters, + { appBasePath, element, setHeaderActionMenu, dataSourceId }: AppMountParameters, dataSourceManagement: DataSourceManagementPluginSetup ) => { ReactDOM.render( @@ -28,6 +27,7 @@ export const renderApp = ( dataSourceEnabled={!!dataSource} dataSourceManagement={dataSourceManagement} setActionMenu={setHeaderActionMenu} + dataSourceMDSId={dataSourceId} />, element ); diff --git a/public/components/Main/main.tsx b/public/components/Main/main.tsx index 183774b..d64bcbc 100644 --- a/public/components/Main/main.tsx +++ b/public/components/Main/main.tsx @@ -42,6 +42,7 @@ import { AsyncApiResponse, AsyncQueryStatus } from '../../../common/types'; import { executeAsyncQuery } from '../../../common/utils/async_query_helpers'; import { fetchDataSources } from '../../../common/utils/fetch_datasources'; import * as pluginManifest from '../../../opensearch_dashboards.json'; +import { coreRefs } from '../../framework/core_refs'; import { MESSAGE_TAB_LABEL } from '../../utils/constants'; import { Tree, @@ -114,6 +115,7 @@ interface MainProps { notifications: NotificationsStart; dataSourceEnabled: boolean; dataSourceManagement: DataSourceManagementPluginSetup; + dataSourceMDSId: string; setActionMenu: (menuMount: MountPoint | undefined) => void; } @@ -290,7 +292,7 @@ export class Main extends React.Component { isCallOutVisible: false, cluster: 'Indexes', dataSourceOptions: [], - selectedMDSDataConnectionId: '', + selectedMDSDataConnectionId: this.props.dataSourceMDSId, mdsClusterName: '', flintDataConnections: false, }; @@ -301,19 +303,22 @@ export class Main extends React.Component { } componentDidMount() { - this.props.setBreadcrumbs([ - { - text: 'Query Workbench', - href: '#', - }, - ]); + if (!coreRefs?.chrome?.navGroup.getNavGroupEnabled()) { + this.props.setBreadcrumbs([ + { + text: 'Query Workbench', + href: '#', + }, + ]); + } + this.fetchFlintDataSources(); } fetchFlintDataSources = () => { fetchDataSources( this.httpClient, - this.state.selectedMDSDataConnectionId, + this.props.dataSourceMDSId, this.props.urlDataSource, (dataOptions) => { if (dataOptions.length > 0) { @@ -435,7 +440,7 @@ export class Main extends React.Component { const endpoint = '/api/sql_console/' + (_.isEqual(language, 'SQL') ? 'sqlquery' : 'pplquery'); let query = {}; if (this.props.dataSourceEnabled) { - query = { dataSourceMDSId: this.state.selectedMDSDataConnectionId }; + query = { dataSourceMDSId: this.props.dataSourceMDSId }; } const responsePromise = Promise.all( queries.map((eachQuery: string) => @@ -570,7 +575,7 @@ export class Main extends React.Component { }); } }, - this.state.selectedMDSDataConnectionId, + this.props.dataSourceMDSId, (errorDetails: string) => { this.setState({ asyncLoading: false, @@ -599,7 +604,7 @@ export class Main extends React.Component { if (queries.length > 0) { let query = {}; if (this.props.dataSourceEnabled) { - query = { dataSourceMDSId: this.state.selectedMDSDataConnectionId }; + query = { dataSourceMDSId: this.props.dataSourceMDSId }; } const endpoint = '/api/sql_console/' + (_.isEqual(language, 'SQL') ? 'translatesql' : 'translateppl'); @@ -651,7 +656,7 @@ export class Main extends React.Component { if (queries.length > 0) { let query = {}; if (this.props.dataSourceEnabled) { - query = { dataSourceMDSId: this.state.selectedMDSDataConnectionId }; + query = { dataSourceMDSId: this.props.dataSourceMDSId }; } Promise.all( queries.map((eachQuery: string) => @@ -688,7 +693,7 @@ export class Main extends React.Component { if (queries.length > 0) { let query = {}; if (this.props.dataSourceEnabled) { - query = { dataSourceMDSId: this.state.selectedMDSDataConnectionId }; + query = { dataSourceMDSId: this.props.dataSourceMDSId }; } const endpoint = '/api/sql_console/' + (_.isEqual(language, 'SQL') ? 'sqlquery' : 'pplquery'); Promise.all( @@ -726,7 +731,7 @@ export class Main extends React.Component { if (queries.length > 0) { let query = {}; if (this.props.dataSourceEnabled) { - query = { dataSourceMDSId: this.state.selectedMDSDataConnectionId }; + query = { dataSourceMDSId: this.props.dataSourceMDSId }; } const endpoint = '/api/sql_console/' + (_.isEqual(language, 'SQL') ? 'sqlcsv' : 'pplcsv'); Promise.all( @@ -764,7 +769,7 @@ export class Main extends React.Component { if (queries.length > 0) { let query = {}; if (this.props.dataSourceEnabled) { - query = { dataSourceMDSId: this.state.selectedMDSDataConnectionId }; + query = { dataSourceMDSId: this.props.dataSourceMDSId }; } const endpoint = '/api/sql_console/' + (_.isEqual(language, 'SQL') ? 'sqltext' : 'ppltext'); Promise.all( @@ -947,7 +952,7 @@ export class Main extends React.Component { openAccelerationFlyout={ this.props.isAccelerationFlyoutOpen && !this.state.isAccelerationFlyoutOpened } - dataSourceMDSId={this.state.selectedMDSDataConnectionId} + dataSourceMDSId={this.props.dataSourceMDSId} setIsAccelerationFlyoutOpened={this.setIsAccelerationFlyoutOpened} /> ); @@ -1086,7 +1091,7 @@ export class Main extends React.Component { onSelect={this.handleDataSelect} urlDataSource={this.props.urlDataSource} asyncLoading={this.state.asyncLoading} - dataSourceMDSId={this.state.selectedMDSDataConnectionId} + dataSourceMDSId={this.props.dataSourceMDSId} /> @@ -1105,7 +1110,7 @@ export class Main extends React.Component { updateSQLQueries={this.updateSQLQueries} refreshTree={this.state.refreshTree} dataSourceEnabled={this.props.dataSourceEnabled} - dataSourceMDSId={this.state.selectedMDSDataConnectionId} + dataSourceMDSId={this.props.dataSourceMDSId} clusterTab={this.state.cluster} language={this.state.language} updatePPLQueries={this.updatePPLQueries} diff --git a/public/components/app.tsx b/public/components/app.tsx index 61709cc..5cf455f 100644 --- a/public/components/app.tsx +++ b/public/components/app.tsx @@ -13,6 +13,7 @@ import { CoreStart, MountPoint } from '../../../../src/core/public'; import { DataSourceManagementPluginSetup } from '../../../../src/plugins/data_source_management/public'; import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public'; +import { coreRefs } from '../framework/core_refs'; import { Main } from './Main'; interface WorkbenchAppDeps { @@ -24,6 +25,7 @@ interface WorkbenchAppDeps { savedObjects: CoreStart['savedObjects']; dataSourceEnabled: boolean; dataSourceManagement: DataSourceManagementPluginSetup; + dataSourceMDSId: string; setActionMenu: (menuMount: MountPoint | undefined) => void; } @@ -36,8 +38,12 @@ export const WorkbenchApp = ({ savedObjects, dataSourceEnabled, dataSourceManagement, + dataSourceMDSId: dataSourceId, setActionMenu, }: WorkbenchAppDeps) => { + const isNavGroupEnabled = coreRefs?.chrome?.navGroup.getNavGroupEnabled(); + const basePath = isNavGroupEnabled ? 'opensearch-query-workbench' : ''; + return ( @@ -47,7 +53,7 @@ export const WorkbenchApp = ({ (
)} /> (
)} /> (
)} diff --git a/public/plugin.ts b/public/plugin.ts index 03480a9..4711363 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -4,41 +4,83 @@ */ import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '../../../src/core/public'; -import { DataSourcePluginSetup, DataSourcePluginStart } from '../../../src/plugins/data_source/public'; +import { + DataSourcePluginSetup, + DataSourcePluginStart, +} from '../../../src/plugins/data_source/public'; import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public'; +import { DevToolsSetup } from '../../../src/plugins/dev_tools/public'; import { PLUGIN_NAME } from '../common/constants'; +import { convertLegacyWorkbenchUrl } from '../common/utils/legacy_route_helper'; import { registerObservabilityDependencies } from './dependencies/register_observability_dependencies'; import { coreRefs } from './framework/core_refs'; import { AppPluginStartDependencies, WorkbenchPluginSetup, WorkbenchPluginStart } from './types'; export interface WorkbenchPluginSetupDependencies { dataSource: DataSourcePluginSetup; - dataSourceManagement: DataSourceManagementPluginSetup + dataSourceManagement: DataSourceManagementPluginSetup; + devTools: DevToolsSetup; } export interface WorkbenchPluginStartDependencies { dataSource: DataSourcePluginStart; } export class WorkbenchPlugin implements Plugin { - public setup(core: CoreSetup, {dataSource, dataSourceManagement} : WorkbenchPluginSetupDependencies): WorkbenchPluginSetup { - // Register an application into the side navigation menu - core.application.register({ - id: 'opensearch-query-workbench', - title: PLUGIN_NAME, - category: { - id: 'opensearch', - label: 'OpenSearch Plugins', - order: 2000, - }, - order: 1000, - async mount(params: AppMountParameters) { - // Load application bundle - const { renderApp } = await import('./application'); - // Get start services as specified in opensearch_dashboards.json - const [coreStart, depsStart] = await core.getStartServices(); - // Render the application - return renderApp(coreStart, depsStart as AppPluginStartDependencies, params, dataSourceManagement); - }, - }); + public setup( + core: CoreSetup, + { dataSource, dataSourceManagement, devTools }: WorkbenchPluginSetupDependencies + ): WorkbenchPluginSetup { + const isNavGroupEnabled = core.chrome.navGroup.getNavGroupEnabled(); + + if (isNavGroupEnabled) { + devTools.register({ + id: 'opensearch-query-workbench', + title: PLUGIN_NAME, + enableRouting: true, + order: 2, + async mount(params: AppMountParameters) { + // Load application bundle + const { renderApp } = await import('./application'); + // Get start services as specified in opensearch_dashboards.json + const [coreStart, depsStart] = await core.getStartServices(); + // Render the application + return renderApp( + coreStart, + depsStart as AppPluginStartDependencies, + params, + dataSourceManagement + ); + }, + }); + + // redirect legacy workbench URL to current URL under dev-tools + if (window.location.pathname.includes('app/opensearch-query-workbench')) { + window.location.assign(convertLegacyWorkbenchUrl(window.location)); + } + } else { + core.application.register({ + id: 'opensearch-query-workbench', + title: PLUGIN_NAME, + category: { + id: 'opensearch', + label: 'OpenSearch Plugins', + order: 2000, + }, + order: 1000, + async mount(params: AppMountParameters) { + // Load application bundle + const { renderApp } = await import('./application'); + // Get start services as specified in opensearch_dashboards.json + const [coreStart, depsStart] = await core.getStartServices(); + // Render the application + return renderApp( + coreStart, + depsStart as AppPluginStartDependencies, + params, + dataSourceManagement + ); + }, + }); + } // Return methods that should be available to other plugins return {};