Skip to content

Commit

Permalink
Update url with data source id; redirect on reload if ds id not prese…
Browse files Browse the repository at this point in the history
…nt; minor fixes (#1125)

* update url with data source id; redirect on reload if ds id not present; minor fixes

Signed-off-by: Amardeepsingh Siglani <amardeep7194@gmail.com>

* revert unwanted change

Signed-off-by: Amardeepsingh Siglani <amardeep7194@gmail.com>

---------

Signed-off-by: Amardeepsingh Siglani <amardeep7194@gmail.com>
  • Loading branch information
amsiglan committed Sep 4, 2024
1 parent 7a23b49 commit e55f421
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 51 deletions.
70 changes: 55 additions & 15 deletions public/components/MDS/DataSourceMenuWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -31,11 +31,12 @@ export const DataSourceMenuWrapper: React.FC<DataSourceMenuWrapperProps> = ({
dataSourceLoading,
setHeaderActionMenu,
dataSourceFilterFn,
location,
history,
}) => {
if (!dataSourceManagement) {
return null;
}

const { dataSource, setDataSource } = useContext(DataSourceContext)!;
const DataSourceMenuViewComponent = dataSourceManagement.ui?.getDataSourceMenu<
DataSourceViewConfig
Expand All @@ -44,29 +45,66 @@ export const DataSourceMenuWrapper: React.FC<DataSourceMenuWrapperProps> = ({
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 (
<Switch>
<Route
path={[
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`,
]}
path={readonlyDataSourcePaths}
render={() => {
return (
<DataSourceMenuViewComponent
componentConfig={{
fullWidth: false,
activeOption: [dataSource],
notifications: core.notifications,
savedObjects: core.savedObjects.client,
dataSourceFilter: dataSourceFilterFn,
}}
componentType="DataSourceView"
Expand All @@ -83,6 +121,8 @@ export const DataSourceMenuWrapper: React.FC<DataSourceMenuWrapperProps> = ({
componentConfig={{
fullWidth: false,
activeOption: [dataSource],
notifications: core.notifications,
savedObjects: core.savedObjects.client,
dataSourceFilter: dataSourceFilterFn,
}}
componentType="DataSourceView"
Expand Down
42 changes: 27 additions & 15 deletions public/pages/Main/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -202,11 +202,19 @@ export default class Main extends Component<MainProps, MainState> {
prevState: Readonly<MainState>,
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) => {
Expand Down Expand Up @@ -399,11 +407,13 @@ export default class Main extends Component<MainProps, MainState> {
};

dataSourceFilterFn = (dataSource: SavedObject<DataSourceAttributes>) => {
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)
)
);
};

Expand Down Expand Up @@ -447,6 +457,7 @@ export default class Main extends Component<MainProps, MainState> {
<>
{multiDataSourceEnabled && (
<DataSourceMenuWrapper
{...this.props}
dataSourceManagement={dataSourceManagement}
core={core}
dataSourceLoading={this.state.dataSourceLoading}
Expand All @@ -458,12 +469,12 @@ export default class Main extends Component<MainProps, MainState> {
{!dataSourceLoading && services && (
<EuiPage restrictWidth={'100%'}>
{/* Hide side navigation bar when on any HIDDEN_NAV_ROUTES pages. */}
{!HIDDEN_NAV_ROUTES.some((route) => pathname.match(route)) && (
!core.chrome.navGroup.getNavGroupEnabled() &&
<EuiPageSideBar style={{ minWidth: 200 }}>
<EuiSideNav style={{ width: 200 }} items={sideNav} />
</EuiPageSideBar>
)}
{!HIDDEN_NAV_ROUTES.some((route) => pathname.match(route)) &&
!core.chrome.navGroup.getNavGroupEnabled() && (
<EuiPageSideBar style={{ minWidth: 200 }}>
<EuiSideNav style={{ width: 200 }} items={sideNav} />
</EuiPageSideBar>
)}
<EuiPageBody>
{callout ? <Callout {...callout} /> : null}
{showFlyoutData ? (
Expand Down Expand Up @@ -784,6 +795,7 @@ export default class Main extends Component<MainProps, MainState> {
<ThreatIntelOverview
{...props}
threatIntelService={services.threatIntelService}
dataSource={selectedDataSource}
/>
);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -596,19 +596,21 @@ export const AddThreatIntelSource: React.FC<AddThreatIntelSourceProps> = ({
</p>
</EuiText>
<EuiSpacer size="m" />
<EuiFormRow isInvalid={!!inputErrors.ioc_types} error={inputErrors.ioc_types}>
<EuiCompressedFormRow isInvalid={!!inputErrors.ioc_types} error={inputErrors.ioc_types}>
<EuiCompressedCheckboxGroup
options={checkboxes}
idToSelectedMap={checkboxIdToSelectedMap}
onChange={onIocTypesChange}
/>
</EuiFormRow>
</EuiCompressedFormRow>
<EuiSpacer />
</EuiPanel>
<EuiSpacer />
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiSmallButton onClick={() => history.push(ROUTES.THREAT_INTEL_OVERVIEW)}>Cancel</EuiSmallButton>
<EuiSmallButton onClick={() => history.push(ROUTES.THREAT_INTEL_OVERVIEW)}>
Cancel
</EuiSmallButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSmallButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import React, { MouseEventHandler, useCallback, useEffect, useMemo } from 'react
import { BREADCRUMBS, ROUTES } from '../../../../utils/constants';
import { useState } from 'react';
import {
DataSourceProps,
ThreatIntelNextStepId,
ThreatIntelScanConfig,
ThreatIntelSourceItem,
Expand All @@ -34,13 +35,14 @@ import { ThreatIntelLogScanConfig } from '../../components/ThreatIntelLogScanCon
import { setBreadcrumbs } from '../../../../utils/helpers';
import { PageHeader } from '../../../../components/PageHeader/PageHeader';

export interface ThreatIntelOverviewProps extends RouteComponentProps {
export interface ThreatIntelOverviewProps extends RouteComponentProps, DataSourceProps {
threatIntelService: ThreatIntelService;
}

export const ThreatIntelOverview: React.FC<ThreatIntelOverviewProps> = ({
history,
threatIntelService,
dataSource,
}) => {
const [threatIntelSources, setThreatIntelSources] = useState<ThreatIntelSourceItem[]>([]);
const [scanConfig, setScanConfig] = useState<ThreatIntelScanConfig | undefined>(undefined);
Expand Down Expand Up @@ -136,7 +138,7 @@ export const ThreatIntelOverview: React.FC<ThreatIntelOverviewProps> = ({

searchSources();
getScanConfig();
}, [threatIntelService]);
}, [threatIntelService, dataSource.id]);

const nextStepClickHandlerById: Record<ThreatIntelNextStepId, MouseEventHandler> = {
['add-source']: addThreatIntelSourceActionHandler,
Expand Down
73 changes: 58 additions & 15 deletions public/security_analytics_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -37,21 +39,62 @@ export function renderApp(
ReactDOM.render(
<Router>
<Route
render={(props) => (
<DarkModeContext.Provider value={isDarkMode}>
<SecurityAnalyticsContext.Provider value={{ services, metrics }}>
<CoreServicesContext.Provider value={coreStart}>
<Main
{...props}
landingPage={landingPage}
multiDataSourceEnabled={!!depsStart.dataSource}
dataSourceManagement={dataSourceManagement}
setActionMenu={params.setHeaderActionMenu}
/>
</CoreServicesContext.Provider>
</SecurityAnalyticsContext.Provider>
</DarkModeContext.Provider>
)}
render={(props) => {
const originalMethods = {
push: props.history.push,
replace: props.history.replace,
};
const wrapper = (method: 'push' | 'replace') => (
...args: Parameters<History['push']>
) => {
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 (
<DarkModeContext.Provider value={isDarkMode}>
<SecurityAnalyticsContext.Provider value={{ services, metrics }}>
<CoreServicesContext.Provider value={coreStart}>
<Main
{...props}
landingPage={landingPage}
multiDataSourceEnabled={!!depsStart.dataSource}
dataSourceManagement={dataSourceManagement}
setActionMenu={params.setHeaderActionMenu}
/>
</CoreServicesContext.Provider>
</SecurityAnalyticsContext.Provider>
</DarkModeContext.Provider>
);
}}
/>
</Router>,
params.element
Expand Down
1 change: 0 additions & 1 deletion types/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down

0 comments on commit e55f421

Please sign in to comment.