From 47e9047ffae6d0585d6ef06c2dfec7f0a159f6af Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Fri, 13 Sep 2024 10:01:30 +0200 Subject: [PATCH] fix: location.query --- src/web/entities/container.jsx | 17 +- src/web/entities/withEntitiesContainer.jsx | 175 +++++++++--------- src/web/hooks/usePageFilter.js | 20 +- .../extras/__tests__/cvsscalculatorpage.jsx | 30 ++- .../pages/extras/cvssV4/CvssV4Calculator.jsx | 12 +- .../cvssV4/__tests__/cvssV4Calculator.jsx | 40 ++-- src/web/pages/extras/cvsscalculatorpage.jsx | 39 ++-- src/web/pages/omp.jsx | 7 +- src/web/pages/performance/performancepage.jsx | 9 +- src/web/utils/withRouter.jsx | 15 +- 10 files changed, 191 insertions(+), 173 deletions(-) diff --git a/src/web/entities/container.jsx b/src/web/entities/container.jsx index b05c809a84..b7871952bb 100644 --- a/src/web/entities/container.jsx +++ b/src/web/entities/container.jsx @@ -320,17 +320,18 @@ class EntitiesContainer extends React.Component { this.changeFilter(RESET_FILTER); } - handleFilterReset() { - const {navigate, location} = this.props; - const query = {...location.query}; + handleFilterReset = () => { + const {navigate, location, searchParams} = this.props; - // remove filter param from url - delete query.filter; + searchParams.delete('filter'); - navigate({pathname: location.pathname, query}); + navigate({ + pathname: location.pathname, + search: searchParams.toString(), + }); this.changeFilter(); - } + }; openTagDialog() { this.setState({tagDialogVisible: true}); @@ -582,6 +583,8 @@ EntitiesContainer.propTypes = { entitiesCounts: PropTypes.counts, entitiesError: PropTypes.error, filter: PropTypes.filter, + searchParams: PropTypes.object, + location: PropTypes.object, gmp: PropTypes.gmp.isRequired, gmpname: PropTypes.string.isRequired, navigate: PropTypes.object.isRequired, diff --git a/src/web/entities/withEntitiesContainer.jsx b/src/web/entities/withEntitiesContainer.jsx index 0db78be131..7f3f179db9 100644 --- a/src/web/entities/withEntitiesContainer.jsx +++ b/src/web/entities/withEntitiesContainer.jsx @@ -5,7 +5,7 @@ import React from 'react'; -import {useLocation} from 'react-router-dom'; +import {useSearchParams} from 'react-router-dom'; import {connect} from 'react-redux'; @@ -27,99 +27,98 @@ import EntitiesContainer from './container'; const noop = () => {}; -const withEntitiesContainer = ( - gmpname, - { - entitiesSelector, - loadEntities: loadEntitiesFunc, - reloadInterval = noop, - fallbackFilter, - }, -) => Component => { - let EntitiesContainerWrapper = ({ - children, - filter, - loadEntities, - notify, - ...props - }) => ( - reloadInterval(props)} - reload={(newFilter = filter) => loadEntities(newFilter)} - name={gmpname} - > - {({reload}) => ( - - {pageProps => } - - )} - - ); - - EntitiesContainerWrapper.propTypes = { - filter: PropTypes.filter, - loadEntities: PropTypes.func.isRequired, - notify: PropTypes.func.isRequired, - }; - - const mapStateToProps = (state, {filter}) => { - const eSelector = entitiesSelector(state); - const entities = eSelector.getEntities(filter); - return { - entities, - entitiesCounts: eSelector.getEntitiesCounts(filter), - entitiesError: eSelector.getEntitiesError(filter), +const withEntitiesContainer = + ( + gmpname, + { + entitiesSelector, + loadEntities: loadEntitiesFunc, + reloadInterval = noop, + fallbackFilter, + }, + ) => + Component => { + let EntitiesContainerWrapper = ({ + children, filter, - isLoading: eSelector.isLoadingEntities(filter), - loadedFilter: eSelector.getLoadedFilter(filter), - }; - }; - - const mapDispatchToProps = (dispatch, {gmp}) => ({ - loadEntities: filter => dispatch(loadEntitiesFunc(gmp)(filter)), - updateFilter: filter => dispatch(pageFilter(gmpname, filter)), - onInteraction: () => dispatch(renewSessionTimeout(gmp)()), - }); - - EntitiesContainerWrapper = compose( - withDialogNotification, - withDownload, - withGmp, - connect( - mapStateToProps, - mapDispatchToProps, - ), - )(EntitiesContainerWrapper); - - return props => { - const location = useLocation(); - return ( - - {({notify}) => ( - ( + reloadInterval(props)} + reload={(newFilter = filter) => loadEntities(newFilter)} + name={gmpname} + > + {({reload}) => ( + - {({filter}) => ( - - )} - + {pageProps => } + )} - + ); + + EntitiesContainerWrapper.propTypes = { + filter: PropTypes.filter, + loadEntities: PropTypes.func.isRequired, + notify: PropTypes.func.isRequired, + }; + + const mapStateToProps = (state, {filter}) => { + const eSelector = entitiesSelector(state); + const entities = eSelector.getEntities(filter); + return { + entities, + entitiesCounts: eSelector.getEntitiesCounts(filter), + entitiesError: eSelector.getEntitiesError(filter), + filter, + isLoading: eSelector.isLoadingEntities(filter), + loadedFilter: eSelector.getLoadedFilter(filter), + }; + }; + + const mapDispatchToProps = (dispatch, {gmp}) => ({ + loadEntities: filter => dispatch(loadEntitiesFunc(gmp)(filter)), + updateFilter: filter => dispatch(pageFilter(gmpname, filter)), + onInteraction: () => dispatch(renewSessionTimeout(gmp)()), + }); + + EntitiesContainerWrapper = compose( + withDialogNotification, + withDownload, + withGmp, + connect(mapStateToProps, mapDispatchToProps), + )(EntitiesContainerWrapper); + + return props => { + const [searchParams] = useSearchParams(); + return ( + + {({notify}) => ( + + {({filter}) => ( + + )} + + )} + + ); + }; }; -}; export default withEntitiesContainer; diff --git a/src/web/hooks/usePageFilter.js b/src/web/hooks/usePageFilter.js index 16f2f225cc..f38fd2d689 100644 --- a/src/web/hooks/usePageFilter.js +++ b/src/web/hooks/usePageFilter.js @@ -5,7 +5,7 @@ import {useCallback, useEffect, useState} from 'react'; -import {useLocation, useNavigate} from 'react-router-dom'; +import {useSearchParams} from 'react-router-dom'; import {useDispatch} from 'react-redux'; @@ -52,6 +52,7 @@ const useDefaultFilter = pageName => * still loading, function to change the filter, function to remove the * filter and function to reset the filter */ + const usePageFilter = ( pageName, gmpName, @@ -62,8 +63,7 @@ const usePageFilter = ( ) => { const gmp = useGmp(); const dispatch = useDispatch(); - const location = useLocation(); - const navigate = useNavigate(); + const [searchParams, setSearchParams] = useSearchParams(); const [defaultSettingFilter, defaultSettingsFilterError] = useDefaultFilter(gmpName); @@ -80,9 +80,9 @@ const usePageFilter = ( // use null as value for not set at all let returnedFilter; - // only use location directly if locationQueryFilterString is undefined + // only use searchParams directly if locationQueryFilterString is undefined const locationQueryFilterString = - initialLocationQueryFilterString || location?.query?.filter; + initialLocationQueryFilterString || searchParams.get('filter'); const [locationQueryFilter, setLocationQueryFilter] = useState( hasValue(locationQueryFilterString) @@ -163,15 +163,13 @@ const usePageFilter = ( }, [changeFilter]); const resetFilter = useCallback(() => { - const query = {...location.query}; - - // remove filter param from url - delete query.filter; + const query = new URLSearchParams(searchParams); + query.delete('filter'); - navigate({pathname: location.pathname, query}); + setSearchParams(query); changeFilter(); - }, [changeFilter, navigate, location]); + }, [changeFilter, setSearchParams, searchParams]); return [ returnedFilter, diff --git a/src/web/pages/extras/__tests__/cvsscalculatorpage.jsx b/src/web/pages/extras/__tests__/cvsscalculatorpage.jsx index 78092c03c9..6f9e5b325f 100644 --- a/src/web/pages/extras/__tests__/cvsscalculatorpage.jsx +++ b/src/web/pages/extras/__tests__/cvsscalculatorpage.jsx @@ -3,8 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - -import {describe, test, expect, testing} from '@gsa/testing'; +import {describe, test, expect, testing, beforeEach} from '@gsa/testing'; import {fireEvent, rendererWith, waitFor, wait} from 'web/utils/testing'; @@ -37,10 +36,14 @@ const location = { }; describe('CvssCalculator page tests', () => { + beforeEach(() => { + window.history.pushState({}, 'Test Title', '/'); + }); test('Should render with default values', () => { const {render} = rendererWith({ gmp, store: true, + router: true, }); const {element, getAllByTestId} = render(); @@ -75,14 +78,19 @@ describe('CvssCalculator page tests', () => { }); test('Should render userVector from url', async () => { + window.history.pushState( + {}, + 'Test Title', + `?cvssVector=AV:N/AC:L/Au:N/C:P/I:P/A:P`, + ); + const {render} = rendererWith({ gmp, store: true, + router: true, }); - const {element, getAllByTestId} = render( - , - ); + const {element, getAllByTestId} = render(); await wait(); @@ -117,6 +125,7 @@ describe('CvssCalculator page tests', () => { const {render} = rendererWith({ gmp, store: true, + router: true, }); const {element, getAllByTestId} = render( @@ -164,14 +173,19 @@ describe('CvssCalculator page tests', () => { }); test('Changing displayed select values should change userVector', async () => { + window.history.pushState( + {}, + 'Test Title', + `?cvssVector=AV:N/AC:L/Au:N/C:P/I:P/A:P`, + ); + const {render} = rendererWith({ gmp, store: true, + router: true, }); - const {element, getAllByTestId} = render( - , - ); + const {element, getAllByTestId} = render(); await wait(); diff --git a/src/web/pages/extras/cvssV4/CvssV4Calculator.jsx b/src/web/pages/extras/cvssV4/CvssV4Calculator.jsx index c75584b8f3..ceea5aa7c1 100644 --- a/src/web/pages/extras/cvssV4/CvssV4Calculator.jsx +++ b/src/web/pages/extras/cvssV4/CvssV4Calculator.jsx @@ -24,6 +24,7 @@ import SeverityBar from 'web/components/bar/severitybar'; import styled from 'styled-components'; import TextField from 'web/components/form/textfield'; import MetricsGroups from 'web/pages/extras/cvssV4/MetricsGroups'; +import {useSearchParams} from 'react-router-dom'; const StyledTextField = styled(TextField)` width: 180px; @@ -31,7 +32,9 @@ const StyledTextField = styled(TextField)` const cvssV4Prefix = 'CVSS:4.0/'; -const CvssV4Calculator = ({location}) => { +const CvssV4Calculator = () => { + const [searchParams] = useSearchParams(); + const initialState = useMemo(() => { return expectedMetricOptionsOrdered.reduce((obj, item) => { obj[item[0]] = item[1]; @@ -50,13 +53,14 @@ const CvssV4Calculator = ({location}) => { const cvssVector = `${cvssV4Prefix}${removeUnusedMetrics(selectedOptions)}`; useEffect(() => { - if (location?.query?.cvssVector?.includes(cvssV4Prefix)) { - const newOptions = processVector(location.query.cvssVector); + const cvssVectorParam = searchParams.get('cvssVector'); + if (cvssVectorParam?.includes(cvssV4Prefix)) { + const newOptions = processVector(cvssVectorParam); setSelectedOptions({...initialState, ...newOptions}); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [location]); + }, [searchParams]); const handleInputCVSSVectorChange = value => { setInputCVSSVector(value); diff --git a/src/web/pages/extras/cvssV4/__tests__/cvssV4Calculator.jsx b/src/web/pages/extras/cvssV4/__tests__/cvssV4Calculator.jsx index ebc129c008..6e9f124c90 100644 --- a/src/web/pages/extras/cvssV4/__tests__/cvssV4Calculator.jsx +++ b/src/web/pages/extras/cvssV4/__tests__/cvssV4Calculator.jsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import {describe, test, expect} from '@gsa/testing'; +import {describe, test, expect, beforeEach} from '@gsa/testing'; import {fireEvent, rendererWith, wait} from 'web/utils/testing'; import CvssV4Calculator from 'web/pages/extras/cvssV4/CvssV4Calculator'; @@ -12,22 +12,18 @@ const gmp = {}; const baseCVSSVector = 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N'; -const location = { - query: { - cvssVector: baseCVSSVector, - }, -}; - describe('CvssV4Calculator page tests', () => { + beforeEach(() => { + window.history.pushState({}, 'Test Title', '/'); + }); test('Should render with default values', async () => { const {render} = rendererWith({ gmp, store: true, + router: true, }); - const {getByText, within} = render( - , - ); + const {getByText, within} = render(); const cvssVectorEl = getByText('CVSS Base Vector'); const spanElement = within(cvssVectorEl.parentElement).getByText( @@ -38,24 +34,22 @@ describe('CvssV4Calculator page tests', () => { }); test('Should render userVector from url', async () => { - const locationModified = { - query: { - cvssVector: - 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N/CR:M/MSC:H', - }, - }; + const cvssVector = + 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N/CR:M/MSC:H'; + + window.history.pushState({}, 'Test Title', `?cvssVector=${cvssVector}`); + const {render} = rendererWith({ gmp, store: true, + router: true, }); - const {getByText, within} = render( - , - ); + const {getByText, within} = render(); const cvssVectorEl = getByText('CVSS Base Vector'); const spanElement = within(cvssVectorEl.parentElement).getByText( - locationModified.query.cvssVector, + cvssVector, ); expect(spanElement).toBeVisible(); @@ -65,10 +59,11 @@ describe('CvssV4Calculator page tests', () => { const {render} = rendererWith({ gmp, store: true, + router: true, }); const {getByText, within, element, getAllByTestId} = render( - , + , ); await wait(); @@ -102,10 +97,11 @@ describe('CvssV4Calculator page tests', () => { const {render} = rendererWith({ gmp, store: true, + router: true, }); const {element, getAllByTestId, getByText, within} = render( - , + , ); await wait(); diff --git a/src/web/pages/extras/cvsscalculatorpage.jsx b/src/web/pages/extras/cvsscalculatorpage.jsx index a7e1b16102..a5635ca675 100644 --- a/src/web/pages/extras/cvsscalculatorpage.jsx +++ b/src/web/pages/extras/cvsscalculatorpage.jsx @@ -4,7 +4,7 @@ */ import React, {useState, useEffect} from 'react'; -import PropTypes from 'prop-types'; +import {useSearchParams} from 'react-router-dom'; import styled from 'styled-components'; @@ -54,6 +54,7 @@ const ToolBarIcons = () => ( const CvssV2Calculator = props => { const [, renewSession] = useUserSessionTimeout(); + const [searchParams] = useSearchParams(); const [state, setState] = useState({ accessVector: 'Local', @@ -68,18 +69,15 @@ const CvssV2Calculator = props => { }); useEffect(() => { - const {location} = props; + const cvssVector = searchParams.get('cvssVector'); if ( - isDefined(location) && - isDefined(location.query) && - isDefined(location.query.cvssVector) + cvssVector && + !cvssVector.includes('CVSS:3') && + !cvssVector.includes('CVSS:4') ) { - const {cvssVector} = location.query; - if (!cvssVector.includes('CVSS:3')) { - setState(vals => ({...vals, cvssVector, userVector: cvssVector})); - handleVectorChange(); - } + setState(vals => ({...vals, cvssVector, userVector: cvssVector})); + handleVectorChange(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -351,6 +349,7 @@ const CvssV2Calculator = props => { const CvssV3Calculator = props => { const [, renewSession] = useUserSessionTimeout(); + const [searchParams] = useSearchParams(); const [state, setState] = useState({ attackVector: 'Network', @@ -367,18 +366,11 @@ const CvssV3Calculator = props => { }); useEffect(() => { - const {location} = props; + const cvssVector = searchParams.get('cvssVector'); - if ( - isDefined(location) && - isDefined(location.query) && - isDefined(location.query.cvssVector) - ) { - const {cvssVector} = location.query; - if (cvssVector.includes('CVSS:3')) { - setState(vals => ({...vals, cvssVector, userVector: cvssVector})); - handleVectorChange(); - } + if (cvssVector?.includes('CVSS:3')) { + setState(vals => ({...vals, cvssVector, userVector: cvssVector})); + handleVectorChange(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -706,14 +698,11 @@ const CvssCalculator = props => ( - + ); -CvssCalculator.propTypes = { - location: PropTypes.object.isRequired, -}; export default CvssCalculator; // vim: set ts=2 sw=2 tw=80: diff --git a/src/web/pages/omp.jsx b/src/web/pages/omp.jsx index 7dfe296a74..eb293b63a2 100644 --- a/src/web/pages/omp.jsx +++ b/src/web/pages/omp.jsx @@ -18,8 +18,10 @@ import PropTypes from 'web/utils/proptypes'; */ class LegacyOmpPage extends React.Component { componentDidMount() { - const {location, navigate} = this.props; - const {cmd, info_type, info_id = ''} = location.query; + const {navigate, searchParams} = this.props; + const cmd = searchParams.get('cmd'); + const info_type = searchParams.get('info_type'); + const info_id = searchParams.get('info_id') || ''; if (cmd !== 'get_info') { navigate('/notfound', {replace: true}); @@ -57,6 +59,7 @@ class LegacyOmpPage extends React.Component { LegacyOmpPage.propTypes = { navigate: PropTypes.object.isRequired, + searchParams: PropTypes.object.isRequired, }; export default withRouter(LegacyOmpPage); diff --git a/src/web/pages/performance/performancepage.jsx b/src/web/pages/performance/performancepage.jsx index 7c8edb172e..e0d5e57adf 100644 --- a/src/web/pages/performance/performancepage.jsx +++ b/src/web/pages/performance/performancepage.jsx @@ -52,6 +52,7 @@ import {renderSelectItems} from 'web/utils/render'; import {styledExcludeProps} from 'web/utils/styledConfig'; import StartEndTimeSelection from './startendtimeselection'; +import {withRouter} from 'web/utils/withRouter'; const DURATION_HOUR = 60 * 60; const DURATION_DAY = DURATION_HOUR * 24; @@ -174,8 +175,10 @@ class PerformancePage extends React.Component { } componentDidMount() { - const {start, end, scanner} = this.props.location.query; - const {gmp, timezone} = this.props; + const {gmp, timezone, searchParams} = this.props; + const start = searchParams.get('start'); + const end = searchParams.get('end'); + const scanner = searchParams.get('scanner'); gmp.performance.get().then(response => { this.setState({reports: response.data}); @@ -353,6 +356,7 @@ PerformancePage.propTypes = { scanners: PropTypes.arrayOf(PropTypes.model), timezone: PropTypes.string.isRequired, onInteraction: PropTypes.func.isRequired, + searchParams: PropTypes.object, }; const SENSOR_SCANNER_FILTER = Filter.fromString( @@ -376,6 +380,7 @@ const mapStateToProps = rootState => { export default compose( withGmp, + withRouter, connect(mapStateToProps, mapDispatchToProps), )(PerformancePage); diff --git a/src/web/utils/withRouter.jsx b/src/web/utils/withRouter.jsx index b97f0c0679..fc5d45aa49 100644 --- a/src/web/utils/withRouter.jsx +++ b/src/web/utils/withRouter.jsx @@ -3,13 +3,19 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import {useLocation, useNavigate, useParams} from 'react-router-dom'; +import { + useLocation, + useNavigate, + useParams, + useSearchParams, +} from 'react-router-dom'; export const withRouter = Component => { function ComponentWithRouterProp(props) { - let location = useLocation(); - let navigate = useNavigate(); - let params = useParams(); + const location = useLocation(); + const navigate = useNavigate(); + const params = useParams(); + const [searchParams] = useSearchParams(); return ( { location={location} navigate={navigate} params={params} + searchParams={searchParams} /> ); }