@parca/profile 0.16.467 → 0.16.468
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 +4 -0
- package/dist/MatchersInput/SuggestionsList.d.ts +2 -1
- package/dist/MatchersInput/SuggestionsList.d.ts.map +1 -1
- package/dist/MatchersInput/SuggestionsList.js +6 -2
- package/dist/MatchersInput/index.d.ts +4 -2
- package/dist/MatchersInput/index.d.ts.map +1 -1
- package/dist/MatchersInput/index.js +51 -22
- package/dist/ProfileSelector/index.d.ts +7 -1
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +3 -2
- package/dist/SimpleMatchers/index.d.ts +4 -2
- package/dist/SimpleMatchers/index.d.ts.map +1 -1
- package/dist/SimpleMatchers/index.js +46 -32
- package/dist/contexts/MatchersInputLabelsContext.d.ts +25 -0
- package/dist/contexts/MatchersInputLabelsContext.d.ts.map +1 -0
- package/dist/contexts/MatchersInputLabelsContext.js +75 -0
- package/dist/contexts/SimpleMatchersLabelContext.d.ts +17 -0
- package/dist/contexts/SimpleMatchersLabelContext.d.ts.map +1 -0
- package/dist/contexts/SimpleMatchersLabelContext.js +45 -0
- package/dist/contexts/UtilizationLabelsContext.d.ts +14 -0
- package/dist/contexts/UtilizationLabelsContext.d.ts.map +1 -0
- package/dist/contexts/UtilizationLabelsContext.js +25 -0
- package/package.json +2 -2
- package/src/MatchersInput/SuggestionsList.tsx +9 -2
- package/src/MatchersInput/index.tsx +71 -27
- package/src/ProfileSelector/index.tsx +65 -54
- package/src/SimpleMatchers/index.tsx +65 -33
- package/src/contexts/MatchersInputLabelsContext.tsx +130 -0
- package/src/contexts/SimpleMatchersLabelContext.tsx +78 -0
- package/src/contexts/UtilizationLabelsContext.tsx +44 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
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.468](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.467...@parca/profile@0.16.468) (2025-02-04)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
6
10
|
## [0.16.467](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.466...@parca/profile@0.16.467) (2025-01-22)
|
|
7
11
|
|
|
8
12
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -18,7 +18,8 @@ interface Props {
|
|
|
18
18
|
focusedInput: boolean;
|
|
19
19
|
isLabelNamesLoading: boolean;
|
|
20
20
|
isLabelValuesLoading: boolean;
|
|
21
|
+
shouldTrimPrefix: boolean;
|
|
21
22
|
}
|
|
22
|
-
declare const SuggestionsList: ({ suggestions, applySuggestion, inputRef, runQuery, focusedInput, isLabelNamesLoading, isLabelValuesLoading, }: Props) => JSX.Element;
|
|
23
|
+
declare const SuggestionsList: ({ suggestions, applySuggestion, inputRef, runQuery, focusedInput, isLabelNamesLoading, isLabelValuesLoading, shouldTrimPrefix, }: Props) => JSX.Element;
|
|
23
24
|
export default SuggestionsList;
|
|
24
25
|
//# sourceMappingURL=SuggestionsList.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SuggestionsList.d.ts","sourceRoot":"","sources":["../../src/MatchersInput/SuggestionsList.tsx"],"names":[],"mappings":"AAsBA,qBAAa,UAAU;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;gBAEF,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAK3D;AAED,qBAAa,WAAW;IACtB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,UAAU,EAAE,UAAU,EAAE,CAAC;IACzB,WAAW,EAAE,UAAU,EAAE,CAAC;;CAO3B;AAED,UAAU,KAAK;IACb,WAAW,EAAE,WAAW,CAAC;IACzB,eAAe,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAClD,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SuggestionsList.d.ts","sourceRoot":"","sources":["../../src/MatchersInput/SuggestionsList.tsx"],"names":[],"mappings":"AAsBA,qBAAa,UAAU;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;gBAEF,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAK3D;AAED,qBAAa,WAAW;IACtB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,UAAU,EAAE,UAAU,EAAE,CAAC;IACzB,WAAW,EAAE,UAAU,EAAE,CAAC;;CAO3B;AAED,UAAU,KAAK;IACb,WAAW,EAAE,WAAW,CAAC;IACzB,eAAe,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAClD,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAaD,QAAA,MAAM,eAAe,qIASlB,KAAK,KAAG,GAAG,CAAC,OA6Nd,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -34,7 +34,11 @@ const LoadingSpinner = () => {
|
|
|
34
34
|
const { loader: Spinner } = useParcaContext();
|
|
35
35
|
return _jsx("div", { className: "pt-2 pb-4", children: Spinner });
|
|
36
36
|
};
|
|
37
|
-
const
|
|
37
|
+
const transformLabelsForSuggestions = (labelNames, shouldTrimPrefix = false) => {
|
|
38
|
+
const trimmedLabel = shouldTrimPrefix ? labelNames.split('.').pop() ?? labelNames : labelNames;
|
|
39
|
+
return trimmedLabel;
|
|
40
|
+
};
|
|
41
|
+
const SuggestionsList = ({ suggestions, applySuggestion, inputRef, runQuery, focusedInput, isLabelNamesLoading, isLabelValuesLoading, shouldTrimPrefix = false, }) => {
|
|
38
42
|
const [popperElement, setPopperElement] = useState(null);
|
|
39
43
|
const { styles, attributes } = usePopper(inputRef, popperElement, {
|
|
40
44
|
placement: 'bottom-start',
|
|
@@ -132,7 +136,7 @@ const SuggestionsList = ({ suggestions, applySuggestion, inputRef, runQuery, foc
|
|
|
132
136
|
inputRef.removeEventListener('keypress', handleKeyPress);
|
|
133
137
|
};
|
|
134
138
|
}, [inputRef, highlightedSuggestionIndex, suggestions, handleKeyPress, handleKeyDown]);
|
|
135
|
-
return (_jsx(_Fragment, { children: suggestionsLength > 0 && (_jsx("div", { ref: setPopperElement, style: { ...styles.popper, marginLeft: 0 }, ...attributes.popper, className: "z-50", children: _jsx(Transition, { show: focusedInput && showSuggest, as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0", children: _jsxs("div", { style: { width: inputRef?.offsetWidth }, className: "absolute z-10 mt-1 max-h-[400px] overflow-auto rounded-md bg-gray-50 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-900 sm:text-sm", children: [isLabelNamesLoading ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestions.labelNames.map((l, i) => (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex === i, onHighlight: () => setHighlightedSuggestionIndex(i), onApplySuggestion: () => applySuggestionWithIndex(i), onResetHighlight: () => resetHighlight(), value: l.value }, l.value))) })), suggestions.literals.map((l, i) => (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex === i + suggestions.labelNames.length, onHighlight: () => setHighlightedSuggestionIndex(i + suggestions.labelNames.length), onApplySuggestion: () => applySuggestionWithIndex(i + suggestions.labelNames.length), onResetHighlight: () => resetHighlight(), value: l.value }, l.value))), isLabelValuesLoading ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestions.labelValues.map((l, i) => (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex ===
|
|
139
|
+
return (_jsx(_Fragment, { children: suggestionsLength > 0 && (_jsx("div", { ref: setPopperElement, style: { ...styles.popper, marginLeft: 0 }, ...attributes.popper, className: "z-50", children: _jsx(Transition, { show: focusedInput && showSuggest, as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0", children: _jsxs("div", { style: { width: inputRef?.offsetWidth }, className: "absolute z-10 mt-1 max-h-[400px] overflow-auto rounded-md bg-gray-50 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-900 sm:text-sm", children: [isLabelNamesLoading ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestions.labelNames.map((l, i) => (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex === i, onHighlight: () => setHighlightedSuggestionIndex(i), onApplySuggestion: () => applySuggestionWithIndex(i), onResetHighlight: () => resetHighlight(), value: transformLabelsForSuggestions(l.value, shouldTrimPrefix) }, transformLabelsForSuggestions(l.value, shouldTrimPrefix)))) })), suggestions.literals.map((l, i) => (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex === i + suggestions.labelNames.length, onHighlight: () => setHighlightedSuggestionIndex(i + suggestions.labelNames.length), onApplySuggestion: () => applySuggestionWithIndex(i + suggestions.labelNames.length), onResetHighlight: () => resetHighlight(), value: l.value }, l.value))), isLabelValuesLoading ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestions.labelValues.map((l, i) => (_jsx(SuggestionItem, { isHighlighted: highlightedSuggestionIndex ===
|
|
136
140
|
i + suggestions.labelNames.length + suggestions.literals.length, onHighlight: () => setHighlightedSuggestionIndex(i + suggestions.labelNames.length + suggestions.literals.length), onApplySuggestion: () => applySuggestionWithIndex(i + suggestions.labelNames.length + suggestions.literals.length), onResetHighlight: () => resetHighlight(), value: l.value }, l.value))) }))] }) }) })) }));
|
|
137
141
|
};
|
|
138
142
|
export default SuggestionsList;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LabelsResponse, QueryServiceClient } from '@parca/client';
|
|
2
2
|
import { Query } from '@parca/parser';
|
|
3
|
+
import { UtilizationLabels } from '../ProfileSelector';
|
|
3
4
|
interface MatchersInputProps {
|
|
4
5
|
queryClient: QueryServiceClient;
|
|
5
6
|
setMatchersString: (arg: string) => void;
|
|
@@ -24,6 +25,7 @@ interface UseLabelValues {
|
|
|
24
25
|
loading: boolean;
|
|
25
26
|
}
|
|
26
27
|
export declare const useLabelValues: (client: QueryServiceClient, labelName: string, profileType: string) => UseLabelValues;
|
|
27
|
-
declare const
|
|
28
|
-
export default
|
|
28
|
+
export declare const useFetchUtilizationLabelValues: (labelName: string, utilizationLabels?: UtilizationLabels) => string[];
|
|
29
|
+
export default function MatchersInputWithProvider(props: MatchersInputProps): JSX.Element;
|
|
30
|
+
export {};
|
|
29
31
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/MatchersInput/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/MatchersInput/index.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAAgB,cAAc,EAAE,kBAAkB,EAAgB,MAAM,eAAe,CAAC;AAE/F,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAGpC,OAAO,EAAC,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAKrD,UAAU,kBAAkB;IAC1B,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,aAAa;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,aAAa,WAChB,kBAAkB,eACb,MAAM,UACX,MAAM,QACR,MAAM,UACJ,MAAM,EAAE,KACf,aAyBF,CAAC;AAEF,UAAU,cAAc;IACtB,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,EAAE,KAAK,CAAC;KACf,CAAC;IACF,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,cAAc,WACjB,kBAAkB,aACf,MAAM,eACJ,MAAM,KAClB,cAsBF,CAAC;AAEF,eAAO,MAAM,8BAA8B,cAC9B,MAAM,sBACG,iBAAiB,KACpC,MAAM,EASR,CAAC;AAiMF,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAAC,KAAK,EAAE,kBAAkB,GAAG,GAAG,CAAC,OAAO,CAMxF"}
|
|
@@ -12,11 +12,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
// See the License for the specific language governing permissions and
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
import { useMemo, useRef, useState } from 'react';
|
|
15
|
+
import { useQuery } from '@tanstack/react-query';
|
|
15
16
|
import cx from 'classnames';
|
|
16
17
|
import TextareaAutosize from 'react-textarea-autosize';
|
|
17
18
|
import { useGrpcMetadata } from '@parca/components';
|
|
18
19
|
import { Query } from '@parca/parser';
|
|
19
20
|
import { millisToProtoTimestamp, sanitizeLabelValue } from '@parca/utilities';
|
|
21
|
+
import { LabelsProvider, useLabels } from '../contexts/MatchersInputLabelsContext';
|
|
20
22
|
import useGrpcQuery from '../useGrpcQuery';
|
|
21
23
|
import SuggestionsList, { Suggestion, Suggestions } from './SuggestionsList';
|
|
22
24
|
export const useLabelNames = (client, profileType, start, end, match) => {
|
|
@@ -63,21 +65,20 @@ export const useLabelValues = (client, labelName, profileType) => {
|
|
|
63
65
|
});
|
|
64
66
|
return { result: { response: data ?? [], error: error }, loading: isLoading };
|
|
65
67
|
};
|
|
66
|
-
const
|
|
68
|
+
export const useFetchUtilizationLabelValues = (labelName, utilizationLabels) => {
|
|
69
|
+
const { data } = useQuery({
|
|
70
|
+
queryKey: ['utilizationLabelValues', labelName],
|
|
71
|
+
queryFn: async () => {
|
|
72
|
+
return await utilizationLabels?.utilizationFetchLabelValues?.(labelName);
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
return data ?? [];
|
|
76
|
+
};
|
|
77
|
+
const MatchersInput = ({ setMatchersString, runQuery, currentQuery, }) => {
|
|
67
78
|
const inputRef = useRef(null);
|
|
68
79
|
const [focusedInput, setFocusedInput] = useState(false);
|
|
69
80
|
const [lastCompleted, setLastCompleted] = useState(new Suggestion('', '', ''));
|
|
70
|
-
const
|
|
71
|
-
const { loading: labelNamesLoading, result } = useLabelNames(queryClient, profileType);
|
|
72
|
-
const { response: labelNamesResponse, error: labelNamesError } = result;
|
|
73
|
-
const { loading: labelValuesLoading, result: { response: labelValues }, } = useLabelValues(queryClient, currentLabelName ?? '', profileType);
|
|
74
|
-
const labelNames = useMemo(() => {
|
|
75
|
-
return (labelNamesError === undefined || labelNamesError == null) &&
|
|
76
|
-
labelNamesResponse !== undefined &&
|
|
77
|
-
labelNamesResponse != null
|
|
78
|
-
? labelNamesResponse.labelNames.filter(e => e !== '__name__')
|
|
79
|
-
: [];
|
|
80
|
-
}, [labelNamesError, labelNamesResponse]);
|
|
81
|
+
const { labelNames, labelValues, labelNameMappings, isLabelNamesLoading, isLabelValuesLoading, currentLabelName, setCurrentLabelName, shouldHandlePrefixes, } = useLabels();
|
|
81
82
|
const value = currentQuery.matchersString();
|
|
82
83
|
const suggestionSections = useMemo(() => {
|
|
83
84
|
const suggestionSections = new Suggestions();
|
|
@@ -104,15 +105,31 @@ const MatchersInput = ({ queryClient, setMatchersString, runQuery, currentQuery,
|
|
|
104
105
|
const matches = labelNames.filter(function (label) {
|
|
105
106
|
return label.toLowerCase().slice(0, inputLength) === inputValue;
|
|
106
107
|
});
|
|
107
|
-
matches.forEach(m =>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
matches.forEach(m => {
|
|
109
|
+
const suggestion = {
|
|
110
|
+
type: s.type,
|
|
111
|
+
typeahead: s.typeahead,
|
|
112
|
+
value: m,
|
|
113
|
+
};
|
|
114
|
+
if (shouldHandlePrefixes) {
|
|
115
|
+
const mapping = labelNameMappings.find(l => l.displayName === m);
|
|
116
|
+
if (mapping != null) {
|
|
117
|
+
suggestion.fullName = mapping.fullName;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
suggestionSections.labelNames.push(suggestion);
|
|
121
|
+
});
|
|
112
122
|
}
|
|
113
123
|
if (s.type === 'labelValue') {
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
let labelNameForQuery = s.labelName;
|
|
125
|
+
if (shouldHandlePrefixes) {
|
|
126
|
+
const mapping = labelNameMappings.find(l => l.displayName === s.labelName);
|
|
127
|
+
if (mapping != null) {
|
|
128
|
+
labelNameForQuery = mapping.fullName;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (currentLabelName === null || labelNameForQuery !== currentLabelName) {
|
|
132
|
+
setCurrentLabelName(labelNameForQuery);
|
|
116
133
|
return;
|
|
117
134
|
}
|
|
118
135
|
if (labelValues !== null) {
|
|
@@ -127,7 +144,17 @@ const MatchersInput = ({ queryClient, setMatchersString, runQuery, currentQuery,
|
|
|
127
144
|
}
|
|
128
145
|
});
|
|
129
146
|
return suggestionSections;
|
|
130
|
-
}, [
|
|
147
|
+
}, [
|
|
148
|
+
currentQuery,
|
|
149
|
+
lastCompleted,
|
|
150
|
+
labelNames,
|
|
151
|
+
labelValues,
|
|
152
|
+
currentLabelName,
|
|
153
|
+
value,
|
|
154
|
+
shouldHandlePrefixes,
|
|
155
|
+
labelNameMappings,
|
|
156
|
+
setCurrentLabelName,
|
|
157
|
+
]);
|
|
131
158
|
const resetLastCompleted = () => setLastCompleted(new Suggestion('', '', ''));
|
|
132
159
|
const onChange = (e) => {
|
|
133
160
|
const newValue = e.target.value;
|
|
@@ -166,6 +193,8 @@ const MatchersInput = ({ queryClient, setMatchersString, runQuery, currentQuery,
|
|
|
166
193
|
? 'Select a profile first to enter a filter...'
|
|
167
194
|
: 'filter profiles... eg. node="test"', onChange: onChange, value: value, onBlur: unfocus, onFocus: focus, disabled: profileSelected, title: profileSelected
|
|
168
195
|
? 'Select a profile first to enter a filter...'
|
|
169
|
-
: 'filter profiles... eg. node="test"', id: "matchers-input" }), _jsx(SuggestionsList, { isLabelNamesLoading:
|
|
196
|
+
: 'filter profiles... eg. node="test"', id: "matchers-input" }), _jsx(SuggestionsList, { isLabelNamesLoading: isLabelNamesLoading, suggestions: suggestionSections, applySuggestion: applySuggestion, inputRef: inputRef.current, runQuery: runQuery, focusedInput: focusedInput, isLabelValuesLoading: isLabelValuesLoading && lastCompleted.type === 'literal', shouldTrimPrefix: shouldHandlePrefixes })] }));
|
|
170
197
|
};
|
|
171
|
-
export default
|
|
198
|
+
export default function MatchersInputWithProvider(props) {
|
|
199
|
+
return (_jsx(LabelsProvider, { queryClient: props.queryClient, profileType: props.profileType, children: _jsx(MatchersInput, { ...props }) }));
|
|
200
|
+
}
|
|
@@ -29,6 +29,11 @@ export interface UtilizationMetrics {
|
|
|
29
29
|
[key: string]: string;
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
+
export interface UtilizationLabels {
|
|
33
|
+
utilizationLabelNames?: string[];
|
|
34
|
+
utilizationFetchLabelValues?: (key: string) => Promise<string[]>;
|
|
35
|
+
utilizationLabelValues?: string[];
|
|
36
|
+
}
|
|
32
37
|
interface ProfileSelectorProps extends ProfileSelectorFeatures {
|
|
33
38
|
queryClient: QueryServiceClient;
|
|
34
39
|
querySelection: QuerySelection;
|
|
@@ -43,6 +48,7 @@ interface ProfileSelectorProps extends ProfileSelectorFeatures {
|
|
|
43
48
|
suffix?: string;
|
|
44
49
|
utilizationMetrics?: UtilizationMetrics[];
|
|
45
50
|
utilizationMetricsLoading?: boolean;
|
|
51
|
+
utilizationLabels?: UtilizationLabels;
|
|
46
52
|
}
|
|
47
53
|
export interface IProfileTypesResult {
|
|
48
54
|
loading: boolean;
|
|
@@ -50,6 +56,6 @@ export interface IProfileTypesResult {
|
|
|
50
56
|
error?: RpcError;
|
|
51
57
|
}
|
|
52
58
|
export declare const useProfileTypes: (client: QueryServiceClient) => IProfileTypesResult;
|
|
53
|
-
declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, showMetricsGraph, showSumBySelector, showProfileTypeSelector, disableExplorativeQuerying, setDisplayHideMetricsGraphButton, utilizationMetrics, utilizationMetricsLoading, }: ProfileSelectorProps) => JSX.Element;
|
|
59
|
+
declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, showMetricsGraph, showSumBySelector, showProfileTypeSelector, disableExplorativeQuerying, setDisplayHideMetricsGraphButton, utilizationMetrics, utilizationMetricsLoading, utilizationLabels, }: ProfileSelectorProps) => JSX.Element;
|
|
54
60
|
export default ProfileSelector;
|
|
55
61
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,QAAQ,EAAE,cAAc,EAAuC,MAAM,OAAO,CAAC;AAErF,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAC,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAUvE,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAC,gBAAgB,EAAC,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,QAAQ,EAAE,cAAc,EAAuC,MAAM,OAAO,CAAC;AAErF,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAC,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAUvE,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAC,gBAAgB,EAAC,MAAM,IAAI,CAAC;AASpC,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,uBAAuB;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QACR,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;IACF,UAAU,EAAE;QACV,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,2BAA2B,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjE,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;CACnC;AAED,UAAU,oBAAqB,SAAQ,uBAAuB;IAC5D,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,gCAAgC,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC1C,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;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,2UAkBlB,oBAAoB,KAAG,GAAG,CAAC,OA4M7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -17,6 +17,7 @@ import { CloseIcon } from '@parca/icons';
|
|
|
17
17
|
import { Query } from '@parca/parser';
|
|
18
18
|
import { useLabelNames } from '../MatchersInput/index';
|
|
19
19
|
import { useMetricsGraphDimensions } from '../MetricsGraph/useMetricsGraphDimensions';
|
|
20
|
+
import { UtilizationLabelsProvider } from '../contexts/UtilizationLabelsContext';
|
|
20
21
|
import { useDefaultSumBy, useSumBySelection } from '../useSumBy';
|
|
21
22
|
import { MetricsGraphSection } from './MetricsGraphSection';
|
|
22
23
|
import { QueryControls } from './QueryControls';
|
|
@@ -38,7 +39,7 @@ export const useProfileTypes = (client) => {
|
|
|
38
39
|
}, [client, metadata, loading]);
|
|
39
40
|
return { loading, data: result, error };
|
|
40
41
|
};
|
|
41
|
-
const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, showMetricsGraph = true, showSumBySelector = true, showProfileTypeSelector = true, disableExplorativeQuerying = false, setDisplayHideMetricsGraphButton, utilizationMetrics, utilizationMetricsLoading, }) => {
|
|
42
|
+
const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, showMetricsGraph = true, showSumBySelector = true, showProfileTypeSelector = true, disableExplorativeQuerying = false, setDisplayHideMetricsGraphButton, utilizationMetrics, utilizationMetricsLoading, utilizationLabels, }) => {
|
|
42
43
|
const { loading: profileTypesLoading, data: profileTypesData, error, } = useProfileTypes(queryClient);
|
|
43
44
|
const { heightStyle } = useMetricsGraphDimensions(comparing);
|
|
44
45
|
const { viewComponent } = useParcaContext();
|
|
@@ -139,6 +140,6 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
|
|
|
139
140
|
queryExpressionString === '{}';
|
|
140
141
|
const queryBrowserRef = useRef(null);
|
|
141
142
|
const sumByRef = useRef(null);
|
|
142
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex", children: [_jsx(QueryControls, { showProfileTypeSelector: showProfileTypeSelector, showSumBySelector: showSumBySelector, disableExplorativeQuerying: disableExplorativeQuerying, profileTypesData: profileTypesData, profileTypesLoading: profileTypesLoading, selectedProfileName: selectedProfileName, setProfileName: setProfileName, setMatchersString: setMatchersString, setQueryExpression: setQueryExpression, query: query, queryBrowserRef: queryBrowserRef, timeRangeSelection: timeRangeSelection, setTimeRangeSelection: setTimeRangeSelection, searchDisabled: searchDisabled, queryBrowserMode: queryBrowserMode, setQueryBrowserMode: setQueryBrowserMode, advancedModeForQueryBrowser: advancedModeForQueryBrowser, setAdvancedModeForQueryBrowser: setAdvancedModeForQueryBrowser, queryClient: queryClient, sumByRef: sumByRef, labels: labels, sumBySelection: sumBySelection ?? [], setUserSumBySelection: setUserSumBySelection, profileType: profileType, profileTypesError: error }), comparing && (_jsx("div", { children: _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) }))] }), _jsx(MetricsGraphSection, { showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setDisplayHideMetricsGraphButton, heightStyle: heightStyle, querySelection: querySelection, profileSelection: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? defaultSumBy ?? [], defaultSumByLoading: defaultSumByLoading, queryClient: queryClient, queryExpressionString: queryExpressionString, setTimeRangeSelection: setTimeRangeSelection, selectQuery: selectQuery, selectProfile: selectProfile, query: query, setQueryExpression: setQueryExpression, setNewQueryExpression: setNewQueryExpression, utilizationMetrics: utilizationMetrics, utilizationMetricsLoading: utilizationMetricsLoading })] }));
|
|
143
|
+
return (_jsx(UtilizationLabelsProvider, { value: utilizationLabels, children: _jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex", children: [_jsx(QueryControls, { showProfileTypeSelector: showProfileTypeSelector, showSumBySelector: showSumBySelector, disableExplorativeQuerying: disableExplorativeQuerying, profileTypesData: profileTypesData, profileTypesLoading: profileTypesLoading, selectedProfileName: selectedProfileName, setProfileName: setProfileName, setMatchersString: setMatchersString, setQueryExpression: setQueryExpression, query: query, queryBrowserRef: queryBrowserRef, timeRangeSelection: timeRangeSelection, setTimeRangeSelection: setTimeRangeSelection, searchDisabled: searchDisabled, queryBrowserMode: queryBrowserMode, setQueryBrowserMode: setQueryBrowserMode, advancedModeForQueryBrowser: advancedModeForQueryBrowser, setAdvancedModeForQueryBrowser: setAdvancedModeForQueryBrowser, queryClient: queryClient, sumByRef: sumByRef, labels: labels, sumBySelection: sumBySelection ?? [], setUserSumBySelection: setUserSumBySelection, profileType: profileType, profileTypesError: error }), comparing && (_jsx("div", { children: _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) }))] }), _jsx(MetricsGraphSection, { showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setDisplayHideMetricsGraphButton, heightStyle: heightStyle, querySelection: querySelection, profileSelection: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? defaultSumBy ?? [], defaultSumByLoading: defaultSumByLoading, queryClient: queryClient, queryExpressionString: queryExpressionString, setTimeRangeSelection: setTimeRangeSelection, selectQuery: selectQuery, selectProfile: selectProfile, query: query, setQueryExpression: setQueryExpression, setNewQueryExpression: setNewQueryExpression, utilizationMetrics: utilizationMetrics, utilizationMetricsLoading: utilizationMetricsLoading })] }) }));
|
|
143
144
|
};
|
|
144
145
|
export default ProfileSelector;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { QueryServiceClient } from '@parca/client';
|
|
2
2
|
import { Query } from '@parca/parser';
|
|
3
|
+
import { type SelectItem } from './Select';
|
|
3
4
|
interface Props {
|
|
4
5
|
queryClient: QueryServiceClient;
|
|
5
6
|
setMatchersString: (arg: string) => void;
|
|
@@ -8,6 +9,7 @@ interface Props {
|
|
|
8
9
|
profileType: string;
|
|
9
10
|
queryBrowserRef: React.RefObject<HTMLDivElement>;
|
|
10
11
|
}
|
|
11
|
-
declare const
|
|
12
|
-
export default
|
|
12
|
+
export declare const transformLabelsForSelect: (labelNames: string[], shouldTrimPrefix?: boolean) => SelectItem[];
|
|
13
|
+
export default function SimpleMathersWithProvider(props: Props): JSX.Element;
|
|
14
|
+
export {};
|
|
13
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SimpleMatchers/index.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SimpleMatchers/index.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAKpC,OAAe,EAAC,KAAK,UAAU,EAAC,MAAM,UAAU,CAAC;AAEjD,UAAU,KAAK;IACb,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;CAClD;AAUD,eAAO,MAAM,wBAAwB,eACvB,MAAM,EAAE,iCAEnB,UAAU,EAQZ,CAAC;AA+UF,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAAC,KAAK,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CAkB3E"}
|
|
@@ -17,12 +17,16 @@ import { useQueryClient } from '@tanstack/react-query';
|
|
|
17
17
|
import cx from 'classnames';
|
|
18
18
|
import { useGrpcMetadata } from '@parca/components';
|
|
19
19
|
import { sanitizeLabelValue } from '@parca/utilities';
|
|
20
|
-
import {
|
|
20
|
+
import { LabelProvider, useLabels } from '../contexts/SimpleMatchersLabelContext';
|
|
21
|
+
import { useUtilizationLabels } from '../contexts/UtilizationLabelsContext';
|
|
21
22
|
import Select from './Select';
|
|
22
|
-
const transformLabelsForSelect = (labelNames) => {
|
|
23
|
+
export const transformLabelsForSelect = (labelNames, shouldTrimPrefix = false) => {
|
|
23
24
|
return labelNames.map(labelName => ({
|
|
24
25
|
key: labelName,
|
|
25
|
-
element: {
|
|
26
|
+
element: {
|
|
27
|
+
active: _jsx(_Fragment, { children: shouldTrimPrefix ? labelName.split('.').pop() : labelName }),
|
|
28
|
+
expanded: _jsx(_Fragment, { children: shouldTrimPrefix ? labelName.split('.').pop() : labelName }),
|
|
29
|
+
},
|
|
26
30
|
}));
|
|
27
31
|
};
|
|
28
32
|
const operatorOptions = [
|
|
@@ -55,27 +59,18 @@ const operatorOptions = [
|
|
|
55
59
|
},
|
|
56
60
|
},
|
|
57
61
|
];
|
|
58
|
-
const SimpleMatchers = ({ queryClient, setMatchersString,
|
|
59
|
-
|
|
60
|
-
currentQuery, profileType, queryBrowserRef, }) => {
|
|
62
|
+
const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileType, queryBrowserRef, }) => {
|
|
63
|
+
const utilizationLabels = useUtilizationLabels();
|
|
61
64
|
const [queryRows, setQueryRows] = useState([
|
|
62
65
|
{ labelName: '', operator: '=', labelValue: '', labelValues: [], isLoading: false },
|
|
63
66
|
]);
|
|
64
67
|
const reactQueryClient = useQueryClient();
|
|
65
68
|
const metadata = useGrpcMetadata();
|
|
66
|
-
const { loading: labelNamesLoading, result } = useLabelNames(queryClient, profileType);
|
|
67
|
-
const { response: labelNamesResponse, error: labelNamesError } = result;
|
|
68
69
|
const [showAll, setShowAll] = useState(false);
|
|
69
70
|
const visibleRows = showAll ? queryRows : queryRows.slice(0, 3);
|
|
70
71
|
const hiddenRowsCount = queryRows.length - 3;
|
|
71
72
|
const maxWidthInPixels = `max-w-[${queryBrowserRef.current?.offsetWidth.toString()}px]`;
|
|
72
73
|
const currentMatchers = currentQuery.matchersString();
|
|
73
|
-
const labelNameFromMatchers = useMemo(() => {
|
|
74
|
-
if (currentQuery === undefined)
|
|
75
|
-
return [];
|
|
76
|
-
const matchers = currentQuery.matchers;
|
|
77
|
-
return matchers.map(matcher => matcher.key);
|
|
78
|
-
}, [currentQuery]);
|
|
79
74
|
const fetchLabelValues = useCallback(async (labelName) => {
|
|
80
75
|
if (labelName == null || labelName === '' || profileType == null || profileType === '') {
|
|
81
76
|
return [];
|
|
@@ -95,6 +90,9 @@ currentQuery, profileType, queryBrowserRef, }) => {
|
|
|
95
90
|
return [];
|
|
96
91
|
}
|
|
97
92
|
}, [queryClient, metadata, profileType, reactQueryClient]);
|
|
93
|
+
const fetchLabelValuesUtilization = useCallback(async (labelName) => {
|
|
94
|
+
return (await utilizationLabels?.utilizationFetchLabelValues?.(labelName)) ?? [];
|
|
95
|
+
}, [utilizationLabels]);
|
|
98
96
|
const updateMatchersString = useCallback((rows) => {
|
|
99
97
|
const matcherString = rows
|
|
100
98
|
.filter(row => row.labelName.length > 0 && row.labelValue)
|
|
@@ -115,7 +113,9 @@ currentQuery, profileType, queryBrowserRef, }) => {
|
|
|
115
113
|
const trimmedLabelName = labelName.trim();
|
|
116
114
|
if (trimmedLabelName === '')
|
|
117
115
|
return null;
|
|
118
|
-
const labelValues =
|
|
116
|
+
const labelValues = utilizationLabels?.utilizationFetchLabelValues !== undefined
|
|
117
|
+
? await fetchLabelValuesUtilization(trimmedLabelName)
|
|
118
|
+
: await fetchLabelValues(trimmedLabelName);
|
|
119
119
|
const sanitizedLabelValue = labelValue.startsWith('"') && labelValue.endsWith('"')
|
|
120
120
|
? labelValue.slice(1, -1)
|
|
121
121
|
: labelValue;
|
|
@@ -131,18 +131,14 @@ currentQuery, profileType, queryBrowserRef, }) => {
|
|
|
131
131
|
updateMatchersString(filteredRows);
|
|
132
132
|
};
|
|
133
133
|
void fetchAndSetQueryRows();
|
|
134
|
-
}, [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const labelNameOptions = useMemo(() => {
|
|
143
|
-
const uniqueLabelNames = Array.from(new Set([...labelNames, ...labelNameFromMatchers]));
|
|
144
|
-
return transformLabelsForSelect(uniqueLabelNames);
|
|
145
|
-
}, [labelNames, labelNameFromMatchers]);
|
|
134
|
+
}, [
|
|
135
|
+
currentMatchers,
|
|
136
|
+
fetchLabelValues,
|
|
137
|
+
updateMatchersString,
|
|
138
|
+
fetchLabelValuesUtilization,
|
|
139
|
+
utilizationLabels,
|
|
140
|
+
]);
|
|
141
|
+
const { labelNameOptions, isLoading: labelNamesLoading } = useLabels();
|
|
146
142
|
const updateRow = useCallback(async (index, field, value) => {
|
|
147
143
|
const updatedRows = [...queryRows];
|
|
148
144
|
const prevLabelName = updatedRows[index].labelName;
|
|
@@ -152,13 +148,21 @@ currentQuery, profileType, queryBrowserRef, }) => {
|
|
|
152
148
|
updatedRows[index].labelValue = '';
|
|
153
149
|
updatedRows[index].isLoading = true;
|
|
154
150
|
setQueryRows([...updatedRows]);
|
|
155
|
-
const labelValues =
|
|
151
|
+
const labelValues = utilizationLabels?.utilizationFetchLabelValues !== undefined
|
|
152
|
+
? await fetchLabelValuesUtilization(value)
|
|
153
|
+
: await fetchLabelValues(value);
|
|
156
154
|
updatedRows[index].labelValues = labelValues;
|
|
157
155
|
updatedRows[index].isLoading = false;
|
|
158
156
|
}
|
|
159
157
|
setQueryRows([...updatedRows]);
|
|
160
158
|
updateMatchersString(updatedRows);
|
|
161
|
-
}, [
|
|
159
|
+
}, [
|
|
160
|
+
queryRows,
|
|
161
|
+
fetchLabelValues,
|
|
162
|
+
updateMatchersString,
|
|
163
|
+
fetchLabelValuesUtilization,
|
|
164
|
+
utilizationLabels,
|
|
165
|
+
]);
|
|
162
166
|
const handleUpdateRow = useCallback((index, field, value) => {
|
|
163
167
|
void updateRow(index, field, value);
|
|
164
168
|
}, [updateRow]);
|
|
@@ -196,7 +200,9 @@ currentQuery, profileType, queryBrowserRef, }) => {
|
|
|
196
200
|
updatedRows[index].isLoading = true;
|
|
197
201
|
setQueryRows([...updatedRows]);
|
|
198
202
|
try {
|
|
199
|
-
const labelValues =
|
|
203
|
+
const labelValues = utilizationLabels?.utilizationFetchLabelValues !== undefined
|
|
204
|
+
? await fetchLabelValuesUtilization(updatedRows[index].labelName)
|
|
205
|
+
: await fetchLabelValues(updatedRows[index].labelName);
|
|
200
206
|
updatedRows[index].labelValues = labelValues;
|
|
201
207
|
}
|
|
202
208
|
catch (error) {
|
|
@@ -211,10 +217,18 @@ currentQuery, profileType, queryBrowserRef, }) => {
|
|
|
211
217
|
console.log(`Label values already present or empty label name`);
|
|
212
218
|
}
|
|
213
219
|
};
|
|
214
|
-
}, [queryRows, fetchLabelValues]);
|
|
220
|
+
}, [queryRows, fetchLabelValues, fetchLabelValuesUtilization, utilizationLabels]);
|
|
215
221
|
const isRowRegex = (row) => row.operator === '=~' || row.operator === '!~';
|
|
216
222
|
return (_jsxs("div", { className: `flex items-center gap-3 ${maxWidthInPixels} w-full flex-wrap`, id: "simple-matchers", children: [visibleRows.map((row, index) => (_jsxs("div", { className: "flex items-center", children: [_jsx(Select, { items: labelNameOptions, onSelection: value => handleUpdateRow(index, 'labelName', value), placeholder: "Select label name", selectedKey: row.labelName, className: "rounded-tr-none rounded-br-none ring-0 focus:ring-0 outline-none", loading: labelNamesLoading, searchable: true }), _jsx(Select, { items: operatorOptions, onSelection: value => handleUpdateRow(index, 'operator', value), selectedKey: row.operator, className: "rounded-none ring-0 focus:ring-0 outline-none" }), _jsx(Select, { items: transformLabelsForSelect(row.labelValues), onSelection: value => handleUpdateRow(index, 'labelValue', value), placeholder: "Select label value", selectedKey: row.labelValue, className: "rounded-none ring-0 focus:ring-0 outline-none max-w-48", optionsClassname: cx('max-w-[300px]', {
|
|
217
223
|
'w-[300px]': isRowRegex(row),
|
|
218
224
|
}), searchable: true, disabled: row.labelName === '', loading: row.isLoading, onButtonClick: () => handleLabelValueClick(index), editable: isRowRegex(row) }), _jsx("button", { onClick: () => removeRow(index), className: cx('p-2 border-gray-200 border rounded rounded-tl-none rounded-bl-none focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900'), children: _jsx(Icon, { icon: "carbon:close", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) })] }, index))), queryRows.length > 3 && (_jsx("button", { onClick: () => setShowAll(!showAll), className: "mr-2 px-3 py-1 text-sm font-medium text-gray-700 dark:text-gray-200 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-gray-900", children: showAll ? 'Show less' : `Show ${hiddenRowsCount} more` })), _jsx("button", { onClick: addNewRow, className: "p-2 border-gray-200 dark:bg-gray-900 dark:border-gray-600 border rounded focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", children: _jsx(Icon, { icon: "material-symbols:add", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) })] }));
|
|
219
225
|
};
|
|
220
|
-
export default
|
|
226
|
+
export default function SimpleMathersWithProvider(props) {
|
|
227
|
+
const labelNameFromMatchers = useMemo(() => {
|
|
228
|
+
if (props.currentQuery === undefined)
|
|
229
|
+
return [];
|
|
230
|
+
const matchers = props.currentQuery.matchers;
|
|
231
|
+
return matchers.map(matcher => matcher.key);
|
|
232
|
+
}, [props.currentQuery]);
|
|
233
|
+
return (_jsx(LabelProvider, { queryClient: props.queryClient, profileType: props.profileType, labelNameFromMatchers: labelNameFromMatchers, children: _jsx(SimpleMatchers, { ...props }) }));
|
|
234
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { QueryServiceClient } from '@parca/client';
|
|
3
|
+
interface LabelNameMapping {
|
|
4
|
+
displayName: string;
|
|
5
|
+
fullName: string;
|
|
6
|
+
}
|
|
7
|
+
interface LabelsContextType {
|
|
8
|
+
labelNames: string[];
|
|
9
|
+
labelValues: string[];
|
|
10
|
+
labelNameMappings: LabelNameMapping[];
|
|
11
|
+
isLabelNamesLoading: boolean;
|
|
12
|
+
isLabelValuesLoading: boolean;
|
|
13
|
+
currentLabelName: string | null;
|
|
14
|
+
setCurrentLabelName: (name: string | null) => void;
|
|
15
|
+
shouldHandlePrefixes: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface LabelsProviderProps {
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
queryClient: QueryServiceClient;
|
|
20
|
+
profileType: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function LabelsProvider({ children, queryClient, profileType, }: LabelsProviderProps): JSX.Element;
|
|
23
|
+
export declare function useLabels(): LabelsContextType;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=MatchersInputLabelsContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MatchersInputLabelsContext.d.ts","sourceRoot":"","sources":["../../src/contexts/MatchersInputLabelsContext.tsx"],"names":[],"mappings":"AAaA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAEhE,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAKjD,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,iBAAiB;IACzB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IACtC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACnD,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAID,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,WAAW,EAAE,kBAAkB,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,WAAW,EACX,WAAW,GACZ,EAAE,mBAAmB,GAAG,GAAG,CAAC,OAAO,CAuEnC;AAED,wBAAgB,SAAS,IAAI,iBAAiB,CAM7C"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// Copyright 2022 The Parca Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
import React, { createContext, useContext, useMemo } from 'react';
|
|
15
|
+
import { useFetchUtilizationLabelValues, useLabelNames, useLabelValues } from '../MatchersInput';
|
|
16
|
+
import { useUtilizationLabels } from './UtilizationLabelsContext';
|
|
17
|
+
const LabelsContext = createContext(null);
|
|
18
|
+
// With there being the possibility of having utilization labels, we need to be able to determine whether the labels to be used are utilization labels or profiling data labels.
|
|
19
|
+
// This context is used to determine this.
|
|
20
|
+
export function LabelsProvider({ children, queryClient, profileType, }) {
|
|
21
|
+
const [currentLabelName, setCurrentLabelName] = React.useState(null);
|
|
22
|
+
const utilizationLabels = useUtilizationLabels();
|
|
23
|
+
const { result: labelNamesResponse, loading: isLabelNamesLoading } = useLabelNames(queryClient, profileType);
|
|
24
|
+
const labelNamesFromAPI = useMemo(() => {
|
|
25
|
+
return (labelNamesResponse.error === undefined || labelNamesResponse.error == null) &&
|
|
26
|
+
labelNamesResponse !== undefined &&
|
|
27
|
+
labelNamesResponse != null
|
|
28
|
+
? labelNamesResponse.response?.labelNames.filter(e => e !== '__name__') ?? []
|
|
29
|
+
: [];
|
|
30
|
+
}, [labelNamesResponse]);
|
|
31
|
+
const { result: labelValuesOriginal, loading: isLabelValuesLoading } = useLabelValues(queryClient, currentLabelName ?? '', profileType);
|
|
32
|
+
const utilizationLabelValues = useFetchUtilizationLabelValues(currentLabelName ?? '', utilizationLabels);
|
|
33
|
+
const shouldHandlePrefixes = utilizationLabels?.utilizationLabelNames !== undefined;
|
|
34
|
+
const labelNameMappings = useMemo(() => {
|
|
35
|
+
const names = utilizationLabels?.utilizationLabelNames ?? labelNamesFromAPI;
|
|
36
|
+
return names.map(name => ({
|
|
37
|
+
displayName: name.replace(/^(attributes\.|attributes_resource\.)/, ''),
|
|
38
|
+
fullName: name,
|
|
39
|
+
}));
|
|
40
|
+
}, [labelNamesFromAPI, utilizationLabels?.utilizationLabelNames]);
|
|
41
|
+
const labelNames = useMemo(() => {
|
|
42
|
+
return shouldHandlePrefixes ? labelNameMappings.map(m => m.displayName) : labelNamesFromAPI;
|
|
43
|
+
}, [labelNameMappings, labelNamesFromAPI, shouldHandlePrefixes]);
|
|
44
|
+
const labelValues = useMemo(() => {
|
|
45
|
+
return utilizationLabels?.utilizationFetchLabelValues !== undefined
|
|
46
|
+
? utilizationLabelValues
|
|
47
|
+
: labelValuesOriginal.response;
|
|
48
|
+
}, [labelValuesOriginal, utilizationLabelValues, utilizationLabels]);
|
|
49
|
+
const value = useMemo(() => ({
|
|
50
|
+
labelNames,
|
|
51
|
+
labelValues,
|
|
52
|
+
labelNameMappings,
|
|
53
|
+
isLabelNamesLoading,
|
|
54
|
+
isLabelValuesLoading,
|
|
55
|
+
currentLabelName,
|
|
56
|
+
setCurrentLabelName,
|
|
57
|
+
shouldHandlePrefixes,
|
|
58
|
+
}), [
|
|
59
|
+
labelNames,
|
|
60
|
+
labelValues,
|
|
61
|
+
labelNameMappings,
|
|
62
|
+
isLabelNamesLoading,
|
|
63
|
+
isLabelValuesLoading,
|
|
64
|
+
currentLabelName,
|
|
65
|
+
shouldHandlePrefixes,
|
|
66
|
+
]);
|
|
67
|
+
return _jsx(LabelsContext.Provider, { value: value, children: children });
|
|
68
|
+
}
|
|
69
|
+
export function useLabels() {
|
|
70
|
+
const context = useContext(LabelsContext);
|
|
71
|
+
if (context === null) {
|
|
72
|
+
throw new Error('useLabels must be used within a LabelsProvider');
|
|
73
|
+
}
|
|
74
|
+
return context;
|
|
75
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { QueryServiceClient } from '@parca/client';
|
|
2
|
+
import type { SelectItem } from '../SimpleMatchers/Select';
|
|
3
|
+
interface LabelContextValue {
|
|
4
|
+
labelNameOptions: SelectItem[];
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
error: Error | null;
|
|
7
|
+
}
|
|
8
|
+
interface LabelProviderProps {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
queryClient: QueryServiceClient;
|
|
11
|
+
profileType: string;
|
|
12
|
+
labelNameFromMatchers: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare function LabelProvider({ children, queryClient, profileType, labelNameFromMatchers, }: LabelProviderProps): JSX.Element;
|
|
15
|
+
export declare function useLabels(): LabelContextValue;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=SimpleMatchersLabelContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SimpleMatchersLabelContext.d.ts","sourceRoot":"","sources":["../../src/contexts/SimpleMatchersLabelContext.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAIjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAC;AAGzD,UAAU,iBAAiB;IACzB,gBAAgB,EAAE,UAAU,EAAE,CAAC;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAID,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,WAAW,EAAE,kBAAkB,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAKD,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,WAAW,EACX,WAAW,EACX,qBAAqB,GACtB,EAAE,kBAAkB,GAAG,GAAG,CAAC,OAAO,CAwBlC;AAED,wBAAgB,SAAS,IAAI,iBAAiB,CAM7C"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// Copyright 2022 The Parca Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
15
|
+
import { useLabelNames } from '../MatchersInput';
|
|
16
|
+
import { transformLabelsForSelect } from '../SimpleMatchers';
|
|
17
|
+
import { useUtilizationLabels } from './UtilizationLabelsContext';
|
|
18
|
+
const LabelContext = createContext(null);
|
|
19
|
+
// With there being the possibility of having utilization labels, we need to be able to determine whether the labels to be used are utilization labels or profiling data labels.
|
|
20
|
+
// This context is used to determine this.
|
|
21
|
+
export function LabelProvider({ children, queryClient, profileType, labelNameFromMatchers, }) {
|
|
22
|
+
const utilizationLabels = useUtilizationLabels();
|
|
23
|
+
const { loading, result } = useLabelNames(queryClient, profileType);
|
|
24
|
+
const value = useMemo(() => {
|
|
25
|
+
const baseLabels = result.error != null ? [] : result.response?.labelNames.filter(e => e !== '__name__') ?? [];
|
|
26
|
+
const labelsToUse = utilizationLabels?.utilizationLabelNames?.length !== undefined
|
|
27
|
+
? utilizationLabels?.utilizationLabelNames ?? []
|
|
28
|
+
: baseLabels;
|
|
29
|
+
const uniqueLabels = Array.from(new Set([...labelsToUse, ...labelNameFromMatchers]));
|
|
30
|
+
const shouldTrimPrefix = Boolean(utilizationLabels?.utilizationLabelNames);
|
|
31
|
+
return {
|
|
32
|
+
labelNameOptions: transformLabelsForSelect(uniqueLabels, shouldTrimPrefix),
|
|
33
|
+
isLoading: loading,
|
|
34
|
+
error: result.error ?? null,
|
|
35
|
+
};
|
|
36
|
+
}, [result, loading, utilizationLabels, labelNameFromMatchers]);
|
|
37
|
+
return _jsx(LabelContext.Provider, { value: value, children: children });
|
|
38
|
+
}
|
|
39
|
+
export function useLabels() {
|
|
40
|
+
const context = useContext(LabelContext);
|
|
41
|
+
if (context === null) {
|
|
42
|
+
throw new Error('useLabels must be used within a LabelProvider');
|
|
43
|
+
}
|
|
44
|
+
return context;
|
|
45
|
+
}
|