From 116bc577509dc69e724ae5947bc6b6c7c8a53e32 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 (cherry picked from commit 7aba1fd8b4ba174820ca09370012c3eeae3d67ec) --- common/utils/legacy_route_helper.tsx | 31 ++++++++++ opensearch_dashboards.json | 15 ++--- public/application.tsx | 4 +- public/components/Main/main.tsx | 41 +++++++------ public/components/app.tsx | 15 ++++- public/plugin.ts | 86 +++++++++++++++++++++------- 6 files changed, 137 insertions(+), 55 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 00000000..e4e0cbd7 --- /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 5cff9b19..e838eb41 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -4,18 +4,13 @@ "opensearchDashboardsVersion": "2.16.0", "server": true, "ui": true, + "supportedOSDataSourceVersions": ">=1.0.0", + "requiredOSDataSourcePlugins": ["opensearch-sql"], "requiredPlugins": [ + "devTools", "navigation", "opensearchDashboardsReact", "opensearchDashboardsUtils" ], - "optionalPlugins": [ - "observabilityDashboards", - "dataSource", - "dataSourceManagement" - ], - "supportedOSDataSourceVersions": ">=1.0.0", - "requiredOSDataSourcePlugins": [ - "opensearch-sql" - ] -} \ No newline at end of file + "optionalPlugins": ["observabilityDashboards", "dataSource", "dataSourceManagement"] +} diff --git a/public/application.tsx b/public/application.tsx index 885e512c..481cfe4a 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 183774b1..d64bcbcd 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 61709cc7..5cf455fc 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 03480a90..47113639 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 {};