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