feat: resource attribute query is shared in the app navigation from sidebar (#2553)

This commit is contained in:
Palash Gupta 2023-04-20 18:14:32 +05:30 committed by GitHub
parent 6c11c6d4da
commit 6949c659af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 132 additions and 31 deletions

View File

@ -1,5 +1,4 @@
// eslint-disable-next-line @typescript-eslint/naming-convention export enum QueryParams {
export enum METRICS_PAGE_QUERY_PARAM {
interval = 'interval', interval = 'interval',
startTime = 'startTime', startTime = 'startTime',
endTime = 'endTime', endTime = 'endTime',
@ -12,4 +11,5 @@ export enum METRICS_PAGE_QUERY_PARAM {
selectedTags = 'selectedTags', selectedTags = 'selectedTags',
aggregationOption = 'aggregationOption', aggregationOption = 'aggregationOption',
entity = 'entity', entity = 'entity',
resourceAttributes = 'resourceAttribute',
} }

View File

@ -1,8 +1,10 @@
import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
import Graph from 'components/Graph'; import Graph from 'components/Graph';
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query'; import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';
import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder'; 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 useResourceAttribute from 'hooks/useResourceAttribute';
import { import {
convertRawQueriesToTraceSelectedTags, convertRawQueriesToTraceSelectedTags,
@ -13,7 +15,7 @@ import { colors } from 'lib/getRandomColor';
import history from 'lib/history'; import history from 'lib/history';
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux'; 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 { UpdateTimeInterval } from 'store/actions';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
@ -35,6 +37,7 @@ import {
function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
const { servicename } = useParams<{ servicename?: string }>(); const { servicename } = useParams<{ servicename?: string }>();
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0); const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
const { search } = useLocation();
const handleSetTimeStamp = useCallback((selectTime: number) => { const handleSetTimeStamp = useCallback((selectTime: number) => {
setSelectedTimeStamp(selectTime); setSelectedTimeStamp(selectTime);
@ -122,14 +125,19 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
const currentTime = timestamp; const currentTime = timestamp;
const tPlusOne = timestamp + 60 * 1000; const tPlusOne = timestamp + 60 * 1000;
const urlParams = new URLSearchParams(); const urlParams = new URLSearchParams(search);
urlParams.set(METRICS_PAGE_QUERY_PARAM.startTime, currentTime.toString()); urlParams.set(QueryParams.startTime, currentTime.toString());
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString()); urlParams.set(QueryParams.endTime, tPlusOne.toString());
const avialableParams = routeConfig[ROUTES.TRACE];
const queryString = getQueryString(avialableParams, urlParams);
history.replace( history.replace(
`${ `${
ROUTES.TRACE 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(
'',
)}`,
); );
}; };

View File

@ -1,6 +1,8 @@
import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; 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 ROUTES from 'constants/routes';
import { routeConfig } from 'container/SideNav/config';
import { getQueryString } from 'container/SideNav/helper';
import history from 'lib/history'; import history from 'lib/history';
import { IQueryBuilderTagFilterItems } from 'types/api/dashboard/getAll'; import { IQueryBuilderTagFilterItems } from 'types/api/dashboard/getAll';
import { Tags } from 'types/reducer/trace'; import { Tags } from 'types/reducer/trace';
@ -31,16 +33,18 @@ export function onViewTracePopupClick({
const currentTime = timestamp; const currentTime = timestamp;
const tPlusOne = timestamp + 60 * 1000; const tPlusOne = timestamp + 60 * 1000;
const urlParams = new URLSearchParams(); const urlParams = new URLSearchParams(window.location.search);
urlParams.set(METRICS_PAGE_QUERY_PARAM.startTime, currentTime.toString()); urlParams.set(QueryParams.startTime, currentTime.toString());
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString()); urlParams.set(QueryParams.endTime, tPlusOne.toString());
const avialableParams = routeConfig[ROUTES.TRACE];
const queryString = getQueryString(avialableParams, urlParams);
history.replace( history.replace(
`${ `${
ROUTES.TRACE 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${ }?${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' : '' isExternalCall ? '&spanKind=3' : ''
}`, }&${queryString.join('&')}`,
); );
}; };
} }

View File

@ -1,7 +1,7 @@
import { Tooltip, Typography } from 'antd'; import { Tooltip, Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table'; import { ColumnsType } from 'antd/lib/table';
import { ResizeTable } from 'components/ResizeTable'; import { ResizeTable } from 'components/ResizeTable';
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query'; import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';
import useResourceAttribute from 'hooks/useResourceAttribute'; import useResourceAttribute from 'hooks/useResourceAttribute';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils'; import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
@ -29,14 +29,8 @@ function TopOperationsTable(props: TopOperationsTableProps): JSX.Element {
const handleOnClick = (operation: string): void => { const handleOnClick = (operation: string): void => {
const urlParams = new URLSearchParams(); const urlParams = new URLSearchParams();
const { servicename } = params; const { servicename } = params;
urlParams.set( urlParams.set(QueryParams.startTime, (minTime / 1000000).toString());
METRICS_PAGE_QUERY_PARAM.startTime, urlParams.set(QueryParams.endTime, (maxTime / 1000000).toString());
(minTime / 1000000).toString(),
);
urlParams.set(
METRICS_PAGE_QUERY_PARAM.endTime,
(maxTime / 1000000).toString(),
);
history.push( history.push(
`${ `${

View File

@ -11,6 +11,8 @@ import localStorageSet from 'api/browser/localstorage/set';
import { ResizeTable } from 'components/ResizeTable'; import { ResizeTable } from 'components/ResizeTable';
import { SKIP_ONBOARDING } from 'constants/onboarding'; import { SKIP_ONBOARDING } from 'constants/onboarding';
import ROUTES from 'constants/routes'; 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 React, { useCallback, useMemo, useState } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { Link, useLocation } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
@ -88,11 +90,17 @@ function Metrics(): JSX.Element {
.toString() .toString()
.toLowerCase() .toLowerCase()
.includes(value.toString().toLowerCase()), .includes(value.toString().toLowerCase()),
render: (text: string): JSX.Element => ( render: (metrics: string): JSX.Element => {
<Link to={`${ROUTES.APPLICATION}/${text}${search}`}> const urlParams = new URLSearchParams(search);
<Name>{text}</Name> const avialableParams = routeConfig[ROUTES.SERVICE_METRICS];
</Link> const queryString = getQueryString(avialableParams, urlParams);
),
return (
<Link to={`${ROUTES.APPLICATION}/${metrics}?${queryString.join('')}`}>
<Name>{metrics}</Name>
</Link>
);
},
}), }),
[filterDropdown, FilterIcon, search], [filterDropdown, FilterIcon, search],
); );

View File

@ -1 +1,38 @@
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
export const styles = { background: '#1f1f1f' }; export const styles = { background: '#1f1f1f' };
export const routeConfig: Record<string, QueryParams[]> = {
[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],
};

View File

@ -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&param2=value2&param4=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&param5=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&param2=value2&param3=value3',
);
const result = getQueryString(availableParams, params);
expect(result).toEqual([]);
});
});

View File

@ -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 '';
});

View File

@ -12,7 +12,8 @@ import { SideBarCollapse } from 'store/actions/app';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app'; import AppReducer from 'types/reducer/app';
import { styles } from './config'; import { routeConfig, styles } from './config';
import { getQueryString } from './helper';
import menus from './menuItems'; import menus from './menuItems';
import Slack from './Slack'; import Slack from './Slack';
import { import {
@ -34,7 +35,7 @@ function SideNav(): JSX.Element {
AppReducer AppReducer
>((state) => state.app); >((state) => state.app);
const { pathname } = useLocation(); const { pathname, search } = useLocation();
const { t } = useTranslation(''); const { t } = useTranslation('');
const onCollapse = useCallback(() => { const onCollapse = useCallback(() => {
@ -47,11 +48,16 @@ function SideNav(): JSX.Element {
const onClickHandler = useCallback( const onClickHandler = useCallback(
(to: string) => { (to: string) => {
const params = new URLSearchParams(search);
const avialableParams = routeConfig[to];
const queryString = getQueryString(avialableParams, params);
if (pathname !== to) { if (pathname !== to) {
history.push(`${to}`); history.push(`${to}?${queryString.join('&')}`);
} }
}, },
[pathname], [pathname, search],
); );
const onClickSlackHandler = (): void => { const onClickSlackHandler = (): void => {