1111// See the License for the specific language governing permissions and
1212// limitations under the License.
1313
14- import React , { useMemo , useRef , useState } from 'react' ;
14+ import React , { useEffect , useMemo , useRef , useState } from 'react' ;
1515
16+ import { useQuery } from '@tanstack/react-query' ;
1617import cx from 'classnames' ;
1718import TextareaAutosize from 'react-textarea-autosize' ;
1819
20+ import { LabelsRequest , LabelsResponse , QueryServiceClient , ValuesRequest } from '@parca/client' ;
21+ import { useGrpcMetadata } from '@parca/components' ;
1922import { Query } from '@parca/parser' ;
2023import { TEST_IDS , testId } from '@parca/test-utils' ;
24+ import { millisToProtoTimestamp , sanitizeLabelValue } from '@parca/utilities' ;
2125
22- import { useUnifiedLabels } from '../contexts/UnifiedLabelsContext' ;
23- import { useQueryState } from '../hooks/useQueryState' ;
26+ import { UtilizationLabels } from '../ProfileSelector' ;
27+ import { LabelsProvider , useLabels } from '../contexts/MatchersInputLabelsContext' ;
28+ import useGrpcQuery from '../useGrpcQuery' ;
2429import SuggestionsList , { Suggestion , Suggestions } from './SuggestionsList' ;
2530
26- const MatchersInput = ( ) : JSX . Element => {
31+ interface MatchersInputProps {
32+ queryClient : QueryServiceClient ;
33+ setMatchersString : ( arg : string ) => void ;
34+ runQuery : ( ) => void ;
35+ currentQuery : Query ;
36+ profileType : string ;
37+ start ?: number ;
38+ end ?: number ;
39+ }
40+
41+ export interface ILabelNamesResult {
42+ response ?: LabelsResponse ;
43+ error ?: Error ;
44+ }
45+
46+ interface UseLabelNames {
47+ result : ILabelNamesResult ;
48+ loading : boolean ;
49+ refetch : ( ) => Promise < void > ;
50+ }
51+
52+ export const useLabelNames = (
53+ client : QueryServiceClient ,
54+ profileType : string ,
55+ start ?: number ,
56+ end ?: number ,
57+ match ?: string [ ]
58+ ) : UseLabelNames => {
59+ const metadata = useGrpcMetadata ( ) ;
60+
61+ const { data, isLoading, error, refetch} = useGrpcQuery < LabelsResponse > ( {
62+ key : [ 'labelNames' , profileType , match ?. join ( ',' ) , start , end ] ,
63+ queryFn : async signal => {
64+ const request : LabelsRequest = { match : match !== undefined ? match : [ ] } ;
65+ if ( start !== undefined && end !== undefined ) {
66+ request . start = millisToProtoTimestamp ( start ) ;
67+ request . end = millisToProtoTimestamp ( end ) ;
68+ }
69+ if ( profileType !== undefined ) {
70+ request . profileType = profileType ;
71+ }
72+ const { response} = await client . labels ( request , { meta : metadata , abort : signal } ) ;
73+ return response ;
74+ } ,
75+ options : {
76+ enabled : profileType !== undefined && profileType !== '' ,
77+ keepPreviousData : false ,
78+ } ,
79+ } ) ;
80+
81+ useEffect ( ( ) => {
82+ console . log ( 'Label names query result:' , { data, error, isLoading} ) ;
83+ } , [ data , error , isLoading ] ) ;
84+
85+ return {
86+ result : { response : data , error : error as Error } ,
87+ loading : isLoading ,
88+ refetch : async ( ) => {
89+ await refetch ( ) ;
90+ } ,
91+ } ;
92+ } ;
93+
94+ interface UseLabelValues {
95+ result : {
96+ response : string [ ] ;
97+ error ?: Error ;
98+ } ;
99+ loading : boolean ;
100+ refetch : ( ) => Promise < void > ;
101+ }
102+
103+ export const useLabelValues = (
104+ client : QueryServiceClient ,
105+ labelName : string ,
106+ profileType : string ,
107+ start ?: number ,
108+ end ?: number
109+ ) : UseLabelValues => {
110+ const metadata = useGrpcMetadata ( ) ;
111+
112+ const { data, isLoading, error, refetch} = useGrpcQuery < string [ ] > ( {
113+ key : [ 'labelValues' , labelName , profileType , start , end ] ,
114+ queryFn : async signal => {
115+ const request : ValuesRequest = { labelName, match : [ ] , profileType} ;
116+ if ( start !== undefined && end !== undefined ) {
117+ request . start = millisToProtoTimestamp ( start ) ;
118+ request . end = millisToProtoTimestamp ( end ) ;
119+ }
120+ const { response} = await client . values ( request , { meta : metadata , abort : signal } ) ;
121+ return sanitizeLabelValue ( response . labelValues ) ;
122+ } ,
123+ options : {
124+ enabled :
125+ profileType !== undefined &&
126+ profileType !== '' &&
127+ labelName !== undefined &&
128+ labelName !== '' ,
129+ keepPreviousData : false ,
130+ } ,
131+ } ) ;
132+
133+ console . log ( 'Label values query result:' , { data, error, isLoading, labelName} ) ;
134+
135+ return {
136+ result : { response : data ?? [ ] , error : error as Error } ,
137+ loading : isLoading ,
138+ refetch : async ( ) => {
139+ await refetch ( ) ;
140+ } ,
141+ } ;
142+ } ;
143+
144+ export const useFetchUtilizationLabelValues = (
145+ labelName : string ,
146+ utilizationLabels ?: UtilizationLabels
147+ ) : string [ ] => {
148+ const { data} = useQuery ( {
149+ queryKey : [ 'utilizationLabelValues' , labelName ] ,
150+ queryFn : async ( ) => {
151+ const result = await utilizationLabels ?. utilizationFetchLabelValues ?.( labelName ) ;
152+ return result ?? [ ] ;
153+ } ,
154+ enabled : utilizationLabels ?. utilizationFetchLabelValues != null && labelName !== '' ,
155+ } ) ;
156+
157+ return data ?? [ ] ;
158+ } ;
159+
160+ const MatchersInput = ( {
161+ setMatchersString,
162+ runQuery,
163+ currentQuery,
164+ } : MatchersInputProps ) : JSX . Element => {
27165 const inputRef = useRef < HTMLTextAreaElement | null > ( null ) ;
28166 const [ focusedInput , setFocusedInput ] = useState ( false ) ;
29167 const [ lastCompleted , setLastCompleted ] = useState < Suggestion > ( new Suggestion ( '' , '' , '' ) ) ;
30168
31169 const {
32170 labelNames,
33171 labelValues,
34- labelNameMappingsForMatchersInput : labelNameMappings ,
172+ labelNameMappings,
35173 isLabelNamesLoading,
36174 isLabelValuesLoading,
37175 currentLabelName,
38176 setCurrentLabelName,
39177 shouldHandlePrefixes,
40178 refetchLabelValues,
41179 refetchLabelNames,
42- suffix,
43- } = useUnifiedLabels ( ) ;
44-
45- const { setDraftMatchers, commitDraft, draftParsedQuery} = useQueryState ( { suffix} ) ;
180+ } = useLabels ( ) ;
46181
47- const value = draftParsedQuery != null ? draftParsedQuery . matchersString ( ) : '' ;
182+ const value = currentQuery . matchersString ( ) ;
48183
49184 const suggestionSections = useMemo ( ( ) => {
50185 const suggestionSections = new Suggestions ( ) ;
51- Query . suggest ( `${ draftParsedQuery ? .profileName ( ) as string } {${ value } ` ) . forEach ( function ( s ) {
186+ Query . suggest ( `${ currentQuery . profileName ( ) } {${ value } ` ) . forEach ( function ( s ) {
52187 // Skip suggestions that we just completed. This really only works,
53188 // because we know the language is not repetitive. For a language that
54189 // has a repeating word, this would not work.
@@ -121,7 +256,7 @@ const MatchersInput = (): JSX.Element => {
121256 } ) ;
122257 return suggestionSections ;
123258 } , [
124- draftParsedQuery ,
259+ currentQuery ,
125260 lastCompleted ,
126261 labelNames ,
127262 labelValues ,
@@ -136,7 +271,7 @@ const MatchersInput = (): JSX.Element => {
136271
137272 const onChange = ( e : React . ChangeEvent < HTMLTextAreaElement > ) : void => {
138273 const newValue = e . target . value ;
139- setDraftMatchers ( newValue ) ;
274+ setMatchersString ( newValue ) ;
140275 resetLastCompleted ( ) ;
141276 } ;
142277
@@ -159,7 +294,7 @@ const MatchersInput = (): JSX.Element => {
159294 const applySuggestion = ( suggestion : Suggestion ) : void => {
160295 const newValue = complete ( suggestion ) ;
161296 setLastCompleted ( suggestion ) ;
162- setDraftMatchers ( newValue ) ;
297+ setMatchersString ( newValue ) ;
163298 if ( inputRef . current !== null ) {
164299 inputRef . current . value = newValue ;
165300 inputRef . current . focus ( ) ;
@@ -174,7 +309,7 @@ const MatchersInput = (): JSX.Element => {
174309 setFocusedInput ( false ) ;
175310 } ;
176311
177- const profileSelected = draftParsedQuery ? .profileName ( ) === '' ;
312+ const profileSelected = currentQuery . profileName ( ) === '' ;
178313
179314 return (
180315 < div
@@ -210,7 +345,7 @@ const MatchersInput = (): JSX.Element => {
210345 suggestions = { suggestionSections }
211346 applySuggestion = { applySuggestion }
212347 inputRef = { inputRef . current }
213- runQuery = { commitDraft }
348+ runQuery = { runQuery }
214349 focusedInput = { focusedInput }
215350 isLabelValuesLoading = {
216351 isLabelValuesLoading && lastCompleted . type === 'literal' && lastCompleted . value !== ','
@@ -223,4 +358,15 @@ const MatchersInput = (): JSX.Element => {
223358 ) ;
224359} ;
225360
226- export default MatchersInput ;
361+ export default function MatchersInputWithProvider ( props : MatchersInputProps ) : JSX . Element {
362+ return (
363+ < LabelsProvider
364+ queryClient = { props . queryClient }
365+ profileType = { props . profileType }
366+ start = { props . start }
367+ end = { props . end }
368+ >
369+ < MatchersInput { ...props } />
370+ </ LabelsProvider >
371+ ) ;
372+ }
0 commit comments