@parca/profile 0.16.61 → 0.16.63
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/Callgraph/index.js +17 -12
- package/dist/MatchersInput/index.d.ts +1 -5
- package/dist/MatchersInput/index.js +69 -280
- package/dist/ProfileMetricsGraph/index.d.ts +2 -2
- package/dist/ProfileMetricsGraph/index.js +22 -30
- package/dist/styles.css +1 -1
- package/package.json +4 -4
- package/src/Callgraph/index.tsx +18 -12
- package/src/MatchersInput/index.tsx +173 -436
- package/src/ProfileMetricsGraph/index.tsx +26 -23
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.63](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.62...@parca/profile@0.16.63) (2022-11-08)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
10
|
+
## 0.16.62 (2022-11-08)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @parca/profile
|
|
13
|
+
|
|
6
14
|
## 0.16.61 (2022-11-07)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @parca/profile
|
package/dist/Callgraph/index.js
CHANGED
|
@@ -108,7 +108,7 @@ var Callgraph = function (_a) {
|
|
|
108
108
|
}), stage = _f[0], setStage = _f[1];
|
|
109
109
|
var rawNodes = graph.nodes, total = graph.cumulative;
|
|
110
110
|
var currentSearchString = useAppSelector(selectSearchNodeString);
|
|
111
|
-
var isSearchEmpty = currentSearchString === undefined;
|
|
111
|
+
var isSearchEmpty = currentSearchString === undefined || currentSearchString === '';
|
|
112
112
|
useEffect(function () {
|
|
113
113
|
var getDataWithPositions = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
114
114
|
var dataAsDot, jsonGraph;
|
|
@@ -159,24 +159,29 @@ var Callgraph = function (_a) {
|
|
|
159
159
|
// 4. Add zooming
|
|
160
160
|
var handleWheel = function (e) {
|
|
161
161
|
var _a;
|
|
162
|
+
// stop default scrolling
|
|
162
163
|
e.evt.preventDefault();
|
|
163
|
-
var
|
|
164
|
-
var scaleYBy = 1.05;
|
|
164
|
+
var scaleBy = 1.01;
|
|
165
165
|
var stage = e.target.getStage();
|
|
166
166
|
if (stage !== null) {
|
|
167
167
|
var oldScale = stage.scaleX();
|
|
168
|
-
var
|
|
168
|
+
var pointer = (_a = stage.getPointerPosition()) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
|
|
169
169
|
var mousePointTo = {
|
|
170
|
-
x: x / oldScale - stage.x() / oldScale,
|
|
171
|
-
y: y / oldScale - stage.y() / oldScale,
|
|
170
|
+
x: pointer.x / oldScale - stage.x() / oldScale,
|
|
171
|
+
y: pointer.y / oldScale - stage.y() / oldScale,
|
|
172
172
|
};
|
|
173
|
-
|
|
174
|
-
var
|
|
175
|
-
|
|
173
|
+
// whether to zoom in or out
|
|
174
|
+
var direction = e.evt.deltaY > 0 ? 1 : -1;
|
|
175
|
+
// for trackpad, e.evt.ctrlKey is true => in that case, revert direction
|
|
176
|
+
if (e.evt.ctrlKey) {
|
|
177
|
+
direction = -direction;
|
|
178
|
+
}
|
|
179
|
+
var newScale = direction > 0 ? oldScale * scaleBy : oldScale / scaleBy;
|
|
180
|
+
stage.scale({ x: newScale, y: newScale });
|
|
176
181
|
setStage({
|
|
177
|
-
scale: { x:
|
|
178
|
-
x: -(mousePointTo.x - x /
|
|
179
|
-
y: -(mousePointTo.y - y /
|
|
182
|
+
scale: { x: newScale, y: newScale },
|
|
183
|
+
x: -(mousePointTo.x - pointer.x / newScale) * newScale,
|
|
184
|
+
y: -(mousePointTo.y - pointer.y / newScale) * newScale,
|
|
180
185
|
});
|
|
181
186
|
}
|
|
182
187
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Query } from '@parca/parser';
|
|
2
|
-
import { LabelsResponse, QueryServiceClient
|
|
2
|
+
import { LabelsResponse, QueryServiceClient } from '@parca/client';
|
|
3
3
|
interface MatchersInputProps {
|
|
4
4
|
queryClient: QueryServiceClient;
|
|
5
5
|
setMatchersString: (arg: string) => void;
|
|
@@ -10,10 +10,6 @@ export interface ILabelNamesResult {
|
|
|
10
10
|
response?: LabelsResponse;
|
|
11
11
|
error?: Error;
|
|
12
12
|
}
|
|
13
|
-
export interface ILabelValuesResult {
|
|
14
|
-
response?: ValuesResponse;
|
|
15
|
-
error?: Error;
|
|
16
|
-
}
|
|
17
13
|
interface UseLabelNames {
|
|
18
14
|
result: ILabelNamesResult;
|
|
19
15
|
loading: boolean;
|
|
@@ -9,16 +9,7 @@ var __assign = (this && this.__assign) || function () {
|
|
|
9
9
|
};
|
|
10
10
|
return __assign.apply(this, arguments);
|
|
11
11
|
};
|
|
12
|
-
|
|
13
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
14
|
-
if (ar || !(i in from)) {
|
|
15
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
16
|
-
ar[i] = from[i];
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
20
|
-
};
|
|
21
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
12
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
13
|
// Copyright 2022 The Parca Authors
|
|
23
14
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
24
15
|
// you may not use this file except in compliance with the License.
|
|
@@ -37,21 +28,6 @@ import { Query } from '@parca/parser';
|
|
|
37
28
|
import { usePopper } from 'react-popper';
|
|
38
29
|
import cx from 'classnames';
|
|
39
30
|
import { useParcaTheme, useGrpcMetadata } from '@parca/components';
|
|
40
|
-
var Labels;
|
|
41
|
-
(function (Labels) {
|
|
42
|
-
Labels["labelName"] = "labelName";
|
|
43
|
-
Labels["labelValue"] = "labelValue";
|
|
44
|
-
Labels["literal"] = "literal";
|
|
45
|
-
})(Labels || (Labels = {}));
|
|
46
|
-
// eslint-disable-next-line no-useless-escape
|
|
47
|
-
var labelNameValueRe = /(^([a-z])\w+)(=~|=|!=|!~)(\")[a-zA-Z0-9_.-:]+(\")$/g; // labelNameValueRe matches the following: labelName=~"labelValue"
|
|
48
|
-
var labelNameValueWithoutQuotesRe = /(^([a-z])\w+)(=~|=|!=|!~)[a-zA-Z0-9_.-:]+$/g; // labelNameValueWithoutQuotesRe matches the following: labelName=~labelValue
|
|
49
|
-
var labelNameLiteralRe = /(^([a-z])\w+)(=~|=|!=|!~)/; // labelNameLiteralRe matches the following: labelName=~, labelName!=~, labelName=, labelName!=
|
|
50
|
-
var literalRe = /(=~|=|!=|!~)/; // literalRe matches the following: =~, =, !=, !~
|
|
51
|
-
var addQuoteMarks = function (labelValue) {
|
|
52
|
-
// eslint-disable-next-line no-useless-escape
|
|
53
|
-
return "\"".concat(labelValue, "\"");
|
|
54
|
-
};
|
|
55
31
|
export var useLabelNames = function (client) {
|
|
56
32
|
var _a = useState(true), loading = _a[0], setLoading = _a[1];
|
|
57
33
|
var _b = useState({}), result = _b[0], setResult = _b[1];
|
|
@@ -84,46 +60,45 @@ var Suggestions = /** @class */ (function () {
|
|
|
84
60
|
}());
|
|
85
61
|
var MatchersInput = function (_a) {
|
|
86
62
|
var queryClient = _a.queryClient, setMatchersString = _a.setMatchersString, runQuery = _a.runQuery, currentQuery = _a.currentQuery;
|
|
87
|
-
var _b = useState(null),
|
|
88
|
-
var _c = useState(
|
|
89
|
-
var _d = useState(
|
|
90
|
-
var _e = useState(
|
|
91
|
-
var _f = useState(
|
|
92
|
-
var _g = useState(
|
|
93
|
-
var _h = useState(
|
|
94
|
-
var _j = useState(
|
|
95
|
-
var
|
|
96
|
-
var
|
|
97
|
-
var _l = useState(null), labelValuesResponse = _l[0], setLabelValuesResponse = _l[1];
|
|
98
|
-
var _m = useState(null), currentLabelsCollection = _m[0], setCurrentLabelsCollection = _m[1]; // This is an array that contains query expressions that have been matched i.e. they have been completed and have the blue badge around them in the UI.
|
|
99
|
-
var _o = usePopper(divInputRef, popperElement, {
|
|
63
|
+
var _b = useState(null), inputRef = _b[0], setInputRef = _b[1];
|
|
64
|
+
var _c = useState(false), focusedInput = _c[0], setFocusedInput = _c[1];
|
|
65
|
+
var _d = useState(true), showSuggest = _d[0], setShowSuggest = _d[1];
|
|
66
|
+
var _e = useState(-1), highlightedSuggestionIndex = _e[0], setHighlightedSuggestionIndex = _e[1];
|
|
67
|
+
var _f = useState(false), labelValuesLoading = _f[0], setLabelValuesLoading = _f[1];
|
|
68
|
+
var _g = useState(new Suggestion('', '', '')), lastCompleted = _g[0], setLastCompleted = _g[1];
|
|
69
|
+
var _h = useState(null), popperElement = _h[0], setPopperElement = _h[1];
|
|
70
|
+
var _j = useState(null), labelValues = _j[0], setLabelValues = _j[1];
|
|
71
|
+
var _k = useState(null), currentLabelName = _k[0], setCurrentLabelName = _k[1];
|
|
72
|
+
var _l = usePopper(inputRef, popperElement, {
|
|
100
73
|
placement: 'bottom-start',
|
|
101
|
-
}), styles =
|
|
74
|
+
}), styles = _l.styles, attributes = _l.attributes;
|
|
102
75
|
var metadata = useGrpcMetadata();
|
|
103
76
|
var Spinner = useParcaTheme().loader;
|
|
104
|
-
var
|
|
77
|
+
var _m = useLabelNames(queryClient), labelNamesLoading = _m.loading, result = _m.result;
|
|
105
78
|
var labelNamesResponse = result.response, labelNamesError = result.error;
|
|
106
79
|
var LoadingSpinner = function () {
|
|
107
80
|
return _jsx("div", __assign({ className: "pt-2 pb-4" }, { children: Spinner }));
|
|
108
81
|
};
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
82
|
+
useEffect(function () {
|
|
83
|
+
if (currentLabelName !== null) {
|
|
84
|
+
var call = queryClient.values({ labelName: currentLabelName, match: [] }, { meta: metadata });
|
|
85
|
+
setLabelValuesLoading(true);
|
|
86
|
+
call.response
|
|
87
|
+
.then(function (response) {
|
|
88
|
+
setLabelValues(response.labelValues);
|
|
89
|
+
})
|
|
90
|
+
.catch(function () { return setLabelValues(null); })
|
|
91
|
+
.finally(function () { return setLabelValuesLoading(false); });
|
|
92
|
+
}
|
|
93
|
+
}, [currentLabelName, queryClient, metadata]);
|
|
119
94
|
var labelNames = (labelNamesError === undefined || labelNamesError == null) &&
|
|
120
95
|
labelNamesResponse !== undefined &&
|
|
121
96
|
labelNamesResponse != null
|
|
122
97
|
? labelNamesResponse.labelNames.filter(function (e) { return e !== '__name__'; })
|
|
123
98
|
: [];
|
|
124
|
-
var labelValues = labelValuesResponse !== undefined && labelValuesResponse != null ? labelValuesResponse : [];
|
|
125
99
|
var value = currentQuery.matchersString();
|
|
126
|
-
|
|
100
|
+
var suggestionSections = new Suggestions();
|
|
101
|
+
Query.suggest("".concat(currentQuery.profileName(), "{").concat(value)).forEach(function (s) {
|
|
127
102
|
// Skip suggestions that we just completed. This really only works,
|
|
128
103
|
// because we know the language is not repetitive. For a language that
|
|
129
104
|
// has a repeating word, this would not work.
|
|
@@ -133,96 +108,53 @@ var MatchersInput = function (_a) {
|
|
|
133
108
|
// Need to figure out if any literal suggestions make sense, but a
|
|
134
109
|
// closing bracket doesn't in the guided query experience because all
|
|
135
110
|
// we have the user do is type the matchers.
|
|
136
|
-
if (s.type ===
|
|
137
|
-
if (suggestionSections.literals.find(function (e) { return e.value === s.value; }) != null) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
111
|
+
if (s.type === 'literal' && s.value !== '}') {
|
|
140
112
|
suggestionSections.literals.push({
|
|
141
113
|
type: s.type,
|
|
142
|
-
typeahead:
|
|
114
|
+
typeahead: s.typeahead,
|
|
143
115
|
value: s.value,
|
|
144
116
|
});
|
|
145
|
-
suggestionSections.labelNames = [];
|
|
146
|
-
suggestionSections.labelValues = [];
|
|
147
117
|
}
|
|
148
|
-
if (s.type ===
|
|
118
|
+
if (s.type === 'labelName') {
|
|
149
119
|
var inputValue_1 = s.typeahead.trim().toLowerCase();
|
|
150
120
|
var inputLength_1 = inputValue_1.length;
|
|
151
121
|
var matches = labelNames.filter(function (label) {
|
|
152
122
|
return label.toLowerCase().slice(0, inputLength_1) === inputValue_1;
|
|
153
123
|
});
|
|
154
124
|
matches.forEach(function (m) {
|
|
155
|
-
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
suggestionSections.labelNames.push({
|
|
125
|
+
return suggestionSections.labelNames.push({
|
|
159
126
|
type: s.type,
|
|
160
127
|
typeahead: s.typeahead,
|
|
161
128
|
value: m,
|
|
162
129
|
});
|
|
163
|
-
suggestionSections.literals = [];
|
|
164
|
-
suggestionSections.labelValues = [];
|
|
165
130
|
});
|
|
166
131
|
}
|
|
167
|
-
if (s.type ===
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
132
|
+
if (s.type === 'labelValue') {
|
|
133
|
+
if (currentLabelName === null || s.labelName !== currentLabelName) {
|
|
134
|
+
setCurrentLabelName(s.labelName);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (labelValues !== null) {
|
|
138
|
+
labelValues
|
|
139
|
+
.filter(function (v) { return v.slice(0, s.typeahead.length) === s.typeahead; })
|
|
140
|
+
.forEach(function (v) {
|
|
141
|
+
return suggestionSections.labelValues.push({
|
|
142
|
+
type: s.type,
|
|
143
|
+
typeahead: s.typeahead,
|
|
144
|
+
value: v,
|
|
145
|
+
});
|
|
181
146
|
});
|
|
182
|
-
|
|
183
|
-
suggestionSections.literals = [];
|
|
184
|
-
});
|
|
147
|
+
}
|
|
185
148
|
}
|
|
186
149
|
});
|
|
187
150
|
var suggestionsLength = suggestionSections.literals.length +
|
|
188
151
|
suggestionSections.labelNames.length +
|
|
189
152
|
suggestionSections.labelValues.length;
|
|
190
|
-
var getLabelsFromMatchers = function (matchers) {
|
|
191
|
-
return matchers
|
|
192
|
-
.filter(function (matcher) { return matcher.key !== '__name__'; })
|
|
193
|
-
.map(function (matcher) { return "".concat(matcher.key).concat(matcher.matcherType).concat(addQuoteMarks(matcher.value)); });
|
|
194
|
-
};
|
|
195
|
-
useEffect(function () {
|
|
196
|
-
var matchers = currentQuery.matchers.filter(function (matcher) { return matcher.key !== '__name__'; });
|
|
197
|
-
if (matchers.length > 0) {
|
|
198
|
-
setCurrentLabelsCollection(getLabelsFromMatchers(matchers));
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
if (localMatchers !== null)
|
|
202
|
-
setCurrentLabelsCollection(getLabelsFromMatchers(localMatchers));
|
|
203
|
-
}
|
|
204
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
205
|
-
}, [currentQuery.matchers]);
|
|
206
153
|
var resetHighlight = function () { return setHighlightedSuggestionIndex(-1); };
|
|
207
154
|
var resetLastCompleted = function () { return setLastCompleted(new Suggestion('', '', '')); };
|
|
208
155
|
var onChange = function (e) {
|
|
209
156
|
var newValue = e.target.value;
|
|
210
|
-
|
|
211
|
-
if (suggestionSections.labelNames.length > 0) {
|
|
212
|
-
suggestionSections.labelNames = suggestionSections.labelNames.filter(function (suggestion) {
|
|
213
|
-
return suggestion.value.toLowerCase().includes(newValue.toLowerCase());
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
// this checks if the user has typed a label name and a literal (=/!=,=~,!~) i.e labelName=~, labelName!=~, labelName=, labelName!=
|
|
217
|
-
// and is about to type the label value, then it it will filter out the labelvalue list and move to the top
|
|
218
|
-
// the labelvalue that is most similar to what the user is typing.
|
|
219
|
-
if (suggestionSections.labelValues.length > 0 && labelNameLiteralRe.test(newValue)) {
|
|
220
|
-
var labelValueSearch_1 = newValue.split(literalRe)[2];
|
|
221
|
-
suggestionSections.labelValues = suggestionSections.labelValues.filter(function (suggestion) {
|
|
222
|
-
return suggestion.value.toLowerCase().includes(labelValueSearch_1.toLowerCase());
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
setInputRef(newValue);
|
|
157
|
+
setMatchersString(newValue);
|
|
226
158
|
resetLastCompleted();
|
|
227
159
|
resetHighlight();
|
|
228
160
|
};
|
|
@@ -230,16 +162,13 @@ var MatchersInput = function (_a) {
|
|
|
230
162
|
return value.slice(0, value.length - suggestion.typeahead.length) + suggestion.value;
|
|
231
163
|
};
|
|
232
164
|
var getSuggestion = function (index) {
|
|
233
|
-
if (suggestionSections.labelValues.length > 0) {
|
|
234
|
-
if (index < suggestionSections.labelValues.length) {
|
|
235
|
-
return suggestionSections.labelValues[index];
|
|
236
|
-
}
|
|
237
|
-
return suggestionSections.literals[index - suggestionSections.labelValues.length];
|
|
238
|
-
}
|
|
239
165
|
if (index < suggestionSections.labelNames.length) {
|
|
240
166
|
return suggestionSections.labelNames[index];
|
|
241
167
|
}
|
|
242
|
-
|
|
168
|
+
if (index < suggestionSections.labelNames.length + suggestionSections.literals.length) {
|
|
169
|
+
return suggestionSections.literals[index - suggestionSections.labelNames.length];
|
|
170
|
+
}
|
|
171
|
+
return suggestionSections.labelValues[index - suggestionSections.labelNames.length - suggestionSections.literals.length];
|
|
243
172
|
};
|
|
244
173
|
var highlightNext = function () {
|
|
245
174
|
var nextIndex = highlightedSuggestionIndex + 1;
|
|
@@ -259,157 +188,27 @@ var MatchersInput = function (_a) {
|
|
|
259
188
|
};
|
|
260
189
|
var applySuggestion = function (suggestionIndex) {
|
|
261
190
|
var suggestion = getSuggestion(suggestionIndex);
|
|
262
|
-
if (suggestion.type === Labels.labelValue) {
|
|
263
|
-
suggestion.value = addQuoteMarks(suggestion.value);
|
|
264
|
-
}
|
|
265
191
|
var newValue = complete(suggestion);
|
|
266
192
|
resetHighlight();
|
|
267
|
-
if (suggestion.type === Labels.labelName) {
|
|
268
|
-
getLabelNameValues(suggestion.value);
|
|
269
|
-
}
|
|
270
193
|
setLastCompleted(suggestion);
|
|
271
194
|
setMatchersString(newValue);
|
|
272
|
-
if (
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
setCurrentLabelsCollection(values_1);
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
setCurrentLabelsCollection(function (oldValues) { return __spreadArray(__spreadArray([], (oldValues !== null && oldValues !== void 0 ? oldValues : []), true), [
|
|
279
|
-
values_1[values_1.length - 1],
|
|
280
|
-
], false); });
|
|
281
|
-
}
|
|
282
|
-
setInputRef('');
|
|
283
|
-
focus();
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
if (lastCompleted.type === Labels.labelValue && suggestion.type === Labels.literal) {
|
|
287
|
-
setInputRef('');
|
|
288
|
-
focus();
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
if (currentLabelsCollection !== null) {
|
|
292
|
-
setInputRef(newValue.substring(newValue.lastIndexOf(',') + 1));
|
|
293
|
-
focus();
|
|
294
|
-
return;
|
|
195
|
+
if (inputRef !== null) {
|
|
196
|
+
inputRef.value = newValue;
|
|
197
|
+
inputRef.focus();
|
|
295
198
|
}
|
|
296
|
-
setInputRef(newValue);
|
|
297
|
-
focus();
|
|
298
199
|
};
|
|
299
200
|
var applyHighlightedSuggestion = function () {
|
|
300
201
|
applySuggestion(highlightedSuggestionIndex);
|
|
301
202
|
};
|
|
302
|
-
// This function adds quotes to the query expression if the user has typed it in manually, i.e. did not use the arrow up / down keys + Enter
|
|
303
|
-
// to choose the label name and value. Therefore, labelName=value becomes labelName="value".
|
|
304
|
-
var addQuotesToInputRefLabelValue = function (inputRef) {
|
|
305
|
-
var labelValue = inputRef.split(literalRe)[2].replaceAll(',', '');
|
|
306
|
-
var labelValueWithQuotes = addQuoteMarks(labelValue);
|
|
307
|
-
return inputRef.replace(labelValue, labelValueWithQuotes);
|
|
308
|
-
};
|
|
309
|
-
var handleKeyUp = function (event) {
|
|
310
|
-
var values = inputRef.replaceAll(',', '');
|
|
311
|
-
if (labelNameValueRe.test(inputRef)) {
|
|
312
|
-
if (currentLabelsCollection === null) {
|
|
313
|
-
setMatchersString(inputRef);
|
|
314
|
-
}
|
|
315
|
-
else {
|
|
316
|
-
setMatchersString((currentLabelsCollection === null || currentLabelsCollection === void 0 ? void 0 : currentLabelsCollection.join(',')) + ',' + values);
|
|
317
|
-
}
|
|
318
|
-
setInputRef('');
|
|
319
|
-
}
|
|
320
|
-
if (event.key === ',') {
|
|
321
|
-
if (inputRef.length === 0)
|
|
322
|
-
event.preventDefault();
|
|
323
|
-
// If the current typed query expression matches the labelNameValueWithoutQuotesRe regex (i.e. the labelvalue is not quoted), then add quotes to the labelvalue.
|
|
324
|
-
// if not, just use the current inputRef value.
|
|
325
|
-
var inputValues_1 = labelNameValueWithoutQuotesRe.test(inputRef)
|
|
326
|
-
? inputRef.replaceAll(',', '')
|
|
327
|
-
: addQuotesToInputRefLabelValue(inputRef).replaceAll(',', '');
|
|
328
|
-
// if the currentLabelsCollection array is null, we don't need to concat the current inputRef value with the currentLabelsCollection array, so we just push to it.
|
|
329
|
-
if (currentLabelsCollection === null) {
|
|
330
|
-
setCurrentLabelsCollection([inputValues_1]);
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
setCurrentLabelsCollection(function (oldValues) {
|
|
334
|
-
// Don't add the current inputRef value to the currentLabelsCollection array if it doesn't match the regex because that will cause an API error.
|
|
335
|
-
if (!labelNameValueRe.test(inputRef))
|
|
336
|
-
return oldValues;
|
|
337
|
-
return __spreadArray(__spreadArray([], (oldValues !== null && oldValues !== void 0 ? oldValues : []), true), [inputValues_1], false);
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
// update the currentQuery expression with the currentLabelsCollection array if it's not null, otherwise use the current inputRef value.
|
|
341
|
-
setMatchersString(currentLabelsCollection !== null
|
|
342
|
-
? "".concat(currentLabelsCollection === null || currentLabelsCollection === void 0 ? void 0 : currentLabelsCollection.join(','), ",").concat(inputValues_1)
|
|
343
|
-
: "".concat(inputValues_1, ","));
|
|
344
|
-
setInputRef('');
|
|
345
|
-
}
|
|
346
|
-
// We suggest the appropriate label names and label values when a user is typing, depending on what the user has typed.
|
|
347
|
-
// For example, if the user types "labelName=", we suggest the label values next.
|
|
348
|
-
// This bit of code is used for the opposite of the above bit of code, when a user is deleting characters by pressing del/backspace
|
|
349
|
-
// We update the currentQuery expression with what's in the inputRef value so that the suggestions are updated accordingly.
|
|
350
|
-
if (event.key === 'Backspace' && inputRef.length > 0) {
|
|
351
|
-
// if the currentLabelsCollection array is not empty i.e has already previously completed expressions, then we first need to turn the array into a string
|
|
352
|
-
// so it can be concatenated with the current inputRef value. that becomes something like "labelName="value",newLabelName="val
|
|
353
|
-
if (currentLabelsCollection != null && currentLabelsCollection.length > 0) {
|
|
354
|
-
setMatchersString("".concat(currentLabelsCollection === null || currentLabelsCollection === void 0 ? void 0 : currentLabelsCollection.join(','), ",").concat(inputRef, "}"));
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
// if not, we jsut update the currentQuery expression with the current inputRef value.
|
|
358
|
-
setMatchersString(inputRef);
|
|
359
|
-
}
|
|
360
|
-
if (currentLabelsCollection === null && inputRef.length === 0) {
|
|
361
|
-
setMatchersString('');
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
};
|
|
365
203
|
var handleKeyPress = function (event) {
|
|
366
204
|
// If there is a highlighted suggestion and enter is hit, we complete
|
|
367
205
|
// with the highlighted suggestion.
|
|
368
206
|
if (highlightedSuggestionIndex >= 0 && event.key === 'Enter') {
|
|
369
207
|
applyHighlightedSuggestion();
|
|
370
|
-
if (lastCompleted.type === Labels.labelValue)
|
|
371
|
-
setLabelValuesResponse(null);
|
|
372
|
-
var matchers_1 = currentQuery.matchers.filter(function (matcher) { return matcher.key !== '__name__'; });
|
|
373
|
-
setLocalMatchers(function (prevState) {
|
|
374
|
-
if (inputRef.length > 0)
|
|
375
|
-
return prevState;
|
|
376
|
-
if (matchers_1.length === 0)
|
|
377
|
-
return prevState;
|
|
378
|
-
return matchers_1;
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
// If a user has manually typed in a label name that actually exists in the list of label name (and did not use the
|
|
382
|
-
// highlight + arrow keys up/down + Enter/Mouse click method to complete it), and has also typed a literal value, i.e. labelName=,
|
|
383
|
-
// then we can apply a suggestion using the typed label name. This will be as if the user had highlighted the label name and hit enter.
|
|
384
|
-
if (event.key === '!' || event.key === '~' || event.key === '=') {
|
|
385
|
-
var labelName_1 = inputRef.split(literalRe)[0];
|
|
386
|
-
if (suggestionSections.labelNames.length > 0) {
|
|
387
|
-
// Find the label name in the suggestion list and get the index
|
|
388
|
-
var suggestion = suggestionSections.labelNames.find(function (suggestion) { return suggestion.value === labelName_1; });
|
|
389
|
-
// If the typed label name exists, we can apply it using the applySuggestion function
|
|
390
|
-
if (suggestion != null) {
|
|
391
|
-
applySuggestion(suggestionSections.labelNames.indexOf(suggestion));
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
// Same as above, If a user has typed in a label name and literal (and did not use the suggestion box to complete it),
|
|
396
|
-
// we can manually show the next set of suggestions, which are the label values, by applying a literal suggestion.
|
|
397
|
-
if (labelNameLiteralRe.test(inputRef)) {
|
|
398
|
-
var literal_1 = inputRef.split(literalRe)[1];
|
|
399
|
-
if (suggestionSections.literals.length > 0) {
|
|
400
|
-
// Find the literal in the suggestion list and get the index
|
|
401
|
-
var suggestion = suggestionSections.literals.find(function (suggestion) { return suggestion.value === literal_1; });
|
|
402
|
-
// If the typed literal exists, we can apply it using the applySuggestion function
|
|
403
|
-
if (suggestion != null) {
|
|
404
|
-
applySuggestion(suggestionSections.literals.indexOf(suggestion));
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
208
|
}
|
|
408
209
|
// If no suggestions is highlighted and we hit enter, we run the query,
|
|
409
210
|
// and hide suggestions until another actions enables them again.
|
|
410
211
|
if (highlightedSuggestionIndex === -1 && event.key === 'Enter') {
|
|
411
|
-
if (lastCompleted.type === 'labelValue')
|
|
412
|
-
setLabelValuesResponse(null);
|
|
413
212
|
setShowSuggest(false);
|
|
414
213
|
runQuery();
|
|
415
214
|
return;
|
|
@@ -417,12 +216,6 @@ var MatchersInput = function (_a) {
|
|
|
417
216
|
setShowSuggest(true);
|
|
418
217
|
};
|
|
419
218
|
var handleKeyDown = function (event) {
|
|
420
|
-
if (event.key === 'Backspace' && inputRef === '') {
|
|
421
|
-
if (currentLabelsCollection === null)
|
|
422
|
-
return;
|
|
423
|
-
removeLabel(currentLabelsCollection.length - 1);
|
|
424
|
-
removeLocalMatcher();
|
|
425
|
-
}
|
|
426
219
|
// Don't need to handle any key interactions if no suggestions there.
|
|
427
220
|
if (suggestionsLength === 0) {
|
|
428
221
|
return;
|
|
@@ -454,26 +247,22 @@ var MatchersInput = function (_a) {
|
|
|
454
247
|
setFocusedInput(false);
|
|
455
248
|
resetHighlight();
|
|
456
249
|
};
|
|
457
|
-
var
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
var newLabelsAsAString = newLabels.join(',');
|
|
464
|
-
setMatchersString(newLabelsAsAString);
|
|
465
|
-
};
|
|
466
|
-
var removeLocalMatcher = function () {
|
|
467
|
-
if (localMatchers === null)
|
|
468
|
-
return;
|
|
469
|
-
var newMatchers = __spreadArray([], localMatchers, true);
|
|
470
|
-
newMatchers.splice(localMatchers.length - 1, 1);
|
|
471
|
-
setLocalMatchers(newMatchers);
|
|
472
|
-
};
|
|
473
|
-
var profileSelected = currentQuery.profType.profileName === '';
|
|
474
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", __assign({ ref: setDivInputRef, className: "w-full flex items-center text-sm border-gray-300 dark:border-gray-600 border-b" }, { children: [_jsx("ul", __assign({ className: "flex space-x-2" }, { children: currentLabelsCollection === null || currentLabelsCollection === void 0 ? void 0 : currentLabelsCollection.map(function (value, i) { return (_jsx("li", __assign({ className: "bg-indigo-600 w-fit py-1 px-2 text-gray-100 dark-gray-900 rounded-md" }, { children: value }), i)); }) })), _jsx("input", { type: "text", className: cx('bg-transparent focus:ring-indigo-800 flex-1 block w-full px-2 py-2 text-sm outline-none', profileSelected && 'cursor-not-allowed'), placeholder: profileSelected ? 'Select a profile first to query profiles...' : 'query profiles...', onChange: onChange, value: inputRef, onBlur: unfocus, onFocus: focus, onKeyPress: handleKeyPress, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, disabled: profileSelected, title: profileSelected ? 'Select a profile first to query profiles...' : 'query profiles...' })] })), _jsx("div", __assign({ ref: setPopperElement, style: __assign(__assign({}, styles.popper), { marginLeft: 0 }) }, attributes.popper, { className: "z-50" }, { children: _jsx(Transition, __assign({ show: focusedInput && showSuggest, as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0" }, { children: _jsxs("div", __assign({ style: { width: divInputRef === null || divInputRef === void 0 ? void 0 : divInputRef.offsetWidth }, className: "absolute z-10 max-h-[400px] mt-1 bg-gray-50 dark:bg-gray-900 shadow-lg rounded-md text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" }, { children: [labelNamesLoading ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestionSections.labelNames.map(function (l, i) { return (_jsx("div", __assign({ className: cx(highlightedSuggestionIndex === i && 'text-white bg-indigo-600', 'cursor-default select-none relative py-2 pl-3 pr-9'), onMouseOver: function () { return setHighlightedSuggestionIndex(i); }, onClick: function () { return applySuggestion(i); }, onMouseOut: function () { return resetHighlight(); } }, { children: l.value }), i)); }) })), suggestionSections.literals.map(function (l, i) { return (_jsx("div", __assign({ className: cx(highlightedSuggestionIndex === i + suggestionSections.labelNames.length &&
|
|
250
|
+
var profileSelected = currentQuery.profileName() === '';
|
|
251
|
+
return (_jsxs("div", __assign({ className: "font-mono flex-1 w-full block" }, { children: [_jsx("input", { ref: setInputRef, type: "text", className: cx('bg-transparent focus:ring-indigo-800 flex-1 block w-full px-2 py-2 text-sm outline-none', profileSelected && 'cursor-not-allowed'), placeholder: profileSelected
|
|
252
|
+
? 'Select a profile first to enter a filter...'
|
|
253
|
+
: 'filter profiles... eg. node="test"', onChange: onChange, value: value, onBlur: unfocus, onFocus: focus, onKeyPress: handleKeyPress, onKeyDown: handleKeyDown, disabled: profileSelected, title: profileSelected
|
|
254
|
+
? 'Select a profile first to enter a filter...'
|
|
255
|
+
: 'filter profiles... eg. node="test"' }), suggestionsLength > 0 && (_jsx("div", __assign({ ref: setPopperElement, style: __assign(__assign({}, styles.popper), { marginLeft: 0 }) }, attributes.popper, { className: "z-50" }, { children: _jsx(Transition, __assign({ show: focusedInput && showSuggest, as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0" }, { children: _jsxs("div", __assign({ style: { width: inputRef === null || inputRef === void 0 ? void 0 : inputRef.offsetWidth }, className: "absolute z-10 max-h-[400px] mt-1 bg-gray-50 dark:bg-gray-900 shadow-lg rounded-md text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" }, { children: [labelNamesLoading ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestionSections.labelNames.map(function (l, i) { return (_jsx("div", __assign({ className: cx(highlightedSuggestionIndex === i && 'text-white bg-indigo-600', 'cursor-default select-none relative py-2 pl-3 pr-9'), onMouseOver: function () { return setHighlightedSuggestionIndex(i); }, onClick: function () { return applySuggestion(i); }, onMouseOut: function () { return resetHighlight(); } }, { children: l.value }), i)); }) })), suggestionSections.literals.map(function (l, i) { return (_jsx("div", __assign({ className: cx(highlightedSuggestionIndex === i + suggestionSections.labelNames.length &&
|
|
475
256
|
'text-white bg-indigo-600', 'cursor-default select-none relative py-2 pl-3 pr-9'), onMouseOver: function () {
|
|
476
257
|
return setHighlightedSuggestionIndex(i + suggestionSections.labelNames.length);
|
|
477
|
-
}, onClick: function () { return applySuggestion(i + suggestionSections.labelNames.length); }, onMouseOut: function () { return resetHighlight(); } }, { children: l.value }), i)); }), labelValuesLoading && lastCompleted.type === 'literal' ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestionSections.labelValues.map(function (l, i) { return (_jsx("div", __assign({ className: cx(highlightedSuggestionIndex === i && 'text-white bg-indigo-600', 'cursor-default select-none relative py-2 pl-3 pr-9'), onMouseOver: function () {
|
|
258
|
+
}, onClick: function () { return applySuggestion(i + suggestionSections.labelNames.length); }, onMouseOut: function () { return resetHighlight(); } }, { children: l.value }), i)); }), labelValuesLoading && lastCompleted.type === 'literal' ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestionSections.labelValues.map(function (l, i) { return (_jsx("div", __assign({ className: cx(highlightedSuggestionIndex === i && 'text-white bg-indigo-600', 'cursor-default select-none relative py-2 pl-3 pr-9'), onMouseOver: function () {
|
|
259
|
+
return setHighlightedSuggestionIndex(i +
|
|
260
|
+
suggestionSections.labelNames.length +
|
|
261
|
+
suggestionSections.literals.length);
|
|
262
|
+
}, onClick: function () {
|
|
263
|
+
return applySuggestion(i +
|
|
264
|
+
suggestionSections.labelNames.length +
|
|
265
|
+
suggestionSections.literals.length);
|
|
266
|
+
}, onMouseOut: function () { return resetHighlight(); } }, { children: l.value }), i)); }) }))] })) })) })))] })));
|
|
478
267
|
};
|
|
479
268
|
export default MatchersInput;
|
|
@@ -12,11 +12,11 @@ interface ProfileMetricsGraphProps {
|
|
|
12
12
|
setTimeRange: (range: DateTimeRange) => void;
|
|
13
13
|
addLabelMatcher: (key: string, value: string) => void;
|
|
14
14
|
}
|
|
15
|
-
export interface
|
|
15
|
+
export interface IQueryRangeState {
|
|
16
16
|
response: QueryRangeResponse | null;
|
|
17
17
|
isLoading: boolean;
|
|
18
18
|
error: RpcError | null;
|
|
19
19
|
}
|
|
20
|
-
export declare const useQueryRange: (client: QueryServiceClient, queryExpression: string, start: number, end: number) =>
|
|
20
|
+
export declare const useQueryRange: (client: QueryServiceClient, queryExpression: string, start: number, end: number) => IQueryRangeState;
|
|
21
21
|
declare const ProfileMetricsGraph: ({ queryClient, queryExpression, profile, from, to, select, setTimeRange, addLabelMatcher, }: ProfileMetricsGraphProps) => JSX.Element;
|
|
22
22
|
export default ProfileMetricsGraph;
|
|
@@ -66,43 +66,35 @@ import { useGrpcMetadata, useParcaTheme } from '@parca/components';
|
|
|
66
66
|
import { Query } from '@parca/parser';
|
|
67
67
|
import useDelayedLoader from '../useDelayedLoader';
|
|
68
68
|
export var useQueryRange = function (client, queryExpression, start, end) {
|
|
69
|
-
var _a = useState(
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
var _a = useState({
|
|
70
|
+
response: null,
|
|
71
|
+
isLoading: true,
|
|
72
|
+
error: null,
|
|
73
|
+
}), state = _a[0], setState = _a[1];
|
|
72
74
|
var metadata = useGrpcMetadata();
|
|
73
75
|
useEffect(function () {
|
|
74
76
|
void (function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
75
|
-
var
|
|
77
|
+
var call;
|
|
76
78
|
return __generator(this, function (_a) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return [3 /*break*/, 5];
|
|
93
|
-
case 3:
|
|
94
|
-
e_1 = _a.sent();
|
|
95
|
-
setError(e_1);
|
|
96
|
-
return [3 /*break*/, 5];
|
|
97
|
-
case 4:
|
|
98
|
-
setIsLoading(false);
|
|
99
|
-
return [7 /*endfinally*/];
|
|
100
|
-
case 5: return [2 /*return*/];
|
|
101
|
-
}
|
|
79
|
+
setState({
|
|
80
|
+
response: null,
|
|
81
|
+
isLoading: true,
|
|
82
|
+
error: null,
|
|
83
|
+
});
|
|
84
|
+
call = client.queryRange({
|
|
85
|
+
query: queryExpression,
|
|
86
|
+
start: Timestamp.fromDate(new Date(start)),
|
|
87
|
+
end: Timestamp.fromDate(new Date(end)),
|
|
88
|
+
limit: 0,
|
|
89
|
+
}, { meta: metadata });
|
|
90
|
+
call.response
|
|
91
|
+
.then(function (response) { return setState({ response: response, isLoading: false, error: null }); })
|
|
92
|
+
.catch(function (error) { return setState({ response: null, isLoading: false, error: error }); });
|
|
93
|
+
return [2 /*return*/];
|
|
102
94
|
});
|
|
103
95
|
}); })();
|
|
104
96
|
}, [client, queryExpression, start, end, metadata]);
|
|
105
|
-
return
|
|
97
|
+
return state;
|
|
106
98
|
};
|
|
107
99
|
var ProfileMetricsGraph = function (_a) {
|
|
108
100
|
var queryClient = _a.queryClient, queryExpression = _a.queryExpression, profile = _a.profile, from = _a.from, to = _a.to, select = _a.select, setTimeRange = _a.setTimeRange, addLabelMatcher = _a.addLabelMatcher;
|