@parca/profile 0.16.414 → 0.16.416
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/ProfileExplorer/index.d.ts.map +1 -1
- package/dist/ProfileExplorer/index.js +27 -8
- package/dist/ProfileMetricsGraph/index.js +1 -1
- package/dist/ProfileSelector/index.d.ts +1 -1
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +29 -17
- package/dist/ProfileSelector/useAutoQuerySelector.d.ts +2 -1
- package/dist/ProfileSelector/useAutoQuerySelector.d.ts.map +1 -1
- package/dist/ProfileSelector/useAutoQuerySelector.js +8 -3
- package/dist/useSumBy.d.ts +8 -5
- package/dist/useSumBy.d.ts.map +1 -1
- package/dist/useSumBy.js +58 -41
- package/package.json +2 -2
- package/src/ProfileExplorer/index.tsx +34 -8
- package/src/ProfileMetricsGraph/index.tsx +1 -1
- package/src/ProfileSelector/index.tsx +39 -28
- package/src/ProfileSelector/useAutoQuerySelector.ts +9 -2
- package/src/useSumBy.ts +80 -53
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.416](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.415...@parca/profile@0.16.416) (2024-07-23)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
10
|
+
## [0.16.415](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.414...@parca/profile@0.16.415) (2024-07-23)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @parca/profile
|
|
13
|
+
|
|
6
14
|
## [0.16.414](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.413...@parca/profile@0.16.414) (2024-07-22)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAGjD,OAAO,EAA4B,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAGjD,OAAO,EAA4B,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAQlF,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,WAAW,EAAE,GAAG,CAAC;IACjB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAaD,eAAO,MAAM,sBAAsB,eAAgB,MAAM,GAAG,EAAE,KAAG,MAGhE,CAAC;AAyWF,QAAA,MAAM,eAAe,8CAIlB,oBAAoB,KAAG,GAAG,CAAC,OAkB7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -18,6 +18,7 @@ import { createStore } from '@parca/store';
|
|
|
18
18
|
import { capitalizeOnlyFirstLetter } from '@parca/utilities';
|
|
19
19
|
import { ProfileSelectionFromParams, SuffixParams } from '..';
|
|
20
20
|
import { useProfileTypes } from '../ProfileSelector';
|
|
21
|
+
import { sumByToParam, useSumByFromParams } from '../useSumBy';
|
|
21
22
|
import ProfileExplorerCompare from './ProfileExplorerCompare';
|
|
22
23
|
import ProfileExplorerSingle from './ProfileExplorerSingle';
|
|
23
24
|
const ErrorContent = ({ errorMessage }) => {
|
|
@@ -38,6 +39,19 @@ const sanitizeDateRange = (time_selection_a, from_a, to_a) => {
|
|
|
38
39
|
return { time_selection_a: range.getRangeKey(), from_a, to_a };
|
|
39
40
|
};
|
|
40
41
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
42
|
+
const filterEmptyParams = (o) => {
|
|
43
|
+
return Object.fromEntries(Object.entries(o)
|
|
44
|
+
.filter(([_, value]) => value !== '' && value !== undefined && (Array.isArray(value) ? value.length > 0 : true))
|
|
45
|
+
.map(([key, value]) => {
|
|
46
|
+
if (typeof value === 'string') {
|
|
47
|
+
return [key, value];
|
|
48
|
+
}
|
|
49
|
+
if (Array.isArray(value)) {
|
|
50
|
+
return [key, value];
|
|
51
|
+
}
|
|
52
|
+
return [key, value];
|
|
53
|
+
}));
|
|
54
|
+
};
|
|
41
55
|
const filterSuffix = (o, suffix) => Object.fromEntries(Object.entries(o)
|
|
42
56
|
.filter(([key]) => !key.endsWith(suffix))
|
|
43
57
|
.map(([key, value]) => {
|
|
@@ -78,6 +92,8 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
|
|
|
78
92
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
79
93
|
const [profileA, setProfileA] = useState(null);
|
|
80
94
|
const [profileB, setProfileB] = useState(null);
|
|
95
|
+
const sumByA = useSumByFromParams(sum_by_a);
|
|
96
|
+
const sumByB = useSumByFromParams(sum_by_b);
|
|
81
97
|
useEffect(() => {
|
|
82
98
|
const mergeFrom = merge_from_a ?? undefined;
|
|
83
99
|
const mergeTo = merge_to_a ?? undefined;
|
|
@@ -134,7 +150,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
|
|
|
134
150
|
from: parseInt(from_a),
|
|
135
151
|
to: parseInt(to_a),
|
|
136
152
|
timeSelection: time_selection_a,
|
|
137
|
-
sumBy:
|
|
153
|
+
sumBy: sumByA,
|
|
138
154
|
};
|
|
139
155
|
// Show the SingleProfileExplorer when not comparing
|
|
140
156
|
if (compare_a !== 'true' && compare_b !== 'true') {
|
|
@@ -149,17 +165,18 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
|
|
|
149
165
|
return navigateTo('/',
|
|
150
166
|
// Filtering the _a suffix causes us to reset potential profile
|
|
151
167
|
// selection when running a new query.
|
|
152
|
-
{
|
|
168
|
+
filterEmptyParams({
|
|
153
169
|
...filterSuffix(queryParams, '_a'),
|
|
154
170
|
...{
|
|
155
171
|
expression_a: encodeURIComponent(q.expression),
|
|
156
172
|
from_a: q.from.toString(),
|
|
157
173
|
to_a: q.to.toString(),
|
|
158
174
|
time_selection_a: q.timeSelection,
|
|
175
|
+
sum_by_a: sumByToParam(q.sumBy),
|
|
159
176
|
dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
|
|
160
177
|
...mergeParams,
|
|
161
178
|
},
|
|
162
|
-
});
|
|
179
|
+
}));
|
|
163
180
|
};
|
|
164
181
|
const selectProfile = (p) => {
|
|
165
182
|
queryParams.expression_a = encodeURIComponent(queryParams.expression_a);
|
|
@@ -176,7 +193,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
|
|
|
176
193
|
from: parseInt(from_b),
|
|
177
194
|
to: parseInt(to_b),
|
|
178
195
|
timeSelection: time_selection_b,
|
|
179
|
-
sumBy:
|
|
196
|
+
sumBy: sumByB,
|
|
180
197
|
};
|
|
181
198
|
const selectQueryA = (q) => {
|
|
182
199
|
const mergeParams = q.mergeFrom !== undefined && q.mergeTo !== undefined
|
|
@@ -189,7 +206,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
|
|
|
189
206
|
return navigateTo('/',
|
|
190
207
|
// Filtering the _a suffix causes us to reset potential profile
|
|
191
208
|
// selection when running a new query.
|
|
192
|
-
{
|
|
209
|
+
filterEmptyParams({
|
|
193
210
|
...filterSuffix(queryParams, '_a'),
|
|
194
211
|
...{
|
|
195
212
|
compare_a: 'true',
|
|
@@ -199,11 +216,12 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
|
|
|
199
216
|
from_a: q.from.toString(),
|
|
200
217
|
to_a: q.to.toString(),
|
|
201
218
|
time_selection_a: q.timeSelection,
|
|
219
|
+
sum_by_a: sumByToParam(q.sumBy),
|
|
202
220
|
filter_by_function: filter_by_function ?? '',
|
|
203
221
|
dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
|
|
204
222
|
...mergeParams,
|
|
205
223
|
},
|
|
206
|
-
});
|
|
224
|
+
}));
|
|
207
225
|
};
|
|
208
226
|
const selectQueryB = (q) => {
|
|
209
227
|
const mergeParams = q.mergeFrom !== undefined && q.mergeTo !== undefined
|
|
@@ -216,7 +234,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
|
|
|
216
234
|
return navigateTo('/',
|
|
217
235
|
// Filtering the _b suffix causes us to reset potential profile
|
|
218
236
|
// selection when running a new query.
|
|
219
|
-
{
|
|
237
|
+
filterEmptyParams({
|
|
220
238
|
...filterSuffix(queryParams, '_b'),
|
|
221
239
|
...{
|
|
222
240
|
compare_b: 'true',
|
|
@@ -226,11 +244,12 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
|
|
|
226
244
|
from_b: q.from.toString(),
|
|
227
245
|
to_b: q.to.toString(),
|
|
228
246
|
time_selection_b: q.timeSelection,
|
|
247
|
+
sum_by_b: sumByToParam(q.sumBy),
|
|
229
248
|
filter_by_function: filter_by_function ?? '',
|
|
230
249
|
dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
|
|
231
250
|
...mergeParams,
|
|
232
251
|
},
|
|
233
|
-
});
|
|
252
|
+
}));
|
|
234
253
|
};
|
|
235
254
|
const closeProfile = (card) => {
|
|
236
255
|
let newQueryParameters = queryParams;
|
|
@@ -53,7 +53,7 @@ export const useQueryRange = (client, queryExpression, start, end, sumBy, skip =
|
|
|
53
53
|
}
|
|
54
54
|
}, [stepCountStr, defaultStepCount, setStepCount]);
|
|
55
55
|
const { data, isLoading, error } = useGrpcQuery({
|
|
56
|
-
key: ['query-range', queryExpression, start, end, sumBy.join(','), stepCount, metadata],
|
|
56
|
+
key: ['query-range', queryExpression, start, end, (sumBy ?? []).join(','), stepCount, metadata],
|
|
57
57
|
queryFn: async () => {
|
|
58
58
|
const stepDuration = getStepDuration(start, end, stepCount);
|
|
59
59
|
const { response } = await client.queryRange({
|
|
@@ -29,6 +29,6 @@ export interface IProfileTypesResult {
|
|
|
29
29
|
error?: RpcError;
|
|
30
30
|
}
|
|
31
31
|
export declare const useProfileTypes: (client: QueryServiceClient) => IProfileTypesResult;
|
|
32
|
-
declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo,
|
|
32
|
+
declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, }: ProfileSelectorProps) => JSX.Element;
|
|
33
33
|
export default ProfileSelector;
|
|
34
34
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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;AAGlD,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;AAQ5D,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,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;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAGlD,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;AAQ5D,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,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;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;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,OA6U7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
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, useState } from 'react';
|
|
14
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
15
15
|
import Select from 'react-select';
|
|
16
16
|
import { Button, ButtonGroup, DateTimeRange, DateTimeRangePicker, IconButton, useGrpcMetadata, useParcaContext, } from '@parca/components';
|
|
17
17
|
import { CloseIcon } from '@parca/icons';
|
|
@@ -21,7 +21,7 @@ import MatchersInput, { useLabelNames } from '../MatchersInput/index';
|
|
|
21
21
|
import { useMetricsGraphDimensions } from '../MetricsGraph/useMetricsGraphDimensions';
|
|
22
22
|
import ProfileMetricsGraph, { ProfileMetricsEmptyState } from '../ProfileMetricsGraph';
|
|
23
23
|
import ProfileTypeSelector from '../ProfileTypeSelector/index';
|
|
24
|
-
import {
|
|
24
|
+
import { useDefaultSumBy, useSumBySelection } from '../useSumBy';
|
|
25
25
|
import { useAutoQuerySelector } from './useAutoQuerySelector';
|
|
26
26
|
export const useProfileTypes = (client) => {
|
|
27
27
|
const [result, setResult] = useState(undefined);
|
|
@@ -40,10 +40,11 @@ export const useProfileTypes = (client) => {
|
|
|
40
40
|
}, [client, metadata, loading]);
|
|
41
41
|
return { loading, data: result, error };
|
|
42
42
|
};
|
|
43
|
-
const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo,
|
|
43
|
+
const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, }) => {
|
|
44
44
|
const { loading: profileTypesLoading, data: profileTypesData, error, } = useProfileTypes(queryClient);
|
|
45
45
|
const { heightStyle } = useMetricsGraphDimensions(comparing);
|
|
46
46
|
const { viewComponent } = useParcaContext();
|
|
47
|
+
const sumByRef = useRef(null);
|
|
47
48
|
const [timeRangeSelection, setTimeRangeSelection] = useState(DateTimeRange.fromRangeKey(querySelection.timeSelection, querySelection.from, querySelection.to));
|
|
48
49
|
const [queryExpressionString, setQueryExpressionString] = useState(querySelection.expression);
|
|
49
50
|
const profileType = useMemo(() => {
|
|
@@ -62,14 +63,10 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
|
|
|
62
63
|
? []
|
|
63
64
|
: selectedLabelNamesResult.response.labelNames;
|
|
64
65
|
}, [selectedLabelNamesResult]);
|
|
65
|
-
const [
|
|
66
|
-
|
|
67
|
-
});
|
|
68
|
-
const [sumBySelection, setSumBySelection, { isLoading: isSumBySelectionLoading }] = useSumBy(profileType, labelNamesLoading, labels, {
|
|
69
|
-
urlParamKey: 'sum_by_selection' + suffix,
|
|
70
|
-
withURLUpdate: false,
|
|
71
|
-
defaultValue: userSelectedSumBy,
|
|
66
|
+
const [sumBySelection, setUserSumBySelection, { isLoading: sumBySelectionLoading }] = useSumBySelection(profileType, labelNamesLoading, labels, {
|
|
67
|
+
defaultValue: querySelection.sumBy,
|
|
72
68
|
});
|
|
69
|
+
const { defaultSumBy, isLoading: defaultSumByLoading } = useDefaultSumBy(selectedProfileType, selectedLabelNamesLoading, selectedLabels);
|
|
73
70
|
useEffect(() => {
|
|
74
71
|
if (enforcedProfileName !== '') {
|
|
75
72
|
const [q, changed] = Query.parse(querySelection.expression).setProfileName(enforcedProfileName);
|
|
@@ -88,9 +85,6 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
|
|
|
88
85
|
const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(queryExpressionString);
|
|
89
86
|
const selectedProfileName = query.profileName();
|
|
90
87
|
const setNewQueryExpression = (expr, updateTs = false) => {
|
|
91
|
-
if (!isSumBySelectionLoading) {
|
|
92
|
-
setSumBy(sumBySelection);
|
|
93
|
-
}
|
|
94
88
|
const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(expr);
|
|
95
89
|
const delta = query.profileType().delta;
|
|
96
90
|
const from = timeRangeSelection.getFromMs(updateTs);
|
|
@@ -106,6 +100,7 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
|
|
|
106
100
|
from,
|
|
107
101
|
to,
|
|
108
102
|
timeSelection: timeRangeSelection.getRangeKey(),
|
|
103
|
+
sumBy: sumBySelection,
|
|
109
104
|
...mergeParams,
|
|
110
105
|
});
|
|
111
106
|
};
|
|
@@ -165,24 +160,41 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
|
|
|
165
160
|
profileTypesData,
|
|
166
161
|
setProfileName,
|
|
167
162
|
setQueryExpression,
|
|
168
|
-
querySelection: { ...querySelection, sumBy },
|
|
163
|
+
querySelection: { ...querySelection, sumBy: sumBySelection },
|
|
169
164
|
navigateTo,
|
|
165
|
+
loading: sumBySelectionLoading,
|
|
170
166
|
});
|
|
171
167
|
const searchDisabled = queryExpressionString === undefined ||
|
|
172
168
|
queryExpressionString === '' ||
|
|
173
169
|
queryExpressionString === '{}';
|
|
174
170
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex gap-2", children: [_jsxs("div", { className: "flex w-full flex-wrap content-start items-center gap-2", children: [_jsxs("div", { className: "pb-6", children: [_jsx("label", { className: "text-xs", children: "Profile type" }), _jsx(ProfileTypeSelector, { profileTypesData: profileTypesData, loading: profileTypesLoading, selectedKey: selectedProfileName, onSelection: setProfileName, error: error, disabled: viewComponent?.disableProfileTypesDropdown })] }), _jsxs("div", { className: "w-full flex-1 pb-6", children: [_jsxs("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: [_jsx("label", { className: "text-xs", children: "Query" }), (query.matchers.length > 0 || query.inputMatcherString.length > 0) &&
|
|
175
|
-
viewComponent !== undefined && _jsx("div", { children: viewComponent?.createViewComponent })] }), _jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName })] }), _jsxs("div", { className: "pb-6", children: [_jsx("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: _jsx("label", { className: "text-xs", children: "Sum by" }) }), _jsx(Select, { defaultValue: [], isMulti: true, name: "colors", options: labels.map(label => ({ label, value: label })), className: "parca-select-container text-sm w-80", classNamePrefix: "parca-select", value: sumBySelection.map(sumBy => ({ label: sumBy, value: sumBy })), onChange: selectedOptions => {
|
|
176
|
-
|
|
171
|
+
viewComponent !== undefined && _jsx("div", { children: viewComponent?.createViewComponent })] }), _jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName })] }), _jsxs("div", { className: "pb-6", children: [_jsx("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: _jsx("label", { className: "text-xs", children: "Sum by" }) }), _jsx(Select, { defaultValue: [], isMulti: true, name: "colors", options: labels.map(label => ({ label, value: label })), className: "parca-select-container text-sm w-80", classNamePrefix: "parca-select", value: (sumBySelection ?? []).map(sumBy => ({ label: sumBy, value: sumBy })), onChange: selectedOptions => {
|
|
172
|
+
setUserSumBySelection(selectedOptions.map(option => option.value));
|
|
177
173
|
}, placeholder: "Labels...", styles: {
|
|
178
174
|
indicatorSeparator: () => ({ display: 'none' }),
|
|
175
|
+
}, isDisabled: !profileType.delta, ref: sumByRef, onKeyDown: e => {
|
|
176
|
+
const currentRef = sumByRef.current;
|
|
177
|
+
if (currentRef == null) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const inputRef = currentRef.inputRef;
|
|
181
|
+
if (inputRef == null) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (e.key === 'Enter' &&
|
|
185
|
+
inputRef.value === '' &&
|
|
186
|
+
currentRef.state.focusedOptionId === null // menu is not open
|
|
187
|
+
) {
|
|
188
|
+
setQueryExpression(true);
|
|
189
|
+
currentRef.blur();
|
|
190
|
+
}
|
|
179
191
|
} })] }), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection }), _jsx(ButtonGroup, { children: _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
|
|
180
192
|
e.preventDefault();
|
|
181
193
|
setQueryExpression(true);
|
|
182
194
|
}, 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 &&
|
|
183
195
|
querySelection.expression.length > 0 &&
|
|
184
196
|
querySelection.from !== undefined &&
|
|
185
|
-
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, sumBy: sumBy, sumByLoading:
|
|
197
|
+
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, sumBy: querySelection.sumBy ?? defaultSumBy ?? [], sumByLoading: defaultSumByLoading, setTimeRange: (range) => {
|
|
186
198
|
const from = range.getFromMs();
|
|
187
199
|
const to = range.getToMs();
|
|
188
200
|
let mergedProfileParams = {};
|
|
@@ -8,7 +8,8 @@ interface Props {
|
|
|
8
8
|
setQueryExpression: () => void;
|
|
9
9
|
querySelection: QuerySelection;
|
|
10
10
|
navigateTo: NavigateFunction;
|
|
11
|
+
loading: boolean;
|
|
11
12
|
}
|
|
12
|
-
export declare const useAutoQuerySelector: ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, }: Props) => void;
|
|
13
|
+
export declare const useAutoQuerySelector: ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, loading, }: Props) => void;
|
|
13
14
|
export {};
|
|
14
15
|
//# sourceMappingURL=useAutoQuerySelector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAutoQuerySelector.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/useAutoQuerySelector.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,oBAAoB,EAAC,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAGlD,UAAU,KAAK;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACnD,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"useAutoQuerySelector.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/useAutoQuerySelector.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,oBAAoB,EAAC,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAGlD,UAAU,KAAK;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACnD,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,oBAAoB,wHAQ9B,KAAK,KAAG,IAuIV,CAAC"}
|
|
@@ -14,13 +14,16 @@ import { useEffect } from 'react';
|
|
|
14
14
|
import { selectAutoQuery, setAutoQuery, useAppDispatch, useAppSelector } from '@parca/store';
|
|
15
15
|
import { ProfileSelectionFromParams, SuffixParams } from '..';
|
|
16
16
|
import { constructProfileName } from '../ProfileTypeSelector';
|
|
17
|
-
export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, }) => {
|
|
17
|
+
export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, loading, }) => {
|
|
18
18
|
const autoQuery = useAppSelector(selectAutoQuery);
|
|
19
19
|
const dispatch = useAppDispatch();
|
|
20
20
|
const queryParams = new URLSearchParams(location.search);
|
|
21
21
|
const comparing = queryParams.get('comparing') === 'true';
|
|
22
22
|
const expressionA = queryParams.get('expression_a');
|
|
23
23
|
useEffect(() => {
|
|
24
|
+
if (loading) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
24
27
|
if (comparing && expressionA !== null && expressionA !== undefined) {
|
|
25
28
|
if (querySelection.expression === undefined) {
|
|
26
29
|
return;
|
|
@@ -62,7 +65,7 @@ export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, se
|
|
|
62
65
|
dashboard_items: ['icicle'],
|
|
63
66
|
});
|
|
64
67
|
}
|
|
65
|
-
}, [comparing, querySelection, navigateTo, expressionA, dispatch]);
|
|
68
|
+
}, [comparing, querySelection, navigateTo, expressionA, dispatch, loading]);
|
|
66
69
|
// Effect to load some initial data on load when is no selection
|
|
67
70
|
useEffect(() => {
|
|
68
71
|
void (async () => {
|
|
@@ -105,7 +108,8 @@ export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, se
|
|
|
105
108
|
if (autoQuery !== 'true' ||
|
|
106
109
|
profileTypesData?.types == null ||
|
|
107
110
|
profileTypesData.types.length < 1 ||
|
|
108
|
-
selectedProfileName.length === 0
|
|
111
|
+
selectedProfileName.length === 0 ||
|
|
112
|
+
loading) {
|
|
109
113
|
return;
|
|
110
114
|
}
|
|
111
115
|
setQueryExpression();
|
|
@@ -118,5 +122,6 @@ export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, se
|
|
|
118
122
|
setProfileName,
|
|
119
123
|
dispatch,
|
|
120
124
|
selectedProfileName,
|
|
125
|
+
loading,
|
|
121
126
|
]);
|
|
122
127
|
};
|
package/dist/useSumBy.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { ProfileType } from '@parca/parser';
|
|
2
2
|
export declare const DEFAULT_EMPTY_SUM_BY: string[];
|
|
3
|
-
export declare const
|
|
4
|
-
urlParamKey?: string;
|
|
5
|
-
withURLUpdate?: boolean;
|
|
3
|
+
export declare const useSumBySelection: (profileType: ProfileType | undefined, labelNamesLoading: boolean, labels: string[] | undefined, { defaultValue, }?: {
|
|
6
4
|
defaultValue?: string[];
|
|
7
|
-
}) => [string[], (labels: string[]) => void, {
|
|
8
|
-
userSelectedSumBy: string[] | undefined;
|
|
5
|
+
}) => [string[] | undefined, (labels: string[]) => void, {
|
|
9
6
|
isLoading: boolean;
|
|
10
7
|
}];
|
|
8
|
+
export declare const useDefaultSumBy: (profileType: ProfileType | undefined, labelNamesLoading: boolean, labels: string[] | undefined) => {
|
|
9
|
+
defaultSumBy: string[] | undefined;
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare const useSumByFromParams: (param: string | string[] | undefined) => string[] | undefined;
|
|
13
|
+
export declare const sumByToParam: (sumBy: string[] | undefined) => string | string[] | undefined;
|
|
11
14
|
//# sourceMappingURL=useSumBy.d.ts.map
|
package/dist/useSumBy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSumBy.d.ts","sourceRoot":"","sources":["../src/useSumBy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useSumBy.d.ts","sourceRoot":"","sources":["../src/useSumBy.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,oBAAoB,EAAE,MAAM,EAAO,CAAC;AA6BjD,eAAO,MAAM,iBAAiB,gBACf,WAAW,GAAG,SAAS,qBACjB,OAAO,UAClB,MAAM,EAAE,GAAG,SAAS,sBAGzB;IACD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,KACA,CACD,MAAM,EAAE,GAAG,SAAS,EACpB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,EAC1B;IACE,SAAS,EAAE,OAAO,CAAC;CACpB,CAsCF,CAAC;AAEF,eAAO,MAAM,eAAe,gBACb,WAAW,GAAG,SAAS,qBACjB,OAAO,UAClB,MAAM,EAAE,GAAG,SAAS,KAC3B;IAAC,YAAY,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAMzD,CAAC;AAkBF,eAAO,MAAM,kBAAkB,UAAW,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,EAAE,GAAG,SAMpF,CAAC;AAEF,eAAO,MAAM,YAAY,UAAW,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAU9E,CAAC"}
|
package/dist/useSumBy.js
CHANGED
|
@@ -10,13 +10,15 @@
|
|
|
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 { useCallback, useMemo } from 'react';
|
|
14
|
-
import { useParcaContext, useURLState } from '@parca/components';
|
|
13
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
15
14
|
export const DEFAULT_EMPTY_SUM_BY = [];
|
|
16
15
|
const getDefaultSumBy = (profile, labels) => {
|
|
17
16
|
if (profile === undefined || labels === undefined) {
|
|
18
17
|
return undefined;
|
|
19
18
|
}
|
|
19
|
+
if (!profile.delta) {
|
|
20
|
+
return DEFAULT_EMPTY_SUM_BY;
|
|
21
|
+
}
|
|
20
22
|
if (labels.includes('comm')) {
|
|
21
23
|
return ['comm'];
|
|
22
24
|
}
|
|
@@ -28,47 +30,62 @@ const getDefaultSumBy = (profile, labels) => {
|
|
|
28
30
|
}
|
|
29
31
|
return undefined;
|
|
30
32
|
};
|
|
31
|
-
export const
|
|
32
|
-
const {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
if (sumBy.length === 1) {
|
|
60
|
-
// Handle this separately to take care of the empty string scenario
|
|
61
|
-
setUserSelectedSumByParam(sumBy[0]);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
setUserSelectedSumByParam(sumBy);
|
|
65
|
-
}, [setUserSelectedSumByParam]);
|
|
33
|
+
export const useSumBySelection = (profileType, labelNamesLoading, labels, { defaultValue, } = {}) => {
|
|
34
|
+
const [userSelectedSumBy, setUserSelectedSumBy] = useState(profileType != null ? { [profileType.toString()]: defaultValue } : {});
|
|
35
|
+
const setSumBy = useCallback((sumBy) => {
|
|
36
|
+
setUserSelectedSumBy(prev => {
|
|
37
|
+
if (profileType == null) {
|
|
38
|
+
return prev;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
...prev,
|
|
42
|
+
[profileType.toString()]: sumBy,
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}, [setUserSelectedSumBy, profileType]);
|
|
46
|
+
const { defaultSumBy } = useDefaultSumBy(profileType, labelNamesLoading, labels);
|
|
47
|
+
let sumBy = userSelectedSumBy[profileType?.toString() ?? ''] ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY;
|
|
48
|
+
if (profileType?.delta !== true) {
|
|
49
|
+
sumBy = DEFAULT_EMPTY_SUM_BY;
|
|
50
|
+
}
|
|
51
|
+
return [
|
|
52
|
+
labelNamesLoading ? undefined : sumBy,
|
|
53
|
+
setSumBy,
|
|
54
|
+
{
|
|
55
|
+
isLoading: labelNamesLoading,
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
};
|
|
59
|
+
export const useDefaultSumBy = (profileType, labelNamesLoading, labels) => {
|
|
66
60
|
const defaultSumBy = useMemo(() => {
|
|
67
61
|
return getDefaultSumBy(profileType, labels);
|
|
68
62
|
}, [profileType, labels]);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
63
|
+
return { defaultSumBy, isLoading: labelNamesLoading };
|
|
64
|
+
};
|
|
65
|
+
const getSumByFromParam = (param) => {
|
|
66
|
+
if (param?.length === 0) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
if (param === '__none__') {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
if (typeof param === 'string') {
|
|
73
|
+
return [param];
|
|
74
|
+
}
|
|
75
|
+
return param;
|
|
76
|
+
};
|
|
77
|
+
export const useSumByFromParams = (param) => {
|
|
78
|
+
const sumBy = useMemo(() => {
|
|
79
|
+
return getSumByFromParam(param);
|
|
80
|
+
}, [param]);
|
|
81
|
+
return sumBy;
|
|
82
|
+
};
|
|
83
|
+
export const sumByToParam = (sumBy) => {
|
|
84
|
+
if (sumBy === undefined) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
if (sumBy.length === 0) {
|
|
88
|
+
return '__none__';
|
|
72
89
|
}
|
|
73
|
-
return
|
|
90
|
+
return sumBy;
|
|
74
91
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.416",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@headlessui/react": "^1.7.19",
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"access": "public",
|
|
74
74
|
"registry": "https://registry.npmjs.org/"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "b57a9ad4f98b2ea30eca63611e487fb94dbbe106"
|
|
77
77
|
}
|
|
@@ -22,6 +22,7 @@ import {capitalizeOnlyFirstLetter, type NavigateFunction} from '@parca/utilities
|
|
|
22
22
|
|
|
23
23
|
import {ProfileSelection, ProfileSelectionFromParams, SuffixParams} from '..';
|
|
24
24
|
import {QuerySelection, useProfileTypes} from '../ProfileSelector';
|
|
25
|
+
import {sumByToParam, useSumByFromParams} from '../useSumBy';
|
|
25
26
|
import ProfileExplorerCompare from './ProfileExplorerCompare';
|
|
26
27
|
import ProfileExplorerSingle from './ProfileExplorerSingle';
|
|
27
28
|
|
|
@@ -64,6 +65,25 @@ const sanitizeDateRange = (
|
|
|
64
65
|
};
|
|
65
66
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
66
67
|
|
|
68
|
+
const filterEmptyParams = (o: Record<string, any>): Record<string, any> => {
|
|
69
|
+
return Object.fromEntries(
|
|
70
|
+
Object.entries(o)
|
|
71
|
+
.filter(
|
|
72
|
+
([_, value]) =>
|
|
73
|
+
value !== '' && value !== undefined && (Array.isArray(value) ? value.length > 0 : true)
|
|
74
|
+
)
|
|
75
|
+
.map(([key, value]) => {
|
|
76
|
+
if (typeof value === 'string') {
|
|
77
|
+
return [key, value];
|
|
78
|
+
}
|
|
79
|
+
if (Array.isArray(value)) {
|
|
80
|
+
return [key, value];
|
|
81
|
+
}
|
|
82
|
+
return [key, value];
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
67
87
|
const filterSuffix = (
|
|
68
88
|
o: {[key: string]: string | string[] | undefined},
|
|
69
89
|
suffix: string
|
|
@@ -148,6 +168,9 @@ const ProfileExplorerApp = ({
|
|
|
148
168
|
const [profileA, setProfileA] = useState<ProfileSelection | null>(null);
|
|
149
169
|
const [profileB, setProfileB] = useState<ProfileSelection | null>(null);
|
|
150
170
|
|
|
171
|
+
const sumByA = useSumByFromParams(sum_by_a);
|
|
172
|
+
const sumByB = useSumByFromParams(sum_by_b);
|
|
173
|
+
|
|
151
174
|
useEffect(() => {
|
|
152
175
|
const mergeFrom = merge_from_a ?? undefined;
|
|
153
176
|
const mergeTo = merge_to_a ?? undefined;
|
|
@@ -225,7 +248,7 @@ const ProfileExplorerApp = ({
|
|
|
225
248
|
from: parseInt(from_a as string),
|
|
226
249
|
to: parseInt(to_a as string),
|
|
227
250
|
timeSelection: time_selection_a as string,
|
|
228
|
-
sumBy:
|
|
251
|
+
sumBy: sumByA,
|
|
229
252
|
};
|
|
230
253
|
|
|
231
254
|
// Show the SingleProfileExplorer when not comparing
|
|
@@ -243,17 +266,18 @@ const ProfileExplorerApp = ({
|
|
|
243
266
|
'/',
|
|
244
267
|
// Filtering the _a suffix causes us to reset potential profile
|
|
245
268
|
// selection when running a new query.
|
|
246
|
-
{
|
|
269
|
+
filterEmptyParams({
|
|
247
270
|
...filterSuffix(queryParams, '_a'),
|
|
248
271
|
...{
|
|
249
272
|
expression_a: encodeURIComponent(q.expression),
|
|
250
273
|
from_a: q.from.toString(),
|
|
251
274
|
to_a: q.to.toString(),
|
|
252
275
|
time_selection_a: q.timeSelection,
|
|
276
|
+
sum_by_a: sumByToParam(q.sumBy),
|
|
253
277
|
dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
|
|
254
278
|
...mergeParams,
|
|
255
279
|
},
|
|
256
|
-
}
|
|
280
|
+
})
|
|
257
281
|
);
|
|
258
282
|
};
|
|
259
283
|
|
|
@@ -283,7 +307,7 @@ const ProfileExplorerApp = ({
|
|
|
283
307
|
from: parseInt(from_b as string),
|
|
284
308
|
to: parseInt(to_b as string),
|
|
285
309
|
timeSelection: time_selection_b as string,
|
|
286
|
-
sumBy:
|
|
310
|
+
sumBy: sumByB,
|
|
287
311
|
};
|
|
288
312
|
|
|
289
313
|
const selectQueryA = (q: QuerySelection): void => {
|
|
@@ -299,7 +323,7 @@ const ProfileExplorerApp = ({
|
|
|
299
323
|
'/',
|
|
300
324
|
// Filtering the _a suffix causes us to reset potential profile
|
|
301
325
|
// selection when running a new query.
|
|
302
|
-
{
|
|
326
|
+
filterEmptyParams({
|
|
303
327
|
...filterSuffix(queryParams, '_a'),
|
|
304
328
|
...{
|
|
305
329
|
compare_a: 'true',
|
|
@@ -309,11 +333,12 @@ const ProfileExplorerApp = ({
|
|
|
309
333
|
from_a: q.from.toString(),
|
|
310
334
|
to_a: q.to.toString(),
|
|
311
335
|
time_selection_a: q.timeSelection,
|
|
336
|
+
sum_by_a: sumByToParam(q.sumBy),
|
|
312
337
|
filter_by_function: filter_by_function ?? '',
|
|
313
338
|
dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
|
|
314
339
|
...mergeParams,
|
|
315
340
|
},
|
|
316
|
-
}
|
|
341
|
+
})
|
|
317
342
|
);
|
|
318
343
|
};
|
|
319
344
|
|
|
@@ -330,7 +355,7 @@ const ProfileExplorerApp = ({
|
|
|
330
355
|
'/',
|
|
331
356
|
// Filtering the _b suffix causes us to reset potential profile
|
|
332
357
|
// selection when running a new query.
|
|
333
|
-
{
|
|
358
|
+
filterEmptyParams({
|
|
334
359
|
...filterSuffix(queryParams, '_b'),
|
|
335
360
|
...{
|
|
336
361
|
compare_b: 'true',
|
|
@@ -340,11 +365,12 @@ const ProfileExplorerApp = ({
|
|
|
340
365
|
from_b: q.from.toString(),
|
|
341
366
|
to_b: q.to.toString(),
|
|
342
367
|
time_selection_b: q.timeSelection,
|
|
368
|
+
sum_by_b: sumByToParam(q.sumBy),
|
|
343
369
|
filter_by_function: filter_by_function ?? '',
|
|
344
370
|
dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
|
|
345
371
|
...mergeParams,
|
|
346
372
|
},
|
|
347
|
-
}
|
|
373
|
+
})
|
|
348
374
|
);
|
|
349
375
|
};
|
|
350
376
|
|
|
@@ -122,7 +122,7 @@ export const useQueryRange = (
|
|
|
122
122
|
}, [stepCountStr, defaultStepCount, setStepCount]);
|
|
123
123
|
|
|
124
124
|
const {data, isLoading, error} = useGrpcQuery<QueryRangeResponse | undefined>({
|
|
125
|
-
key: ['query-range', queryExpression, start, end, sumBy.join(','), stepCount, metadata],
|
|
125
|
+
key: ['query-range', queryExpression, start, end, (sumBy ?? []).join(','), stepCount, metadata],
|
|
126
126
|
queryFn: async () => {
|
|
127
127
|
const stepDuration = getStepDuration(start, end, stepCount);
|
|
128
128
|
const {response} = await client.queryRange(
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import React, {useEffect, useMemo, useState} from 'react';
|
|
14
|
+
import React, {useEffect, useMemo, useRef, useState} from 'react';
|
|
15
15
|
|
|
16
16
|
import {RpcError} from '@protobuf-ts/runtime-rpc';
|
|
17
|
-
import Select from 'react-select';
|
|
17
|
+
import Select, {type SelectInstance} from 'react-select';
|
|
18
18
|
|
|
19
19
|
import {Label, ProfileTypesResponse, QueryServiceClient} from '@parca/client';
|
|
20
20
|
import {
|
|
@@ -35,7 +35,7 @@ import MatchersInput, {useLabelNames} from '../MatchersInput/index';
|
|
|
35
35
|
import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
|
|
36
36
|
import ProfileMetricsGraph, {ProfileMetricsEmptyState} from '../ProfileMetricsGraph';
|
|
37
37
|
import ProfileTypeSelector from '../ProfileTypeSelector/index';
|
|
38
|
-
import {
|
|
38
|
+
import {useDefaultSumBy, useSumBySelection} from '../useSumBy';
|
|
39
39
|
import {useAutoQuerySelector} from './useAutoQuerySelector';
|
|
40
40
|
|
|
41
41
|
export interface QuerySelection {
|
|
@@ -97,7 +97,6 @@ const ProfileSelector = ({
|
|
|
97
97
|
profileSelection,
|
|
98
98
|
comparing,
|
|
99
99
|
navigateTo,
|
|
100
|
-
suffix = '',
|
|
101
100
|
}: ProfileSelectorProps): JSX.Element => {
|
|
102
101
|
const {
|
|
103
102
|
loading: profileTypesLoading,
|
|
@@ -106,6 +105,7 @@ const ProfileSelector = ({
|
|
|
106
105
|
} = useProfileTypes(queryClient);
|
|
107
106
|
const {heightStyle} = useMetricsGraphDimensions(comparing);
|
|
108
107
|
const {viewComponent} = useParcaContext();
|
|
108
|
+
const sumByRef = useRef(null);
|
|
109
109
|
|
|
110
110
|
const [timeRangeSelection, setTimeRangeSelection] = useState(
|
|
111
111
|
DateTimeRange.fromRangeKey(querySelection.timeSelection, querySelection.from, querySelection.to)
|
|
@@ -137,24 +137,15 @@ const ProfileSelector = ({
|
|
|
137
137
|
: selectedLabelNamesResult.response.labelNames;
|
|
138
138
|
}, [selectedLabelNamesResult]);
|
|
139
139
|
|
|
140
|
-
const [
|
|
140
|
+
const [sumBySelection, setUserSumBySelection, {isLoading: sumBySelectionLoading}] =
|
|
141
|
+
useSumBySelection(profileType, labelNamesLoading, labels, {
|
|
142
|
+
defaultValue: querySelection.sumBy,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const {defaultSumBy, isLoading: defaultSumByLoading} = useDefaultSumBy(
|
|
141
146
|
selectedProfileType,
|
|
142
147
|
selectedLabelNamesLoading,
|
|
143
|
-
selectedLabels
|
|
144
|
-
{
|
|
145
|
-
urlParamKey: 'sum_by' + suffix,
|
|
146
|
-
}
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
const [sumBySelection, setSumBySelection, {isLoading: isSumBySelectionLoading}] = useSumBy(
|
|
150
|
-
profileType,
|
|
151
|
-
labelNamesLoading,
|
|
152
|
-
labels,
|
|
153
|
-
{
|
|
154
|
-
urlParamKey: 'sum_by_selection' + suffix,
|
|
155
|
-
withURLUpdate: false,
|
|
156
|
-
defaultValue: userSelectedSumBy,
|
|
157
|
-
}
|
|
148
|
+
selectedLabels
|
|
158
149
|
);
|
|
159
150
|
|
|
160
151
|
useEffect(() => {
|
|
@@ -181,9 +172,6 @@ const ProfileSelector = ({
|
|
|
181
172
|
const selectedProfileName = query.profileName();
|
|
182
173
|
|
|
183
174
|
const setNewQueryExpression = (expr: string, updateTs = false): void => {
|
|
184
|
-
if (!isSumBySelectionLoading) {
|
|
185
|
-
setSumBy(sumBySelection);
|
|
186
|
-
}
|
|
187
175
|
const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(expr);
|
|
188
176
|
const delta = query.profileType().delta;
|
|
189
177
|
const from = timeRangeSelection.getFromMs(updateTs);
|
|
@@ -200,6 +188,7 @@ const ProfileSelector = ({
|
|
|
200
188
|
from,
|
|
201
189
|
to,
|
|
202
190
|
timeSelection: timeRangeSelection.getRangeKey(),
|
|
191
|
+
sumBy: sumBySelection,
|
|
203
192
|
...mergeParams,
|
|
204
193
|
});
|
|
205
194
|
};
|
|
@@ -269,8 +258,9 @@ const ProfileSelector = ({
|
|
|
269
258
|
profileTypesData,
|
|
270
259
|
setProfileName,
|
|
271
260
|
setQueryExpression,
|
|
272
|
-
querySelection: {...querySelection, sumBy},
|
|
261
|
+
querySelection: {...querySelection, sumBy: sumBySelection},
|
|
273
262
|
navigateTo,
|
|
263
|
+
loading: sumBySelectionLoading,
|
|
274
264
|
});
|
|
275
265
|
|
|
276
266
|
const searchDisabled =
|
|
@@ -318,14 +308,35 @@ const ProfileSelector = ({
|
|
|
318
308
|
options={labels.map(label => ({label, value: label}))}
|
|
319
309
|
className="parca-select-container text-sm w-80"
|
|
320
310
|
classNamePrefix="parca-select"
|
|
321
|
-
value={sumBySelection.map(sumBy => ({label: sumBy, value: sumBy}))}
|
|
311
|
+
value={(sumBySelection ?? []).map(sumBy => ({label: sumBy, value: sumBy}))}
|
|
322
312
|
onChange={selectedOptions => {
|
|
323
|
-
|
|
313
|
+
setUserSumBySelection(selectedOptions.map(option => option.value));
|
|
324
314
|
}}
|
|
325
315
|
placeholder="Labels..."
|
|
326
316
|
styles={{
|
|
327
317
|
indicatorSeparator: () => ({display: 'none'}),
|
|
328
318
|
}}
|
|
319
|
+
isDisabled={!profileType.delta}
|
|
320
|
+
ref={sumByRef}
|
|
321
|
+
onKeyDown={e => {
|
|
322
|
+
const currentRef = sumByRef.current as unknown as SelectInstance | null;
|
|
323
|
+
if (currentRef == null) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const inputRef = currentRef.inputRef;
|
|
327
|
+
if (inputRef == null) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (
|
|
332
|
+
e.key === 'Enter' &&
|
|
333
|
+
inputRef.value === '' &&
|
|
334
|
+
currentRef.state.focusedOptionId === null // menu is not open
|
|
335
|
+
) {
|
|
336
|
+
setQueryExpression(true);
|
|
337
|
+
currentRef.blur();
|
|
338
|
+
}
|
|
339
|
+
}}
|
|
329
340
|
/>
|
|
330
341
|
</div>
|
|
331
342
|
<DateTimeRangePicker
|
|
@@ -361,8 +372,8 @@ const ProfileSelector = ({
|
|
|
361
372
|
to={querySelection.to}
|
|
362
373
|
profile={profileSelection}
|
|
363
374
|
comparing={comparing}
|
|
364
|
-
sumBy={sumBy}
|
|
365
|
-
sumByLoading={
|
|
375
|
+
sumBy={querySelection.sumBy ?? defaultSumBy ?? []}
|
|
376
|
+
sumByLoading={defaultSumByLoading}
|
|
366
377
|
setTimeRange={(range: DateTimeRange) => {
|
|
367
378
|
const from = range.getFromMs();
|
|
368
379
|
const to = range.getToMs();
|
|
@@ -28,6 +28,7 @@ interface Props {
|
|
|
28
28
|
setQueryExpression: () => void;
|
|
29
29
|
querySelection: QuerySelection;
|
|
30
30
|
navigateTo: NavigateFunction;
|
|
31
|
+
loading: boolean;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export const useAutoQuerySelector = ({
|
|
@@ -37,6 +38,7 @@ export const useAutoQuerySelector = ({
|
|
|
37
38
|
setQueryExpression,
|
|
38
39
|
querySelection,
|
|
39
40
|
navigateTo,
|
|
41
|
+
loading,
|
|
40
42
|
}: Props): void => {
|
|
41
43
|
const autoQuery = useAppSelector(selectAutoQuery);
|
|
42
44
|
const dispatch = useAppDispatch();
|
|
@@ -46,6 +48,9 @@ export const useAutoQuerySelector = ({
|
|
|
46
48
|
const expressionA = queryParams.get('expression_a');
|
|
47
49
|
|
|
48
50
|
useEffect(() => {
|
|
51
|
+
if (loading) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
49
54
|
if (comparing && expressionA !== null && expressionA !== undefined) {
|
|
50
55
|
if (querySelection.expression === undefined) {
|
|
51
56
|
return;
|
|
@@ -98,7 +103,7 @@ export const useAutoQuerySelector = ({
|
|
|
98
103
|
dashboard_items: ['icicle'],
|
|
99
104
|
});
|
|
100
105
|
}
|
|
101
|
-
}, [comparing, querySelection, navigateTo, expressionA, dispatch]);
|
|
106
|
+
}, [comparing, querySelection, navigateTo, expressionA, dispatch, loading]);
|
|
102
107
|
|
|
103
108
|
// Effect to load some initial data on load when is no selection
|
|
104
109
|
useEffect(() => {
|
|
@@ -152,7 +157,8 @@ export const useAutoQuerySelector = ({
|
|
|
152
157
|
autoQuery !== 'true' ||
|
|
153
158
|
profileTypesData?.types == null ||
|
|
154
159
|
profileTypesData.types.length < 1 ||
|
|
155
|
-
selectedProfileName.length === 0
|
|
160
|
+
selectedProfileName.length === 0 ||
|
|
161
|
+
loading
|
|
156
162
|
) {
|
|
157
163
|
return;
|
|
158
164
|
}
|
|
@@ -166,5 +172,6 @@ export const useAutoQuerySelector = ({
|
|
|
166
172
|
setProfileName,
|
|
167
173
|
dispatch,
|
|
168
174
|
selectedProfileName,
|
|
175
|
+
loading,
|
|
169
176
|
]);
|
|
170
177
|
};
|
package/src/useSumBy.ts
CHANGED
|
@@ -11,9 +11,8 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import {useCallback, useMemo} from 'react';
|
|
14
|
+
import {useCallback, useMemo, useState} from 'react';
|
|
15
15
|
|
|
16
|
-
import {useParcaContext, useURLState} from '@parca/components';
|
|
17
16
|
import {ProfileType} from '@parca/parser';
|
|
18
17
|
|
|
19
18
|
export const DEFAULT_EMPTY_SUM_BY: string[] = [];
|
|
@@ -26,6 +25,10 @@ const getDefaultSumBy = (
|
|
|
26
25
|
return undefined;
|
|
27
26
|
}
|
|
28
27
|
|
|
28
|
+
if (!profile.delta) {
|
|
29
|
+
return DEFAULT_EMPTY_SUM_BY;
|
|
30
|
+
}
|
|
31
|
+
|
|
29
32
|
if (labels.includes('comm')) {
|
|
30
33
|
return ['comm'];
|
|
31
34
|
}
|
|
@@ -41,80 +44,104 @@ const getDefaultSumBy = (
|
|
|
41
44
|
return undefined;
|
|
42
45
|
};
|
|
43
46
|
|
|
44
|
-
export const
|
|
47
|
+
export const useSumBySelection = (
|
|
45
48
|
profileType: ProfileType | undefined,
|
|
46
49
|
labelNamesLoading: boolean,
|
|
47
50
|
labels: string[] | undefined,
|
|
48
51
|
{
|
|
49
|
-
urlParamKey = 'sum_by',
|
|
50
|
-
withURLUpdate = true,
|
|
51
52
|
defaultValue,
|
|
52
53
|
}: {
|
|
53
|
-
urlParamKey?: string;
|
|
54
|
-
withURLUpdate?: boolean;
|
|
55
54
|
defaultValue?: string[];
|
|
56
55
|
} = {}
|
|
57
56
|
): [
|
|
58
|
-
string[],
|
|
57
|
+
string[] | undefined,
|
|
59
58
|
(labels: string[]) => void,
|
|
60
|
-
{
|
|
59
|
+
{
|
|
60
|
+
isLoading: boolean;
|
|
61
|
+
}
|
|
61
62
|
] => {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
navigateTo,
|
|
66
|
-
withURLUpdate,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const userSelectedSumBy = useMemo<string[] | undefined>(() => {
|
|
70
|
-
if (userSelectedSumByParam?.length === 0) {
|
|
71
|
-
return undefined;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (userSelectedSumByParam === '__none__') {
|
|
75
|
-
return [];
|
|
76
|
-
}
|
|
63
|
+
const [userSelectedSumBy, setUserSelectedSumBy] = useState<Record<string, string[] | undefined>>(
|
|
64
|
+
profileType != null ? {[profileType.toString()]: defaultValue} : {}
|
|
65
|
+
);
|
|
77
66
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
67
|
+
const setSumBy = useCallback(
|
|
68
|
+
(sumBy: string[]) => {
|
|
69
|
+
setUserSelectedSumBy(prev => {
|
|
70
|
+
if (profileType == null) {
|
|
71
|
+
return prev;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
...prev,
|
|
76
|
+
[profileType.toString()]: sumBy,
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
[setUserSelectedSumBy, profileType]
|
|
81
|
+
);
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
return [userSelectedSumByParam];
|
|
84
|
-
}
|
|
83
|
+
const {defaultSumBy} = useDefaultSumBy(profileType, labelNamesLoading, labels);
|
|
85
84
|
|
|
86
|
-
|
|
85
|
+
let sumBy =
|
|
86
|
+
userSelectedSumBy[profileType?.toString() ?? ''] ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY;
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
if (profileType?.delta !== true) {
|
|
89
|
+
sumBy = DEFAULT_EMPTY_SUM_BY;
|
|
90
|
+
}
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (sumBy.length === 1) {
|
|
99
|
-
// Handle this separately to take care of the empty string scenario
|
|
100
|
-
setUserSelectedSumByParam(sumBy[0]);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
setUserSelectedSumByParam(sumBy);
|
|
92
|
+
return [
|
|
93
|
+
labelNamesLoading ? undefined : sumBy,
|
|
94
|
+
setSumBy,
|
|
95
|
+
{
|
|
96
|
+
isLoading: labelNamesLoading,
|
|
105
97
|
},
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
];
|
|
99
|
+
};
|
|
108
100
|
|
|
101
|
+
export const useDefaultSumBy = (
|
|
102
|
+
profileType: ProfileType | undefined,
|
|
103
|
+
labelNamesLoading: boolean,
|
|
104
|
+
labels: string[] | undefined
|
|
105
|
+
): {defaultSumBy: string[] | undefined; isLoading: boolean} => {
|
|
109
106
|
const defaultSumBy = useMemo(() => {
|
|
110
107
|
return getDefaultSumBy(profileType, labels);
|
|
111
108
|
}, [profileType, labels]);
|
|
112
109
|
|
|
113
|
-
|
|
110
|
+
return {defaultSumBy, isLoading: labelNamesLoading};
|
|
111
|
+
};
|
|
114
112
|
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
const getSumByFromParam = (param: string | string[] | undefined): string[] | undefined => {
|
|
114
|
+
if (param?.length === 0) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (param === '__none__') {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (typeof param === 'string') {
|
|
123
|
+
return [param];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return param;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const useSumByFromParams = (param: string | string[] | undefined): string[] | undefined => {
|
|
130
|
+
const sumBy = useMemo(() => {
|
|
131
|
+
return getSumByFromParam(param);
|
|
132
|
+
}, [param]);
|
|
133
|
+
|
|
134
|
+
return sumBy;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const sumByToParam = (sumBy: string[] | undefined): string | string[] | undefined => {
|
|
138
|
+
if (sumBy === undefined) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (sumBy.length === 0) {
|
|
143
|
+
return '__none__';
|
|
117
144
|
}
|
|
118
145
|
|
|
119
|
-
return
|
|
146
|
+
return sumBy;
|
|
120
147
|
};
|