From 6949c659af2f64ffd05cb8a8e2370cac50fc0a77 Mon Sep 17 00:00:00 2001 From: Palash Gupta Date: Thu, 20 Apr 2023 18:14:32 +0530 Subject: [PATCH] feat: resource attribute query is shared in the app navigation from sidebar (#2553) --- frontend/src/constants/query.ts | 4 +- .../MetricsApplication/Tabs/Overview.tsx | 20 +++++++--- .../container/MetricsApplication/Tabs/util.ts | 14 ++++--- .../MetricsApplication/TopOperationsTable.tsx | 12 ++---- frontend/src/container/MetricsTable/index.tsx | 18 ++++++--- frontend/src/container/SideNav/config.ts | 37 +++++++++++++++++++ frontend/src/container/SideNav/helper.test.ts | 34 +++++++++++++++++ frontend/src/container/SideNav/helper.ts | 10 +++++ frontend/src/container/SideNav/index.tsx | 14 +++++-- 9 files changed, 132 insertions(+), 31 deletions(-) create mode 100644 frontend/src/container/SideNav/helper.test.ts create mode 100644 frontend/src/container/SideNav/helper.ts diff --git a/frontend/src/constants/query.ts b/frontend/src/constants/query.ts index 29221e9f9c..35c1e2c2ca 100644 --- a/frontend/src/constants/query.ts +++ b/frontend/src/constants/query.ts @@ -1,5 +1,4 @@ -// eslint-disable-next-line @typescript-eslint/naming-convention -export enum METRICS_PAGE_QUERY_PARAM { +export enum QueryParams { interval = 'interval', startTime = 'startTime', endTime = 'endTime', @@ -12,4 +11,5 @@ export enum METRICS_PAGE_QUERY_PARAM { selectedTags = 'selectedTags', aggregationOption = 'aggregationOption', entity = 'entity', + resourceAttributes = 'resourceAttribute', } diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index f540a69896..64cb0ed49a 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -1,8 +1,10 @@ import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; import Graph from 'components/Graph'; -import { METRICS_PAGE_QUERY_PARAM } from 'constants/query'; +import { QueryParams } from 'constants/query'; import ROUTES from 'constants/routes'; import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder'; +import { routeConfig } from 'container/SideNav/config'; +import { getQueryString } from 'container/SideNav/helper'; import useResourceAttribute from 'hooks/useResourceAttribute'; import { convertRawQueriesToTraceSelectedTags, @@ -13,7 +15,7 @@ import { colors } from 'lib/getRandomColor'; import history from 'lib/history'; import React, { useCallback, useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { useParams } from 'react-router-dom'; +import { useLocation, useParams } from 'react-router-dom'; import { UpdateTimeInterval } from 'store/actions'; import { AppState } from 'store/reducers'; import { Widgets } from 'types/api/dashboard/getAll'; @@ -35,6 +37,7 @@ import { function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { const { servicename } = useParams<{ servicename?: string }>(); const [selectedTimeStamp, setSelectedTimeStamp] = useState(0); + const { search } = useLocation(); const handleSetTimeStamp = useCallback((selectTime: number) => { setSelectedTimeStamp(selectTime); @@ -122,14 +125,19 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { const currentTime = timestamp; const tPlusOne = timestamp + 60 * 1000; - const urlParams = new URLSearchParams(); - urlParams.set(METRICS_PAGE_QUERY_PARAM.startTime, currentTime.toString()); - urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString()); + const urlParams = new URLSearchParams(search); + urlParams.set(QueryParams.startTime, currentTime.toString()); + urlParams.set(QueryParams.endTime, tPlusOne.toString()); + + const avialableParams = routeConfig[ROUTES.TRACE]; + const queryString = getQueryString(avialableParams, urlParams); history.replace( `${ ROUTES.TRACE - }?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&isFilterExclude={"serviceName":false,"status":false}&userSelectedFilter={"serviceName":["${servicename}"],"status":["error"]}&spanAggregateCurrentPage=1`, + }?selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&isFilterExclude={"serviceName":false,"status":false}&userSelectedFilter={"serviceName":["${servicename}"],"status":["error"]}&spanAggregateCurrentPage=1&${queryString.join( + '', + )}`, ); }; diff --git a/frontend/src/container/MetricsApplication/Tabs/util.ts b/frontend/src/container/MetricsApplication/Tabs/util.ts index f6e78a6d6f..185df530e1 100644 --- a/frontend/src/container/MetricsApplication/Tabs/util.ts +++ b/frontend/src/container/MetricsApplication/Tabs/util.ts @@ -1,6 +1,8 @@ import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; -import { METRICS_PAGE_QUERY_PARAM } from 'constants/query'; +import { QueryParams } from 'constants/query'; import ROUTES from 'constants/routes'; +import { routeConfig } from 'container/SideNav/config'; +import { getQueryString } from 'container/SideNav/helper'; import history from 'lib/history'; import { IQueryBuilderTagFilterItems } from 'types/api/dashboard/getAll'; import { Tags } from 'types/reducer/trace'; @@ -31,16 +33,18 @@ export function onViewTracePopupClick({ const currentTime = timestamp; const tPlusOne = timestamp + 60 * 1000; - const urlParams = new URLSearchParams(); - urlParams.set(METRICS_PAGE_QUERY_PARAM.startTime, currentTime.toString()); - urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString()); + const urlParams = new URLSearchParams(window.location.search); + urlParams.set(QueryParams.startTime, currentTime.toString()); + urlParams.set(QueryParams.endTime, tPlusOne.toString()); + const avialableParams = routeConfig[ROUTES.TRACE]; + const queryString = getQueryString(avialableParams, urlParams); history.replace( `${ ROUTES.TRACE }?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&spanAggregateCurrentPage=1${ isExternalCall ? '&spanKind=3' : '' - }`, + }&${queryString.join('&')}`, ); }; } diff --git a/frontend/src/container/MetricsApplication/TopOperationsTable.tsx b/frontend/src/container/MetricsApplication/TopOperationsTable.tsx index 010a60c8e2..d47bc30c94 100644 --- a/frontend/src/container/MetricsApplication/TopOperationsTable.tsx +++ b/frontend/src/container/MetricsApplication/TopOperationsTable.tsx @@ -1,7 +1,7 @@ import { Tooltip, Typography } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import { ResizeTable } from 'components/ResizeTable'; -import { METRICS_PAGE_QUERY_PARAM } from 'constants/query'; +import { QueryParams } from 'constants/query'; import ROUTES from 'constants/routes'; import useResourceAttribute from 'hooks/useResourceAttribute'; import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils'; @@ -29,14 +29,8 @@ function TopOperationsTable(props: TopOperationsTableProps): JSX.Element { const handleOnClick = (operation: string): void => { const urlParams = new URLSearchParams(); const { servicename } = params; - urlParams.set( - METRICS_PAGE_QUERY_PARAM.startTime, - (minTime / 1000000).toString(), - ); - urlParams.set( - METRICS_PAGE_QUERY_PARAM.endTime, - (maxTime / 1000000).toString(), - ); + urlParams.set(QueryParams.startTime, (minTime / 1000000).toString()); + urlParams.set(QueryParams.endTime, (maxTime / 1000000).toString()); history.push( `${ diff --git a/frontend/src/container/MetricsTable/index.tsx b/frontend/src/container/MetricsTable/index.tsx index c815769f54..6e9b74f88a 100644 --- a/frontend/src/container/MetricsTable/index.tsx +++ b/frontend/src/container/MetricsTable/index.tsx @@ -11,6 +11,8 @@ import localStorageSet from 'api/browser/localstorage/set'; import { ResizeTable } from 'components/ResizeTable'; import { SKIP_ONBOARDING } from 'constants/onboarding'; import ROUTES from 'constants/routes'; +import { routeConfig } from 'container/SideNav/config'; +import { getQueryString } from 'container/SideNav/helper'; import React, { useCallback, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; import { Link, useLocation } from 'react-router-dom'; @@ -88,11 +90,17 @@ function Metrics(): JSX.Element { .toString() .toLowerCase() .includes(value.toString().toLowerCase()), - render: (text: string): JSX.Element => ( - - {text} - - ), + render: (metrics: string): JSX.Element => { + const urlParams = new URLSearchParams(search); + const avialableParams = routeConfig[ROUTES.SERVICE_METRICS]; + const queryString = getQueryString(avialableParams, urlParams); + + return ( + + {metrics} + + ); + }, }), [filterDropdown, FilterIcon, search], ); diff --git a/frontend/src/container/SideNav/config.ts b/frontend/src/container/SideNav/config.ts index 87cd2cff60..f14da541c7 100644 --- a/frontend/src/container/SideNav/config.ts +++ b/frontend/src/container/SideNav/config.ts @@ -1 +1,38 @@ +import { QueryParams } from 'constants/query'; +import ROUTES from 'constants/routes'; + export const styles = { background: '#1f1f1f' }; + +export const routeConfig: Record = { + [ROUTES.SERVICE_METRICS]: [QueryParams.resourceAttributes], + [ROUTES.SERVICE_MAP]: [QueryParams.resourceAttributes], + [ROUTES.ALL_ERROR]: [QueryParams.resourceAttributes], + [ROUTES.ALERTS_NEW]: [QueryParams.resourceAttributes], + [ROUTES.ALL_CHANNELS]: [QueryParams.resourceAttributes], + [ROUTES.ALL_DASHBOARD]: [QueryParams.resourceAttributes], + [ROUTES.APPLICATION]: [QueryParams.resourceAttributes], + [ROUTES.CHANNELS_EDIT]: [QueryParams.resourceAttributes], + [ROUTES.CHANNELS_NEW]: [QueryParams.resourceAttributes], + [ROUTES.DASHBOARD]: [QueryParams.resourceAttributes], + [ROUTES.DASHBOARD_WIDGET]: [QueryParams.resourceAttributes], + [ROUTES.EDIT_ALERTS]: [QueryParams.resourceAttributes], + [ROUTES.ERROR_DETAIL]: [QueryParams.resourceAttributes], + [ROUTES.HOME_PAGE]: [QueryParams.resourceAttributes], + [ROUTES.INSTRUMENTATION]: [QueryParams.resourceAttributes], + [ROUTES.LIST_ALL_ALERT]: [QueryParams.resourceAttributes], + [ROUTES.LIST_LICENSES]: [QueryParams.resourceAttributes], + [ROUTES.LOGIN]: [QueryParams.resourceAttributes], + [ROUTES.LOGS]: [QueryParams.resourceAttributes], + [ROUTES.MY_SETTINGS]: [QueryParams.resourceAttributes], + [ROUTES.NOT_FOUND]: [QueryParams.resourceAttributes], + [ROUTES.ORG_SETTINGS]: [QueryParams.resourceAttributes], + [ROUTES.PASSWORD_RESET]: [QueryParams.resourceAttributes], + [ROUTES.SETTINGS]: [QueryParams.resourceAttributes], + [ROUTES.SIGN_UP]: [QueryParams.resourceAttributes], + [ROUTES.SOMETHING_WENT_WRONG]: [QueryParams.resourceAttributes], + [ROUTES.TRACE]: [QueryParams.resourceAttributes], + [ROUTES.TRACE_DETAIL]: [QueryParams.resourceAttributes], + [ROUTES.UN_AUTHORIZED]: [QueryParams.resourceAttributes], + [ROUTES.USAGE_EXPLORER]: [QueryParams.resourceAttributes], + [ROUTES.VERSION]: [QueryParams.resourceAttributes], +}; diff --git a/frontend/src/container/SideNav/helper.test.ts b/frontend/src/container/SideNav/helper.test.ts new file mode 100644 index 0000000000..ada551242e --- /dev/null +++ b/frontend/src/container/SideNav/helper.test.ts @@ -0,0 +1,34 @@ +import { getQueryString } from './helper'; + +describe('getQueryString', () => { + it('returns an array of query strings for given available parameters and URLSearchParams', () => { + const availableParams = ['param1', 'param2', 'param3']; + const params = new URLSearchParams( + 'param1=value1¶m2=value2¶m4=value4', + ); + + const result = getQueryString(availableParams, params); + + expect(result).toEqual(['param1=value1', 'param2=value2', '']); + }); + + it('returns an array of empty strings if no matching parameters are found', () => { + const availableParams = ['param1', 'param2', 'param3']; + const params = new URLSearchParams('param4=value4¶m5=value5'); + + const result = getQueryString(availableParams, params); + + expect(result).toEqual(['', '', '']); + }); + + it('returns an empty array if the available parameters list is empty', () => { + const availableParams: string[] = []; + const params = new URLSearchParams( + 'param1=value1¶m2=value2¶m3=value3', + ); + + const result = getQueryString(availableParams, params); + + expect(result).toEqual([]); + }); +}); diff --git a/frontend/src/container/SideNav/helper.ts b/frontend/src/container/SideNav/helper.ts new file mode 100644 index 0000000000..6160702336 --- /dev/null +++ b/frontend/src/container/SideNav/helper.ts @@ -0,0 +1,10 @@ +export const getQueryString = ( + avialableParams: string[], + params: URLSearchParams, +): string[] => + avialableParams.map((param) => { + if (params.has(param)) { + return `${param}=${params.get(param)}`; + } + return ''; + }); diff --git a/frontend/src/container/SideNav/index.tsx b/frontend/src/container/SideNav/index.tsx index acd8d5595e..d0e7fced95 100644 --- a/frontend/src/container/SideNav/index.tsx +++ b/frontend/src/container/SideNav/index.tsx @@ -12,7 +12,8 @@ import { SideBarCollapse } from 'store/actions/app'; import { AppState } from 'store/reducers'; import AppReducer from 'types/reducer/app'; -import { styles } from './config'; +import { routeConfig, styles } from './config'; +import { getQueryString } from './helper'; import menus from './menuItems'; import Slack from './Slack'; import { @@ -34,7 +35,7 @@ function SideNav(): JSX.Element { AppReducer >((state) => state.app); - const { pathname } = useLocation(); + const { pathname, search } = useLocation(); const { t } = useTranslation(''); const onCollapse = useCallback(() => { @@ -47,11 +48,16 @@ function SideNav(): JSX.Element { const onClickHandler = useCallback( (to: string) => { + const params = new URLSearchParams(search); + const avialableParams = routeConfig[to]; + + const queryString = getQueryString(avialableParams, params); + if (pathname !== to) { - history.push(`${to}`); + history.push(`${to}?${queryString.join('&')}`); } }, - [pathname], + [pathname, search], ); const onClickSlackHandler = (): void => {