11import  React ,  {  ChangeEvent ,  ReactNode  }  from  'react' ; 
2- import  {  Button ,   Field ,  Form ,  Icon ,  Input ,  Select  }  from  '@grafana/ui' ; 
3- import  {  QueryEditorProps ,  SelectableValue  }  from  '@grafana/data' ; 
2+ import  {  AsyncSelect ,   Button ,  Form ,  Icon ,  InlineField ,   Input ,  VerticalGroup  }  from  '@grafana/ui' ; 
3+ import  {  DataFrame ,   DataQueryRequest ,   Field ,   getDefaultTimeRange ,   QueryEditorProps ,  SelectableValue ,   Vector  }  from  '@grafana/data' ; 
44import  {  DataSource  }  from  '../datasource' ; 
55import  {  DEFAULT_QUERY ,  HaystackDataSourceOptions ,  HaystackQuery  }  from  '../types' ; 
66
77type  Props  =  QueryEditorProps < DataSource ,  HaystackQuery ,  HaystackDataSourceOptions > ; 
88
9- export  function  QueryEditor ( {  query,  onChange,  onRunQuery } : Props )  { 
10-   const  onTypeChange  =  ( event : SelectableValue < number > )  =>  { 
11-     let  queryTypeIndex  =  event . value  ??  queryTypeDefault . value ; 
12-     onChange ( {  ...query ,  type : queryTypes [ queryTypeIndex ] . label  } ) ; 
9+ export  function  QueryEditor ( {  datasource,  query,  onChange,  onRunQuery,  range,  app } : Props )  { 
10+   const  onTypeChange  =  ( event : SelectableValue < string > )  =>  { 
11+     onChange ( {  ...query ,  type : event . value  ??  queryTypeDefault . value !  } ) ; 
1312  } ; 
1413  const  onEvalChange  =  ( event : ChangeEvent < HTMLInputElement > )  =>  { 
15-     onChange ( {  ...query ,  type : 'Eval ' ,  eval : event . target . value  } ) ; 
14+     onChange ( {  ...query ,  type : 'eval ' ,  eval : event . target . value  } ) ; 
1615  } ; 
1716  const  onHisReadChange  =  ( event : ChangeEvent < HTMLInputElement > )  =>  { 
18-     onChange ( {  ...query ,  type : 'HisRead ' ,  hisRead : event . target . value  } ) ; 
17+     onChange ( {  ...query ,  type : 'hisRead ' ,  hisRead : event . target . value  } ) ; 
1918  } ; 
2019  const  onReadChange  =  ( event : ChangeEvent < HTMLInputElement > )  =>  { 
21-     onChange ( {  ...query ,  type : 'Read ' ,  read : event . target . value  } ) ; 
20+     onChange ( {  ...query ,  type : 'read ' ,  read : event . target . value  } ) ; 
2221  } ; 
2322
24-   const  queryTypes  =  [ 
25-     {  label : 'Eval' ,  value : 0 ,  description : 'Evaluate an Axon expression'  } , 
26-     {  label : 'HisRead' ,  value : 1 ,  description : 'Read the history of a point'  } , 
27-     {  label : 'Read' ,  value : 2 ,  description : 'Read the records matched by a filter'  } , 
23+   interface  QueryType  extends  SelectableValue < string >  { 
24+     apiRequirements : string [ ] ; 
25+   }  
26+ 
27+   const  queryTypes : QueryType [ ]  =  [ 
28+     {  label : 'Read' ,  value : "read" ,  apiRequirements : [ "read" ] ,  description : 'Read the records matched by a filter'  } , 
29+     {  label : 'HisRead' ,  value : "hisRead" ,  apiRequirements : [ "hisRead" ] ,  description : 'Read the history of a point'  } , 
30+     {  label : 'Eval' ,  value : "eval" ,  apiRequirements : [ "eval" ] ,  description : 'Evaluate an Axon expression'  } , 
2831  ] ; 
2932  const  queryTypeDefault  =  queryTypes [ 0 ] ; 
3033  function  queryTypeFromLabel ( label : string )  { 
31-     return  queryTypes . find ( ( queryType )  =>  queryType . label  ===  label )   ??   queryTypeDefault ; 
34+     return  queryTypes . find ( ( queryType )  =>  queryType . value  ===  label ) ; 
3235  } 
3336
3437  const  SelectComponent  =  ( )  =>  { 
3538    return  ( 
36-       < Field > 
37-         < Select 
38-           options = { queryTypes } 
39+       < InlineField  label = "Type" > 
40+         < AsyncSelect 
41+           loadOptions = { loadOps } 
42+           defaultOptions 
3943          value = { queryTypeFromLabel ( query . type ) } 
40-           defaultValue = { queryTypeDefault } 
4144          width = { 30 } 
4245          onChange = { ( queryType )  =>  { 
4346            onTypeChange ( queryType ) ; 
4447          } } 
4548        /> 
46-       </ Field > 
49+       </ InlineField > 
4750    ) ; 
4851  } ; 
4952
53+   // Queries the available ops from the datasource on only returns the ones that are supported. 
54+   const  loadOps  =  ( )  =>  { 
55+     let  opsRequest : DataQueryRequest < HaystackQuery >  =  { 
56+       requestId : 'ops' , 
57+       dashboardId : 0 , 
58+       interval : '0' , 
59+       intervalMs : 0 , 
60+       panelId : 0 , 
61+       range : range  ??  getDefaultTimeRange ( ) , 
62+       scopedVars : { } , 
63+       targets : [ {  type : 'ops'  ,  eval : "" ,  read : "" ,  hisRead : "" ,  refId : query . refId } ] , 
64+       timezone : 'UTC' , 
65+       app : 'ops' , 
66+       startTime : 0 , 
67+     } 
68+     return  datasource . query ( opsRequest ) . toPromise ( ) . then ( ( result )  =>  { 
69+       if ( result ?. state  ===  'Error' )  { 
70+         return  [ ] ; 
71+       } 
72+       let  frame  =  result ?. data ?. find ( ( frame : DataFrame )  =>  { 
73+         return  frame . refId  ===  query . refId 
74+       } ) 
75+       let  opSymbols  =  frame ?. fields ?. find ( ( field : Field < any ,  Vector < string > > )  =>  { 
76+         return  field . name  ===  'def' 
77+       } ) . values  ??  [ ] ; 
78+       let  ops : string [ ]  =  opSymbols . map ( ( opSymbol : string )  =>  { 
79+         if  ( opSymbol . startsWith ( '^op:' ) )  { 
80+           return  opSymbol . substring ( 4 ) ; 
81+         }  else  { 
82+           return  opSymbol ; 
83+         } 
84+       } ) ; 
85+ 
86+       return  queryTypes . filter ( ( queryType )  =>  { 
87+         return  queryType . apiRequirements . every ( ( apiRequirement )  =>  { 
88+           return  ops . find ( ( op )  =>  { 
89+             return  op  ===  apiRequirement 
90+           } )  !==  undefined ; 
91+         } ) ; 
92+       } ) ; 
93+     } ) ; 
94+   } 
95+ 
5096  function  renderQuery ( ) : ReactNode  { 
97+     let  width  =  100 ; 
5198    let  queryType  =  queryTypeFromLabel ( query . type ) ; 
52-     switch  ( queryType . value )  { 
53-       case  0 :  // Eval 
99+     switch  ( queryType ? .value )  { 
100+       case  "eval" : 
54101        return  ( 
55-           < Field > 
102+           < InlineField > 
56103            < Input 
57-               width = { 100 } 
104+               width = { width } 
58105              prefix = { < Icon  name = "angle-right"  /> } 
59106              onChange = { onEvalChange } 
60107              value = { query . eval } 
61108              placeholder = { DEFAULT_QUERY . eval } 
62109            /> 
63-           </ Field > 
110+           </ InlineField > 
64111        ) ; 
65-       case  1 :  // HisRead 
112+       case  "hisRead" : 
66113        return  ( 
67-           < Field > 
114+           < InlineField > 
68115            < Input 
69-               width = { 30 } 
116+               width = { width } 
70117              prefix = { '@' } 
71118              onChange = { onHisReadChange } 
72119              value = { query . hisRead } 
73120              placeholder = { DEFAULT_QUERY . hisRead } 
74121            /> 
75-           </ Field > 
122+           </ InlineField > 
76123        ) ; 
77-       case  2 :  // Read 
124+       case  "read" : 
78125        return  ( 
79-           < Field > 
126+           < InlineField > 
80127            < Input 
81-               width = { 75 } 
128+               width = { width } 
82129              prefix = { < Icon  name = "filter"  /> } 
83130              onChange = { onReadChange } 
84131              value = { query . read } 
85132              placeholder = { DEFAULT_QUERY . read } 
86133            /> 
87-           </ Field > 
134+           </ InlineField > 
88135        ) ; 
89136    } 
90137    return  < p > Select a query type</ p > ; 
91138  } 
92139
93140  function  onSubmit ( newQuery : Partial < HaystackQuery > )  { 
94141    query  =  {  ...query ,  ...newQuery  } ; 
142+     console . info ( 'onSubmit' ,  query ) ; 
95143    onRunQuery ( ) ; 
96144  } 
97145
@@ -100,11 +148,11 @@ export function QueryEditor({ query, onChange, onRunQuery }: Props) {
100148      < Form  onSubmit = { onSubmit } > 
101149        { ( {  register,  errors } )  =>  { 
102150          return  ( 
103-             < div > 
151+             < VerticalGroup > 
104152              < SelectComponent  /> 
105153              { renderQuery ( ) } 
106-               < Button  type = "submit" > Run</ Button > 
107-             </ div > 
154+               < Button  type = "submit"   > Run</ Button > 
155+             </ VerticalGroup > 
108156          ) ; 
109157        } } 
110158      </ Form > 
0 commit comments