@parca/profile 0.16.408 → 0.16.410
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/ProfileMetricsGraph/index.d.ts +2 -4
- package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
- package/dist/ProfileMetricsGraph/index.js +31 -55
- package/dist/ProfileMetricsGraph/useSumBy.d.ts +1 -0
- package/dist/ProfileMetricsGraph/useSumBy.d.ts.map +1 -1
- package/dist/ProfileMetricsGraph/useSumBy.js +49 -6
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +1 -1
- package/package.json +3 -3
- package/src/ProfileMetricsGraph/index.tsx +71 -88
- package/src/ProfileMetricsGraph/useSumBy.ts +66 -6
- package/src/ProfileSelector/index.tsx +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [0.16.410](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.409...@parca/profile@0.16.410) (2024-07-11)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
10
|
+
## [0.16.409](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.408...@parca/profile@0.16.409) (2024-07-10)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @parca/profile
|
|
13
|
+
|
|
6
14
|
## [0.16.408](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.407...@parca/profile@0.16.408) (2024-07-09)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -12,7 +12,6 @@ interface ProfileMetricsGraphProps {
|
|
|
12
12
|
profile: ProfileSelection | null;
|
|
13
13
|
from: number;
|
|
14
14
|
to: number;
|
|
15
|
-
timeRange: DateTimeRange;
|
|
16
15
|
setTimeRange: (range: DateTimeRange) => void;
|
|
17
16
|
addLabelMatcher: (labels: {
|
|
18
17
|
key: string;
|
|
@@ -27,10 +26,9 @@ interface ProfileMetricsGraphProps {
|
|
|
27
26
|
export interface IQueryRangeState {
|
|
28
27
|
response: QueryRangeResponse | null;
|
|
29
28
|
isLoading: boolean;
|
|
30
|
-
isRefreshing?: boolean;
|
|
31
29
|
error: RpcError | null;
|
|
32
30
|
}
|
|
33
|
-
export declare const useQueryRange: (client: QueryServiceClient, queryExpression: string, start: number, end: number,
|
|
34
|
-
declare const ProfileMetricsGraph: ({ queryClient, queryExpression, profile, from, to, setTimeRange, addLabelMatcher, onPointClick, comparing,
|
|
31
|
+
export declare const useQueryRange: (client: QueryServiceClient, queryExpression: string, start: number, end: number, sumBy?: string[], skip?: boolean) => IQueryRangeState;
|
|
32
|
+
declare const ProfileMetricsGraph: ({ queryClient, queryExpression, profile, from, to, setTimeRange, addLabelMatcher, onPointClick, comparing, }: ProfileMetricsGraphProps) => JSX.Element;
|
|
35
33
|
export default ProfileMetricsGraph;
|
|
36
34
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileMetricsGraph/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAGlD,OAAO,EAAW,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAY,MAAM,eAAe,CAAC;AACjG,OAAO,EACL,aAAa,EAKd,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileMetricsGraph/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAGlD,OAAO,EAAW,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAY,MAAM,eAAe,CAAC;AACjG,OAAO,EACL,aAAa,EAKd,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAQ5D,UAAU,6BAA6B;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD,eAAO,MAAM,wBAAwB,gBAAe,6BAA6B,KAAG,GAAG,CAAC,OAMvF,CAAC;AAEF,UAAU,wBAAwB;IAChC,WAAW,EAAE,kBAAkB,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,eAAe,EAAE,CACf,MAAM,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,KACvE,IAAI,CAAC;IACV,YAAY,EAAE,CACZ,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,KAAK,EAAE,EACf,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,KACb,IAAI,CAAC;IACV,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;CACxB;AAYD,eAAO,MAAM,aAAa,WAChB,kBAAkB,mBACT,MAAM,SAChB,MAAM,OACR,MAAM,UACJ,MAAM,EAAE,qBAEd,gBA+CF,CAAC;AAEF,QAAA,MAAM,mBAAmB,iHAUtB,wBAAwB,KAAG,GAAG,CAAC,OA2GjC,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// Copyright 2022 The Parca Authors
|
|
3
3
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
// you may not use this file except in compliance with the License.
|
|
@@ -11,14 +11,18 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
11
11
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
// See the License for the specific language governing permissions and
|
|
13
13
|
// limitations under the License.
|
|
14
|
-
import { useEffect, useMemo
|
|
14
|
+
import { useEffect, useMemo } from 'react';
|
|
15
15
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
16
16
|
import { Duration, Timestamp } from '@parca/client';
|
|
17
17
|
import { MetricsGraphSkeleton, useGrpcMetadata, useParcaContext, useURLState, } from '@parca/components';
|
|
18
18
|
import { Query } from '@parca/parser';
|
|
19
19
|
import { capitalizeOnlyFirstLetter, getStepDuration } from '@parca/utilities';
|
|
20
|
+
import { useLabelNames } from '../MatchersInput';
|
|
20
21
|
import MetricsGraph from '../MetricsGraph';
|
|
21
22
|
import { useMetricsGraphDimensions } from '../MetricsGraph/useMetricsGraphDimensions';
|
|
23
|
+
import useGrpcQuery from '../useGrpcQuery';
|
|
24
|
+
import { Toolbar } from './Toolbar';
|
|
25
|
+
import { DEFAULT_EMPTY_SUM_BY, useSumBy } from './useSumBy';
|
|
22
26
|
const ErrorContent = ({ errorMessage }) => {
|
|
23
27
|
return (_jsx("div", { className: "relative rounded border border-red-400 bg-red-100 px-4 py-3 text-red-700", role: "alert", children: _jsx("span", { className: "block sm:inline", children: errorMessage }) }));
|
|
24
28
|
};
|
|
@@ -33,15 +37,7 @@ const getStepCountFromScreenWidth = (pixelsPerPoint) => {
|
|
|
33
37
|
width = width - (20 + 24 + 68) * 2;
|
|
34
38
|
return Math.round(width / pixelsPerPoint);
|
|
35
39
|
};
|
|
36
|
-
const
|
|
37
|
-
export const useQueryRange = (client, queryExpression, start, end, timeRange, sumBy = EMPTY_SUM_BY, skip = false) => {
|
|
38
|
-
const previousQueryParams = useRef({});
|
|
39
|
-
const [isLoading, setLoading] = useState(!skip);
|
|
40
|
-
const [state, setState] = useState({
|
|
41
|
-
response: null,
|
|
42
|
-
isLoading,
|
|
43
|
-
error: null,
|
|
44
|
-
});
|
|
40
|
+
export const useQueryRange = (client, queryExpression, start, end, sumBy = DEFAULT_EMPTY_SUM_BY, skip = false) => {
|
|
45
41
|
const metadata = useGrpcMetadata();
|
|
46
42
|
const { navigateTo } = useParcaContext();
|
|
47
43
|
const [stepCountStr, setStepCount] = useURLState({ param: 'step_count', navigateTo });
|
|
@@ -59,23 +55,11 @@ export const useQueryRange = (client, queryExpression, start, end, timeRange, su
|
|
|
59
55
|
setStepCount(defaultStepCount.toString());
|
|
60
56
|
}
|
|
61
57
|
}, [stepCountStr, defaultStepCount, setStepCount]);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
if (previousQueryParams.current.queryExpression !== queryExpression ||
|
|
68
|
-
previousQueryParams.current.timeRange !== timeRange) {
|
|
69
|
-
previousQueryParams.current.queryExpression = queryExpression;
|
|
70
|
-
previousQueryParams.current.timeRange = timeRange;
|
|
71
|
-
previousQueryParams.current.isRefresh = undefined;
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
previousQueryParams.current.isRefresh = true;
|
|
75
|
-
}
|
|
76
|
-
setLoading(true);
|
|
58
|
+
const { data, isLoading, error } = useGrpcQuery({
|
|
59
|
+
key: ['query-range', queryExpression, start, end, sumBy.join(','), stepCount, metadata],
|
|
60
|
+
queryFn: async () => {
|
|
77
61
|
const stepDuration = getStepDuration(start, end, stepCount);
|
|
78
|
-
const
|
|
62
|
+
const { response } = await client.queryRange({
|
|
79
63
|
query: queryExpression,
|
|
80
64
|
start: Timestamp.fromDate(new Date(start)),
|
|
81
65
|
end: Timestamp.fromDate(new Date(end)),
|
|
@@ -83,27 +67,20 @@ export const useQueryRange = (client, queryExpression, start, end, timeRange, su
|
|
|
83
67
|
limit: 0,
|
|
84
68
|
sumBy,
|
|
85
69
|
}, { meta: metadata });
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
})
|
|
96
|
-
.finally(() => {
|
|
97
|
-
if (previousQueryParams.current.isRefresh !== undefined) {
|
|
98
|
-
previousQueryParams.current.isRefresh = undefined;
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
})();
|
|
102
|
-
}, [client, queryExpression, start, end, metadata, timeRange, sumBy, skip, stepCount]);
|
|
103
|
-
return { ...state, isLoading, isRefreshing: previousQueryParams.current.isRefresh };
|
|
70
|
+
return response;
|
|
71
|
+
},
|
|
72
|
+
options: {
|
|
73
|
+
retry: false,
|
|
74
|
+
enabled: !skip && sumBy !== DEFAULT_EMPTY_SUM_BY,
|
|
75
|
+
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
return { isLoading, error: error, response: data ?? null };
|
|
104
79
|
};
|
|
105
|
-
const ProfileMetricsGraph = ({ queryClient, queryExpression, profile, from, to, setTimeRange, addLabelMatcher, onPointClick, comparing = false,
|
|
106
|
-
const {
|
|
80
|
+
const ProfileMetricsGraph = ({ queryClient, queryExpression, profile, from, to, setTimeRange, addLabelMatcher, onPointClick, comparing = false, }) => {
|
|
81
|
+
const { loading: labelNamesLoading, result: labelNamesResult } = useLabelNames(queryClient, profile?.ProfileSource()?.ProfileType()?.toString() ?? '', from, to);
|
|
82
|
+
const [sumBy, setSumBy] = useSumBy(profile?.ProfileSource()?.ProfileType(), labelNamesResult.response?.labelNames);
|
|
83
|
+
const { isLoading: metricsGraphLoading, response, error, } = useQueryRange(queryClient, queryExpression, from, to, sumBy, labelNamesLoading);
|
|
107
84
|
const { onError, perf, authenticationErrorMessage, isDarkMode } = useParcaContext();
|
|
108
85
|
const { width, height, margin, heightStyle } = useMetricsGraphDimensions(comparing);
|
|
109
86
|
useEffect(() => {
|
|
@@ -119,8 +96,9 @@ const ProfileMetricsGraph = ({ queryClient, queryExpression, profile, from, to,
|
|
|
119
96
|
}, [perf, response]);
|
|
120
97
|
const series = response?.series;
|
|
121
98
|
const dataAvailable = series !== null && series !== undefined && series?.length > 0;
|
|
122
|
-
|
|
123
|
-
|
|
99
|
+
const loading = metricsGraphLoading || labelNamesLoading;
|
|
100
|
+
if (!labelNamesLoading && labelNamesResult?.error?.message != null) {
|
|
101
|
+
return (_jsx(ErrorContent, { errorMessage: capitalizeOnlyFirstLetter(labelNamesResult.error.message) }));
|
|
124
102
|
}
|
|
125
103
|
if (!metricsGraphLoading && error !== null) {
|
|
126
104
|
if (authenticationErrorMessage !== undefined && error.code === 'UNAUTHENTICATED') {
|
|
@@ -128,19 +106,17 @@ const ProfileMetricsGraph = ({ queryClient, queryExpression, profile, from, to,
|
|
|
128
106
|
}
|
|
129
107
|
return _jsx(ErrorContent, { errorMessage: capitalizeOnlyFirstLetter(error.message) });
|
|
130
108
|
}
|
|
109
|
+
let sampleUnit = '';
|
|
131
110
|
if (dataAvailable) {
|
|
132
|
-
const handleSampleClick = (timestamp, _value, labels, duration) => {
|
|
133
|
-
onPointClick(timestamp, labels, queryExpression, duration);
|
|
134
|
-
};
|
|
135
|
-
let sampleUnit = '';
|
|
136
111
|
if (series.every((val, i, arr) => val?.sampleType?.unit === arr[0]?.sampleType?.unit)) {
|
|
137
112
|
sampleUnit = series[0]?.sampleType?.unit ?? '';
|
|
138
113
|
}
|
|
139
114
|
if (sampleUnit === '') {
|
|
140
115
|
sampleUnit = Query.parse(queryExpression).profileType().sampleUnit;
|
|
141
116
|
}
|
|
142
|
-
return (_jsx(AnimatePresence, { children: _jsx(motion.div, { className: "h-full w-full relative", initial: { display: 'none', opacity: 0 }, animate: { display: 'block', opacity: 1 }, transition: { duration: 0.5 }, children: _jsx(MetricsGraph, { data: series, from: from, to: to, profile: profile, setTimeRange: setTimeRange, onSampleClick: handleSampleClick, addLabelMatcher: addLabelMatcher, sampleUnit: sampleUnit, height: height, width: width, margin: margin }) }, "metrics-graph-loaded") }));
|
|
143
117
|
}
|
|
144
|
-
return _jsx(
|
|
118
|
+
return (_jsx(AnimatePresence, { children: _jsxs(motion.div, { className: "h-full w-full relative", initial: { display: 'none', opacity: 0 }, animate: { display: 'block', opacity: 1 }, transition: { duration: 0.5 }, children: [_jsx(Toolbar, { sumBy: sumBy, setSumBy: setSumBy, labels: labelNamesResult.response?.labelNames ?? [] }), loading ? (_jsx(MetricsGraphSkeleton, { heightStyle: heightStyle, isDarkMode: isDarkMode })) : dataAvailable ? (_jsx(MetricsGraph, { data: series, from: from, to: to, profile: profile, setTimeRange: setTimeRange, onSampleClick: (timestamp, _value, labels, duration) => {
|
|
119
|
+
onPointClick(timestamp, labels, queryExpression, duration);
|
|
120
|
+
}, addLabelMatcher: addLabelMatcher, sampleUnit: sampleUnit, height: height, width: width, margin: margin })) : (_jsx(ProfileMetricsEmptyState, { message: "No data found. Try a different query." }))] }, "metrics-graph-loaded") }));
|
|
145
121
|
};
|
|
146
122
|
export default ProfileMetricsGraph;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { ProfileType } from '@parca/parser';
|
|
2
|
+
export declare const DEFAULT_EMPTY_SUM_BY: string[];
|
|
2
3
|
export declare const useSumBy: (profileType: ProfileType | undefined, labels: string[] | undefined) => [string[], (labels: string[]) => void];
|
|
3
4
|
//# sourceMappingURL=useSumBy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSumBy.d.ts","sourceRoot":"","sources":["../../src/ProfileMetricsGraph/useSumBy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useSumBy.d.ts","sourceRoot":"","sources":["../../src/ProfileMetricsGraph/useSumBy.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,oBAAoB,EAAE,MAAM,EAAO,CAAC;AA6BjD,eAAO,MAAM,QAAQ,gBACN,WAAW,GAAG,SAAS,UAC5B,MAAM,EAAE,GAAG,SAAS,KAC3B,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CA+EvC,CAAC"}
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
|
-
import { useEffect, useState } from 'react';
|
|
14
|
-
|
|
13
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
14
|
+
import { useParcaContext, useURLState } from '@parca/components';
|
|
15
|
+
export const DEFAULT_EMPTY_SUM_BY = [];
|
|
15
16
|
const getDefaultSumBy = (profile, labels) => {
|
|
16
17
|
if (profile === undefined || labels === undefined) {
|
|
17
18
|
return undefined;
|
|
@@ -31,17 +32,59 @@ const getDefaultSumBy = (profile, labels) => {
|
|
|
31
32
|
return undefined;
|
|
32
33
|
};
|
|
33
34
|
export const useSumBy = (profileType, labels) => {
|
|
34
|
-
const
|
|
35
|
+
const { navigateTo } = useParcaContext();
|
|
36
|
+
const [userSelectedSumByParam, setUserSelectedSumByParam] = useURLState({
|
|
37
|
+
param: 'sum_by',
|
|
38
|
+
navigateTo,
|
|
39
|
+
});
|
|
40
|
+
console.log('labels', labels);
|
|
41
|
+
console.log('userSelectedSumByParam', userSelectedSumByParam);
|
|
42
|
+
const userSelectedSumBy = useMemo(() => {
|
|
43
|
+
if (userSelectedSumByParam?.length === 0) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
if (userSelectedSumByParam === '__none__') {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
if (typeof userSelectedSumByParam === 'string') {
|
|
50
|
+
return [userSelectedSumByParam];
|
|
51
|
+
}
|
|
52
|
+
return userSelectedSumByParam;
|
|
53
|
+
}, [userSelectedSumByParam]);
|
|
54
|
+
console.log('userSelectedSumBy', userSelectedSumBy);
|
|
55
|
+
const setUserSelectedSumBy = useCallback((sumBy) => {
|
|
56
|
+
if (sumBy.length === 0) {
|
|
57
|
+
setUserSelectedSumByParam('__none__');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (sumBy.length === 1) {
|
|
61
|
+
// Handle this separately to take care of the empty string scenario
|
|
62
|
+
setUserSelectedSumByParam(sumBy[0]);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
setUserSelectedSumByParam(sumBy);
|
|
66
|
+
}, [setUserSelectedSumByParam]);
|
|
35
67
|
const [defaultSumBy, setDefaultSumBy] = useState(getDefaultSumBy(profileType, labels));
|
|
36
68
|
useEffect(() => {
|
|
37
69
|
setDefaultSumBy(getDefaultSumBy(profileType, labels));
|
|
38
70
|
}, [profileType, labels]);
|
|
39
71
|
useEffect(() => {
|
|
40
|
-
if (profileType === undefined) {
|
|
72
|
+
if (profileType === undefined || labels === undefined) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (userSelectedSumBy !== undefined && userSelectedSumBy.length === 0) {
|
|
76
|
+
// User has explicitly selected no sumBy, so don't reset it
|
|
41
77
|
return;
|
|
42
78
|
}
|
|
79
|
+
if (userSelectedSumBy !== undefined && userSelectedSumBy.length > 0) {
|
|
80
|
+
// If any of the user selected sumBy is present in the labels, then don't reset it
|
|
81
|
+
if (userSelectedSumBy.some(sumBy => labels?.includes(sumBy))) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
43
85
|
// Reset user selected sumBy if profile type changes
|
|
44
|
-
setUserSelectedSumBy(
|
|
45
|
-
|
|
86
|
+
setUserSelectedSumBy(['']);
|
|
87
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
88
|
+
}, [profileType, labels]);
|
|
46
89
|
return [userSelectedSumBy ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY, setUserSelectedSumBy];
|
|
47
90
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAQ,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAY9E,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAO5D,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,WAAY,kBAAkB,KAAG,mBAkB5D,CAAC;AAEF,QAAA,MAAM,eAAe,6IAUlB,oBAAoB,KAAG,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAQ,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAY9E,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAO5D,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,WAAY,kBAAkB,KAAG,mBAkB5D,CAAC;AAEF,QAAA,MAAM,eAAe,6IAUlB,oBAAoB,KAAG,GAAG,CAAC,OA2P7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -149,7 +149,7 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
|
|
|
149
149
|
}, id: "h-matcher-search-button", children: "Search" }) })] }), _jsx("div", { children: comparing && _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) })] }), _jsx("div", { className: "rounded bg-white shadow dark:border-gray-500 dark:bg-gray-700", children: _jsx("div", { style: { height: heightStyle }, children: querySelection.expression !== undefined &&
|
|
150
150
|
querySelection.expression.length > 0 &&
|
|
151
151
|
querySelection.from !== undefined &&
|
|
152
|
-
querySelection.to !== undefined ? (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing,
|
|
152
|
+
querySelection.to !== undefined ? (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing, setTimeRange: (range) => {
|
|
153
153
|
const from = range.getFromMs();
|
|
154
154
|
const to = range.getToMs();
|
|
155
155
|
let mergedProfileParams = {};
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.410",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@headlessui/react": "^1.7.19",
|
|
7
7
|
"@iconify/react": "^4.0.0",
|
|
8
8
|
"@parca/client": "0.16.122",
|
|
9
|
-
"@parca/components": "0.16.
|
|
9
|
+
"@parca/components": "0.16.293",
|
|
10
10
|
"@parca/dynamicsize": "0.16.65",
|
|
11
11
|
"@parca/hooks": "0.0.67",
|
|
12
12
|
"@parca/icons": "0.16.70",
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"access": "public",
|
|
74
74
|
"registry": "https://registry.npmjs.org/"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "7fc4b0d3e5a29c4a79f8762185ec8b874dfdef2c"
|
|
77
77
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import {useEffect, useMemo
|
|
14
|
+
import {useEffect, useMemo} from 'react';
|
|
15
15
|
|
|
16
16
|
import {RpcError} from '@protobuf-ts/runtime-rpc';
|
|
17
17
|
import {AnimatePresence, motion} from 'framer-motion';
|
|
@@ -28,8 +28,12 @@ import {Query} from '@parca/parser';
|
|
|
28
28
|
import {capitalizeOnlyFirstLetter, getStepDuration} from '@parca/utilities';
|
|
29
29
|
|
|
30
30
|
import {MergedProfileSelection, ProfileSelection} from '..';
|
|
31
|
+
import {useLabelNames} from '../MatchersInput';
|
|
31
32
|
import MetricsGraph from '../MetricsGraph';
|
|
32
33
|
import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
|
|
34
|
+
import useGrpcQuery from '../useGrpcQuery';
|
|
35
|
+
import {Toolbar} from './Toolbar';
|
|
36
|
+
import {DEFAULT_EMPTY_SUM_BY, useSumBy} from './useSumBy';
|
|
33
37
|
|
|
34
38
|
interface ProfileMetricsEmptyStateProps {
|
|
35
39
|
message: string;
|
|
@@ -60,7 +64,6 @@ interface ProfileMetricsGraphProps {
|
|
|
60
64
|
profile: ProfileSelection | null;
|
|
61
65
|
from: number;
|
|
62
66
|
to: number;
|
|
63
|
-
timeRange: DateTimeRange;
|
|
64
67
|
setTimeRange: (range: DateTimeRange) => void;
|
|
65
68
|
addLabelMatcher: (
|
|
66
69
|
labels: {key: string; value: string} | Array<{key: string; value: string}>
|
|
@@ -77,7 +80,6 @@ interface ProfileMetricsGraphProps {
|
|
|
77
80
|
export interface IQueryRangeState {
|
|
78
81
|
response: QueryRangeResponse | null;
|
|
79
82
|
isLoading: boolean;
|
|
80
|
-
isRefreshing?: boolean;
|
|
81
83
|
error: RpcError | null;
|
|
82
84
|
}
|
|
83
85
|
|
|
@@ -91,32 +93,17 @@ const getStepCountFromScreenWidth = (pixelsPerPoint: number): number => {
|
|
|
91
93
|
return Math.round(width / pixelsPerPoint);
|
|
92
94
|
};
|
|
93
95
|
|
|
94
|
-
const EMPTY_SUM_BY: string[] = [];
|
|
95
|
-
|
|
96
96
|
export const useQueryRange = (
|
|
97
97
|
client: QueryServiceClient,
|
|
98
98
|
queryExpression: string,
|
|
99
99
|
start: number,
|
|
100
100
|
end: number,
|
|
101
|
-
|
|
102
|
-
sumBy: string[] = EMPTY_SUM_BY,
|
|
101
|
+
sumBy: string[] = DEFAULT_EMPTY_SUM_BY,
|
|
103
102
|
skip = false
|
|
104
103
|
): IQueryRangeState => {
|
|
105
|
-
const previousQueryParams = useRef<{
|
|
106
|
-
queryExpression?: string;
|
|
107
|
-
timeRange?: DateTimeRange;
|
|
108
|
-
isRefresh?: boolean;
|
|
109
|
-
}>({});
|
|
110
|
-
const [isLoading, setLoading] = useState<boolean>(!skip);
|
|
111
|
-
const [state, setState] = useState<IQueryRangeState>({
|
|
112
|
-
response: null,
|
|
113
|
-
isLoading,
|
|
114
|
-
error: null,
|
|
115
|
-
});
|
|
116
104
|
const metadata = useGrpcMetadata();
|
|
117
105
|
const {navigateTo} = useParcaContext();
|
|
118
106
|
const [stepCountStr, setStepCount] = useURLState({param: 'step_count', navigateTo});
|
|
119
|
-
|
|
120
107
|
const defaultStepCount = useMemo(() => {
|
|
121
108
|
return getStepCountFromScreenWidth(10);
|
|
122
109
|
}, []);
|
|
@@ -135,27 +122,11 @@ export const useQueryRange = (
|
|
|
135
122
|
}
|
|
136
123
|
}, [stepCountStr, defaultStepCount, setStepCount]);
|
|
137
124
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (
|
|
145
|
-
previousQueryParams.current.queryExpression !== queryExpression ||
|
|
146
|
-
previousQueryParams.current.timeRange !== timeRange
|
|
147
|
-
) {
|
|
148
|
-
previousQueryParams.current.queryExpression = queryExpression;
|
|
149
|
-
previousQueryParams.current.timeRange = timeRange;
|
|
150
|
-
previousQueryParams.current.isRefresh = undefined;
|
|
151
|
-
} else {
|
|
152
|
-
previousQueryParams.current.isRefresh = true;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
setLoading(true);
|
|
156
|
-
|
|
125
|
+
const {data, isLoading, error} = useGrpcQuery<QueryRangeResponse | undefined>({
|
|
126
|
+
key: ['query-range', queryExpression, start, end, sumBy.join(','), stepCount, metadata],
|
|
127
|
+
queryFn: async () => {
|
|
157
128
|
const stepDuration = getStepDuration(start, end, stepCount);
|
|
158
|
-
const
|
|
129
|
+
const {response} = await client.queryRange(
|
|
159
130
|
{
|
|
160
131
|
query: queryExpression,
|
|
161
132
|
start: Timestamp.fromDate(new Date(start)),
|
|
@@ -166,26 +137,16 @@ export const useQueryRange = (
|
|
|
166
137
|
},
|
|
167
138
|
{meta: metadata}
|
|
168
139
|
);
|
|
140
|
+
return response;
|
|
141
|
+
},
|
|
142
|
+
options: {
|
|
143
|
+
retry: false,
|
|
144
|
+
enabled: !skip && sumBy !== DEFAULT_EMPTY_SUM_BY,
|
|
145
|
+
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
146
|
+
},
|
|
147
|
+
});
|
|
169
148
|
|
|
170
|
-
|
|
171
|
-
.then(response => {
|
|
172
|
-
setState({response, isLoading: false, error: null});
|
|
173
|
-
setLoading(false);
|
|
174
|
-
return null;
|
|
175
|
-
})
|
|
176
|
-
.catch(error => {
|
|
177
|
-
setState({response: null, isLoading: false, error});
|
|
178
|
-
setLoading(false);
|
|
179
|
-
})
|
|
180
|
-
.finally(() => {
|
|
181
|
-
if (previousQueryParams.current.isRefresh !== undefined) {
|
|
182
|
-
previousQueryParams.current.isRefresh = undefined;
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
})();
|
|
186
|
-
}, [client, queryExpression, start, end, metadata, timeRange, sumBy, skip, stepCount]);
|
|
187
|
-
|
|
188
|
-
return {...state, isLoading, isRefreshing: previousQueryParams.current.isRefresh};
|
|
149
|
+
return {isLoading, error: error as RpcError | null, response: data ?? null};
|
|
189
150
|
};
|
|
190
151
|
|
|
191
152
|
const ProfileMetricsGraph = ({
|
|
@@ -198,13 +159,23 @@ const ProfileMetricsGraph = ({
|
|
|
198
159
|
addLabelMatcher,
|
|
199
160
|
onPointClick,
|
|
200
161
|
comparing = false,
|
|
201
|
-
timeRange,
|
|
202
162
|
}: ProfileMetricsGraphProps): JSX.Element => {
|
|
163
|
+
const {loading: labelNamesLoading, result: labelNamesResult} = useLabelNames(
|
|
164
|
+
queryClient,
|
|
165
|
+
profile?.ProfileSource()?.ProfileType()?.toString() ?? '',
|
|
166
|
+
from,
|
|
167
|
+
to
|
|
168
|
+
);
|
|
169
|
+
const [sumBy, setSumBy] = useSumBy(
|
|
170
|
+
profile?.ProfileSource()?.ProfileType(),
|
|
171
|
+
labelNamesResult.response?.labelNames
|
|
172
|
+
);
|
|
173
|
+
|
|
203
174
|
const {
|
|
204
175
|
isLoading: metricsGraphLoading,
|
|
205
176
|
response,
|
|
206
177
|
error,
|
|
207
|
-
} = useQueryRange(queryClient, queryExpression, from, to,
|
|
178
|
+
} = useQueryRange(queryClient, queryExpression, from, to, sumBy, labelNamesLoading);
|
|
208
179
|
const {onError, perf, authenticationErrorMessage, isDarkMode} = useParcaContext();
|
|
209
180
|
const {width, height, margin, heightStyle} = useMetricsGraphDimensions(comparing);
|
|
210
181
|
|
|
@@ -225,8 +196,12 @@ const ProfileMetricsGraph = ({
|
|
|
225
196
|
const series = response?.series;
|
|
226
197
|
const dataAvailable = series !== null && series !== undefined && series?.length > 0;
|
|
227
198
|
|
|
228
|
-
|
|
229
|
-
|
|
199
|
+
const loading = metricsGraphLoading || labelNamesLoading;
|
|
200
|
+
|
|
201
|
+
if (!labelNamesLoading && labelNamesResult?.error?.message != null) {
|
|
202
|
+
return (
|
|
203
|
+
<ErrorContent errorMessage={capitalizeOnlyFirstLetter(labelNamesResult.error.message)} />
|
|
204
|
+
);
|
|
230
205
|
}
|
|
231
206
|
|
|
232
207
|
if (!metricsGraphLoading && error !== null) {
|
|
@@ -237,52 +212,60 @@ const ProfileMetricsGraph = ({
|
|
|
237
212
|
return <ErrorContent errorMessage={capitalizeOnlyFirstLetter(error.message)} />;
|
|
238
213
|
}
|
|
239
214
|
|
|
240
|
-
|
|
241
|
-
const handleSampleClick = (
|
|
242
|
-
timestamp: number,
|
|
243
|
-
_value: number,
|
|
244
|
-
labels: Label[],
|
|
245
|
-
duration: number
|
|
246
|
-
): void => {
|
|
247
|
-
onPointClick(timestamp, labels, queryExpression, duration);
|
|
248
|
-
};
|
|
215
|
+
let sampleUnit = '';
|
|
249
216
|
|
|
250
|
-
|
|
217
|
+
if (dataAvailable) {
|
|
251
218
|
if (series.every((val, i, arr) => val?.sampleType?.unit === arr[0]?.sampleType?.unit)) {
|
|
252
219
|
sampleUnit = series[0]?.sampleType?.unit ?? '';
|
|
253
220
|
}
|
|
254
221
|
if (sampleUnit === '') {
|
|
255
222
|
sampleUnit = Query.parse(queryExpression).profileType().sampleUnit;
|
|
256
223
|
}
|
|
224
|
+
}
|
|
257
225
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
226
|
+
return (
|
|
227
|
+
<AnimatePresence>
|
|
228
|
+
<motion.div
|
|
229
|
+
className="h-full w-full relative"
|
|
230
|
+
key="metrics-graph-loaded"
|
|
231
|
+
initial={{display: 'none', opacity: 0}}
|
|
232
|
+
animate={{display: 'block', opacity: 1}}
|
|
233
|
+
transition={{duration: 0.5}}
|
|
234
|
+
>
|
|
235
|
+
<Toolbar
|
|
236
|
+
sumBy={sumBy}
|
|
237
|
+
setSumBy={setSumBy}
|
|
238
|
+
labels={labelNamesResult.response?.labelNames ?? []}
|
|
239
|
+
/>
|
|
240
|
+
{loading ? (
|
|
241
|
+
<MetricsGraphSkeleton heightStyle={heightStyle} isDarkMode={isDarkMode} />
|
|
242
|
+
) : dataAvailable ? (
|
|
267
243
|
<MetricsGraph
|
|
268
244
|
data={series}
|
|
269
245
|
from={from}
|
|
270
246
|
to={to}
|
|
271
247
|
profile={profile as MergedProfileSelection}
|
|
272
248
|
setTimeRange={setTimeRange}
|
|
273
|
-
onSampleClick={
|
|
249
|
+
onSampleClick={(
|
|
250
|
+
timestamp: number,
|
|
251
|
+
_value: number,
|
|
252
|
+
labels: Label[],
|
|
253
|
+
duration: number
|
|
254
|
+
): void => {
|
|
255
|
+
onPointClick(timestamp, labels, queryExpression, duration);
|
|
256
|
+
}}
|
|
274
257
|
addLabelMatcher={addLabelMatcher}
|
|
275
258
|
sampleUnit={sampleUnit}
|
|
276
259
|
height={height}
|
|
277
260
|
width={width}
|
|
278
261
|
margin={margin}
|
|
279
262
|
/>
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
263
|
+
) : (
|
|
264
|
+
<ProfileMetricsEmptyState message="No data found. Try a different query." />
|
|
265
|
+
)}
|
|
266
|
+
</motion.div>
|
|
267
|
+
</AnimatePresence>
|
|
268
|
+
);
|
|
286
269
|
};
|
|
287
270
|
|
|
288
271
|
export default ProfileMetricsGraph;
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import {useEffect, useState} from 'react';
|
|
14
|
+
import {useCallback, useEffect, useMemo, useState} from 'react';
|
|
15
15
|
|
|
16
|
+
import {useParcaContext, useURLState} from '@parca/components';
|
|
16
17
|
import {ProfileType} from '@parca/parser';
|
|
17
18
|
|
|
18
|
-
const DEFAULT_EMPTY_SUM_BY: string[] = [];
|
|
19
|
+
export const DEFAULT_EMPTY_SUM_BY: string[] = [];
|
|
19
20
|
|
|
20
21
|
const getDefaultSumBy = (
|
|
21
22
|
profile: ProfileType | undefined,
|
|
@@ -48,7 +49,52 @@ export const useSumBy = (
|
|
|
48
49
|
profileType: ProfileType | undefined,
|
|
49
50
|
labels: string[] | undefined
|
|
50
51
|
): [string[], (labels: string[]) => void] => {
|
|
51
|
-
const
|
|
52
|
+
const {navigateTo} = useParcaContext();
|
|
53
|
+
const [userSelectedSumByParam, setUserSelectedSumByParam] = useURLState({
|
|
54
|
+
param: 'sum_by',
|
|
55
|
+
navigateTo,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
console.log('labels', labels);
|
|
59
|
+
|
|
60
|
+
console.log('userSelectedSumByParam', userSelectedSumByParam);
|
|
61
|
+
|
|
62
|
+
const userSelectedSumBy = useMemo<string[] | undefined>(() => {
|
|
63
|
+
if (userSelectedSumByParam?.length === 0) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (userSelectedSumByParam === '__none__') {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (typeof userSelectedSumByParam === 'string') {
|
|
72
|
+
return [userSelectedSumByParam];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return userSelectedSumByParam;
|
|
76
|
+
}, [userSelectedSumByParam]);
|
|
77
|
+
|
|
78
|
+
console.log('userSelectedSumBy', userSelectedSumBy);
|
|
79
|
+
|
|
80
|
+
const setUserSelectedSumBy = useCallback(
|
|
81
|
+
(sumBy: string[]) => {
|
|
82
|
+
if (sumBy.length === 0) {
|
|
83
|
+
setUserSelectedSumByParam('__none__');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (sumBy.length === 1) {
|
|
88
|
+
// Handle this separately to take care of the empty string scenario
|
|
89
|
+
setUserSelectedSumByParam(sumBy[0]);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
setUserSelectedSumByParam(sumBy);
|
|
94
|
+
},
|
|
95
|
+
[setUserSelectedSumByParam]
|
|
96
|
+
);
|
|
97
|
+
|
|
52
98
|
const [defaultSumBy, setDefaultSumBy] = useState<string[] | undefined>(
|
|
53
99
|
getDefaultSumBy(profileType, labels)
|
|
54
100
|
);
|
|
@@ -58,13 +104,27 @@ export const useSumBy = (
|
|
|
58
104
|
}, [profileType, labels]);
|
|
59
105
|
|
|
60
106
|
useEffect(() => {
|
|
61
|
-
if (profileType === undefined) {
|
|
107
|
+
if (profileType === undefined || labels === undefined) {
|
|
62
108
|
return;
|
|
63
109
|
}
|
|
64
110
|
|
|
111
|
+
if (userSelectedSumBy !== undefined && userSelectedSumBy.length === 0) {
|
|
112
|
+
// User has explicitly selected no sumBy, so don't reset it
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (userSelectedSumBy !== undefined && userSelectedSumBy.length > 0) {
|
|
117
|
+
// If any of the user selected sumBy is present in the labels, then don't reset it
|
|
118
|
+
if (userSelectedSumBy.some(sumBy => labels?.includes(sumBy))) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
65
123
|
// Reset user selected sumBy if profile type changes
|
|
66
|
-
setUserSelectedSumBy(
|
|
67
|
-
|
|
124
|
+
setUserSelectedSumBy(['']);
|
|
125
|
+
|
|
126
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
127
|
+
}, [profileType, labels]);
|
|
68
128
|
|
|
69
129
|
return [userSelectedSumBy ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY, setUserSelectedSumBy];
|
|
70
130
|
};
|
|
@@ -288,7 +288,6 @@ const ProfileSelector = ({
|
|
|
288
288
|
to={querySelection.to}
|
|
289
289
|
profile={profileSelection}
|
|
290
290
|
comparing={comparing}
|
|
291
|
-
timeRange={timeRangeSelection}
|
|
292
291
|
setTimeRange={(range: DateTimeRange) => {
|
|
293
292
|
const from = range.getFromMs();
|
|
294
293
|
const to = range.getToMs();
|