From e55f421ee0f492925e0d211fd20b7f2b2615a677 Mon Sep 17 00:00:00 2001 From: Amardeepsingh Siglani Date: Wed, 4 Sep 2024 11:05:08 -0700 Subject: [PATCH] Update url with data source id; redirect on reload if ds id not present; minor fixes (#1125) * update url with data source id; redirect on reload if ds id not present; minor fixes Signed-off-by: Amardeepsingh Siglani * revert unwanted change Signed-off-by: Amardeepsingh Siglani --------- Signed-off-by: Amardeepsingh Siglani --- .../components/MDS/DataSourceMenuWrapper.tsx | 70 ++++++++++++++---- public/pages/Main/Main.tsx | 42 +++++++---- .../AddThreatIntelSource.tsx | 8 +- .../Overview/ThreatIntelOverview.tsx | 6 +- public/security_analytics_app.tsx | 73 +++++++++++++++---- types/shared.ts | 1 - 6 files changed, 149 insertions(+), 51 deletions(-) diff --git a/public/components/MDS/DataSourceMenuWrapper.tsx b/public/components/MDS/DataSourceMenuWrapper.tsx index 7589c127..9a5c1049 100644 --- a/public/components/MDS/DataSourceMenuWrapper.tsx +++ b/public/components/MDS/DataSourceMenuWrapper.tsx @@ -4,7 +4,7 @@ */ import React, { useContext } from 'react'; -import { Route, Switch } from 'react-router-dom'; +import { Route, RouteComponentProps, Switch, matchPath } from 'react-router-dom'; import { DataSourceManagementPluginSetup, DataSourceSelectableConfig, @@ -15,7 +15,7 @@ import { ROUTES } from '../../utils/constants'; import { DataSourceContext } from '../../services/DataSourceContext'; import { DataSourceAttributes } from 'src/plugins/data_source/common/data_sources'; -export interface DataSourceMenuWrapperProps { +export interface DataSourceMenuWrapperProps extends RouteComponentProps { core: CoreStart; dataSourceManagement?: DataSourceManagementPluginSetup; dataSourceMenuReadOnly: boolean; @@ -31,11 +31,12 @@ export const DataSourceMenuWrapper: React.FC = ({ dataSourceLoading, setHeaderActionMenu, dataSourceFilterFn, + location, + history, }) => { if (!dataSourceManagement) { return null; } - const { dataSource, setDataSource } = useContext(DataSourceContext)!; const DataSourceMenuViewComponent = dataSourceManagement.ui?.getDataSourceMenu< DataSourceViewConfig @@ -44,29 +45,66 @@ export const DataSourceMenuWrapper: React.FC = ({ DataSourceSelectableConfig >(); + const readonlyDataSourcePaths = [ + ROUTES.EDIT_DETECTOR_ALERT_TRIGGERS, + ROUTES.EDIT_DETECTOR_DETAILS, + ROUTES.EDIT_DETECTOR_RULES, + ROUTES.EDIT_FIELD_MAPPINGS, + ROUTES.RULES_EDIT, + ROUTES.CORRELATION_RULE_EDIT, + ROUTES.DETECTOR_DETAILS, + `${ROUTES.LOG_TYPES}/:logTypeId`, + `${ROUTES.ALERTS}/:detectorId`, + `${ROUTES.FINDINGS}/:detectorId`, + `${ROUTES.THREAT_INTEL_SOURCE_DETAILS}/:sourceId`, + ROUTES.THREAT_INTEL_EDIT_SCAN_CONFIG, + ]; + + const pathToParentMap = { + [ROUTES.EDIT_DETECTOR_ALERT_TRIGGERS]: ROUTES.DETECTORS, + [ROUTES.EDIT_DETECTOR_DETAILS]: ROUTES.DETECTORS, + [ROUTES.EDIT_DETECTOR_RULES]: ROUTES.DETECTORS, + [ROUTES.EDIT_FIELD_MAPPINGS]: ROUTES.DETECTORS, + [ROUTES.RULES_EDIT]: ROUTES.RULES, + [ROUTES.CORRELATION_RULE_EDIT]: ROUTES.CORRELATION_RULES, + [ROUTES.DETECTOR_DETAILS]: ROUTES.DETECTORS, + [`${ROUTES.LOG_TYPES}/:logTypeId`]: ROUTES.LOG_TYPES, + [`${ROUTES.ALERTS}/:detectorId`]: ROUTES.ALERTS, + [`${ROUTES.FINDINGS}/:detectorId`]: ROUTES.FINDINGS, + [`${ROUTES.THREAT_INTEL_SOURCE_DETAILS}/:sourceId`]: ROUTES.THREAT_INTEL_OVERVIEW, + [ROUTES.THREAT_INTEL_EDIT_SCAN_CONFIG]: ROUTES.THREAT_INTEL_OVERVIEW, + }; + + const matchedPath = matchPath(location.pathname, { + path: readonlyDataSourcePaths, + }); + + if (matchedPath) { + // should have the data source id in url, if not then redirect back to the overview or related page for each path + const searchParams = new URLSearchParams(location.search); + const dataSourceId = searchParams.get('dataSourceId'); + if (dataSourceId !== null && dataSourceId !== undefined) { + setDataSource([{ id: dataSourceId }]); + } else { + const parentPath = pathToParentMap[matchedPath.path] || ROUTES.OVERVIEW; + history.replace(parentPath); + } + } + const activeOption = dataSourceLoading ? undefined : [dataSource]; return ( { return ( = ({ componentConfig={{ fullWidth: false, activeOption: [dataSource], + notifications: core.notifications, + savedObjects: core.savedObjects.client, dataSourceFilter: dataSourceFilterFn, }} componentType="DataSourceView" diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 040150ac..78fe978f 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -61,10 +61,10 @@ import { ThreatIntelOverview } from '../ThreatIntel/containers/Overview/ThreatIn import { AddThreatIntelSource } from '../ThreatIntel/containers/AddThreatIntelSource/AddThreatIntelSource'; import { ThreatIntelScanConfigForm } from '../ThreatIntel/containers/ScanConfiguration/ThreatIntelScanConfigForm'; import { ThreatIntelSource } from '../ThreatIntel/containers/ThreatIntelSource/ThreatIntelSource'; -import * as pluginManifest from "../../../opensearch_dashboards.json"; -import { DataSourceAttributes } from "../../../../../src/plugins/data_source/common/data_sources"; -import semver from "semver"; -import queryString from "query-string"; +import * as pluginManifest from '../../../opensearch_dashboards.json'; +import { DataSourceAttributes } from '../../../../../src/plugins/data_source/common/data_sources'; +import semver from 'semver'; +import queryString from 'query-string'; enum Navigation { SecurityAnalytics = 'Security Analytics', @@ -202,11 +202,19 @@ export default class Main extends Component { prevState: Readonly, snapshot?: any ): void { - if (this.props.location.pathname === prevProps.location.pathname) { - return; + const pathnameChanged = this.props.location.pathname !== prevProps.location.pathname; + + if (pathnameChanged || this.state.selectedDataSource.id !== prevState.selectedDataSource.id) { + const searchParams = new URLSearchParams(this.props.location.search); + searchParams.set('dataSourceId', this.state.selectedDataSource.id); + this.props.history.replace({ + search: searchParams.toString(), + }); } - this.updateSelectedNavItem(); + if (pathnameChanged) { + this.updateSelectedNavItem(); + } } setDateTimeFilter = (dateTimeFilter: DateTimeFilter) => { @@ -399,11 +407,13 @@ export default class Main extends Component { }; dataSourceFilterFn = (dataSource: SavedObject) => { - const dataSourceVersion = dataSource?.attributes?.dataSourceVersion || ""; + const dataSourceVersion = dataSource?.attributes?.dataSourceVersion || ''; const installedPlugins = dataSource?.attributes?.installedPlugins || []; return ( semver.satisfies(dataSourceVersion, pluginManifest.supportedOSDataSourceVersions) && - pluginManifest.requiredOSDataSourcePlugins.every((plugin) => installedPlugins.includes(plugin)) + pluginManifest.requiredOSDataSourcePlugins.every((plugin) => + installedPlugins.includes(plugin) + ) ); }; @@ -447,6 +457,7 @@ export default class Main extends Component { <> {multiDataSourceEnabled && ( { {!dataSourceLoading && services && ( {/* Hide side navigation bar when on any HIDDEN_NAV_ROUTES pages. */} - {!HIDDEN_NAV_ROUTES.some((route) => pathname.match(route)) && ( - !core.chrome.navGroup.getNavGroupEnabled() && - - - - )} + {!HIDDEN_NAV_ROUTES.some((route) => pathname.match(route)) && + !core.chrome.navGroup.getNavGroupEnabled() && ( + + + + )} {callout ? : null} {showFlyoutData ? ( @@ -784,6 +795,7 @@ export default class Main extends Component { ); }} diff --git a/public/pages/ThreatIntel/containers/AddThreatIntelSource/AddThreatIntelSource.tsx b/public/pages/ThreatIntel/containers/AddThreatIntelSource/AddThreatIntelSource.tsx index 18be1f19..499f705b 100644 --- a/public/pages/ThreatIntel/containers/AddThreatIntelSource/AddThreatIntelSource.tsx +++ b/public/pages/ThreatIntel/containers/AddThreatIntelSource/AddThreatIntelSource.tsx @@ -596,19 +596,21 @@ export const AddThreatIntelSource: React.FC = ({

- + - + - history.push(ROUTES.THREAT_INTEL_OVERVIEW)}>Cancel + history.push(ROUTES.THREAT_INTEL_OVERVIEW)}> + Cancel + = ({ history, threatIntelService, + dataSource, }) => { const [threatIntelSources, setThreatIntelSources] = useState([]); const [scanConfig, setScanConfig] = useState(undefined); @@ -136,7 +138,7 @@ export const ThreatIntelOverview: React.FC = ({ searchSources(); getScanConfig(); - }, [threatIntelService]); + }, [threatIntelService, dataSource.id]); const nextStepClickHandlerById: Record = { ['add-source']: addThreatIntelSourceActionHandler, diff --git a/public/security_analytics_app.tsx b/public/security_analytics_app.tsx index 39d88f3a..b1723e6d 100644 --- a/public/security_analytics_app.tsx +++ b/public/security_analytics_app.tsx @@ -20,6 +20,8 @@ import { CHANNEL_TYPES } from './pages/CreateDetector/components/ConfigureAlerts import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public'; import { getPlugins, setIsNotificationPluginInstalled } from './utils/helpers'; import { OS_NOTIFICATION_PLUGIN } from './utils/constants'; +import { dataSourceInfo } from './services/utils/constants'; +import { History } from 'history'; import { getBrowserServices } from './services/utils/constants'; export function renderApp( @@ -37,21 +39,62 @@ export function renderApp( ReactDOM.render( ( - - - -
- - - - )} + render={(props) => { + const originalMethods = { + push: props.history.push, + replace: props.history.replace, + }; + const wrapper = (method: 'push' | 'replace') => ( + ...args: Parameters + ) => { + if (typeof args[0] === 'string') { + const url = new URL(args[0], window.location.origin); + const searchParams = url.searchParams; + searchParams.set('dataSourceId', dataSourceInfo.activeDataSource.id); + originalMethods[method]( + { + pathname: url.pathname, + search: searchParams.toString(), + }, + ...args.slice(1) + ); + } else if (typeof args[0] === 'object') { + const searchParams = new URLSearchParams(args[0].search); + searchParams.set('dataSourceId', dataSourceInfo.activeDataSource.id); + originalMethods[method]( + { + ...args[0], + search: searchParams.toString(), + }, + ...args.slice(1) + ); + } else { + originalMethods[method](...args); + } + }; + + props.history = { + ...props.history, + push: wrapper('push'), + replace: wrapper('replace'), + }; + + return ( + + + +
+ + + + ); + }} /> , params.element diff --git a/types/shared.ts b/types/shared.ts index 8d57c05c..1e76c377 100644 --- a/types/shared.ts +++ b/types/shared.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React from 'react'; import { CorrelationFinding } from './Correlations'; import { DetectorHit } from './Detector'; import { Finding, FindingDetailsFlyoutProps } from './Finding';