@@ -7,12 +7,17 @@ import {
77 MetricFindValue ,
88 getDefaultTimeRange ,
99 FieldType ,
10+ CustomVariableSupport ,
11+ DataQueryResponse ,
12+ QueryEditorProps ,
1013} from '@grafana/data' ;
1114import { DataSourceWithBackend , getTemplateSrv } from '@grafana/runtime' ;
1215
1316import { HaystackQuery , OpsQuery , HaystackDataSourceOptions , HaystackVariableQuery , QueryType } from './types' ;
14- import { firstValueFrom } from 'rxjs' ;
17+ import { firstValueFrom , map , Observable } from 'rxjs' ;
1518import { isRef , parseRef } from 'haystack' ;
19+ import { ComponentType } from 'react' ;
20+ import { VARIABLE_REF_ID , VariableQueryEditor } from 'components/VariableQueryEditor' ;
1621
1722export const queryTypes : QueryType [ ] = [
1823 { label : 'Eval' , value : 'eval' , apiRequirements : [ 'eval' ] , description : 'Evaluate an Axon expression' } ,
@@ -29,6 +34,9 @@ export const queryTypes: QueryType[] = [
2934export class DataSource extends DataSourceWithBackend < HaystackQuery , HaystackDataSourceOptions > {
3035 constructor ( instanceSettings : DataSourceInstanceSettings < HaystackDataSourceOptions > ) {
3136 super ( instanceSettings ) ;
37+ this . variables = new HaystackVariableSupport ( ( request ) => {
38+ return this . query ( request )
39+ } ) ;
3240 }
3341
3442 // Queries the available ops from the datasource and returns the queryTypes that are supported.
@@ -89,48 +97,6 @@ export class DataSource extends DataSourceWithBackend<HaystackQuery, HaystackDat
8997 } ;
9098 }
9199
92- // This is called when the user is selecting a variable value
93- async metricFindQuery ( variableQuery : HaystackVariableQuery , options ?: any ) {
94- let request : HaystackQuery = variableQuery ;
95- let observable = this . query ( { targets : [ request ] } as DataQueryRequest < HaystackQuery > ) ;
96- let response = await firstValueFrom ( observable ) ;
97-
98- if ( response === undefined || response . data === undefined ) {
99- return [ ] ;
100- }
101-
102- return response . data . reduce ( ( acc : MetricFindValue [ ] , frame : DataFrame ) => {
103- // Default to the first field
104- let column = frame . fields [ 0 ] ;
105- if ( variableQuery . column !== undefined && variableQuery . column !== '' ) {
106- // If a column was input, match the column name
107- column = frame . fields . find ( ( field : Field ) => field . name === variableQuery . column ) ?? column ;
108- } else if ( frame . fields . some ( ( field : Field ) => field . name === 'id' ) ) {
109- // If there is an id column, use that
110- column = frame . fields . find ( ( field : Field ) => field . name === 'id' ) ?? column ;
111- }
112-
113- // Default to the selected column
114- let displayColumn = column ;
115- if ( variableQuery . displayColumn !== undefined && variableQuery . displayColumn !== '' ) {
116- // If a column was input, match the column name
117- displayColumn =
118- frame . fields . find ( ( field : Field ) => field . name === variableQuery . displayColumn ) ?? displayColumn ;
119- }
120-
121- let variableValues = column . values . map ( ( value , index ) => {
122- let variableValue = variableValueFromCell ( value , column . type ) ;
123-
124- let displayValue = displayColumn . values [ index ] ;
125- let variableText = variableTextFromCell ( displayValue , displayColumn . type ) ;
126-
127- return { text : variableText , value : variableValue } ;
128- } ) ;
129-
130- return acc . concat ( variableValues ) ;
131- } , [ ] ) ;
132- }
133-
134100 // Returns a DataQueryRequest that gets the available ops from the datasource
135101 // This applies a bunch of defaults because it's not a time series query
136102 private opsRequest ( refId : string ) : DataQueryRequest < HaystackQuery > {
@@ -150,6 +116,65 @@ export class DataSource extends DataSourceWithBackend<HaystackQuery, HaystackDat
150116 }
151117}
152118
119+ export class HaystackVariableSupport extends CustomVariableSupport < DataSource , HaystackVariableQuery , HaystackQuery , HaystackDataSourceOptions > {
120+ editor : ComponentType < QueryEditorProps < DataSource , HaystackQuery , HaystackDataSourceOptions , HaystackVariableQuery > > ;
121+
122+ // Requests data from the backend. This allows this class to reuse the DataSource.query method to get data.
123+ onQuery : ( request : DataQueryRequest < HaystackVariableQuery > ) => Observable < DataQueryResponse > ;
124+
125+ constructor ( onQuery : ( request : DataQueryRequest < HaystackVariableQuery > ) => Observable < DataQueryResponse > ) {
126+ super ( ) ;
127+ this . editor = VariableQueryEditor ;
128+ this . onQuery = onQuery ;
129+ }
130+
131+ query ( request : DataQueryRequest < HaystackVariableQuery > ) : Observable < DataQueryResponse > {
132+ let variableQuery = request . targets [ 0 ] ;
133+ variableQuery . refId = VARIABLE_REF_ID ;
134+ let observable = this . onQuery ( request ) ;
135+ return observable . pipe (
136+ map ( ( response ) => {
137+ if ( response === undefined || response . errors !== undefined || response . data === undefined ) {
138+ return response
139+ }
140+
141+ let variableValues = response . data . reduce ( ( acc : MetricFindValue [ ] , frame : DataFrame ) => {
142+ // Default to the first field
143+ let column = frame . fields [ 0 ] ;
144+ if ( variableQuery . column !== undefined && variableQuery . column !== '' ) {
145+ // If a column was input, match the column name
146+ column = frame . fields . find ( ( field : Field ) => field . name === variableQuery . column ) ?? column ;
147+ } else if ( frame . fields . some ( ( field : Field ) => field . name === 'id' ) ) {
148+ // If there is an id column, use that
149+ column = frame . fields . find ( ( field : Field ) => field . name === 'id' ) ?? column ;
150+ }
151+
152+ // Default to the selected column
153+ let displayColumn = column ;
154+ if ( variableQuery . displayColumn !== undefined && variableQuery . displayColumn !== '' ) {
155+ // If a column was input, match the column name
156+ displayColumn = frame . fields . find ( ( field : Field ) => {
157+ return field . name === variableQuery . displayColumn
158+ } ) ?? displayColumn ;
159+ }
160+
161+ let variableValues = column . values . map ( ( value , index ) => {
162+ let variableValue = variableValueFromCell ( value , column . type ) ;
163+
164+ let displayValue = displayColumn . values [ index ] ;
165+ let variableText = variableTextFromCell ( displayValue , displayColumn . type ) ;
166+
167+ return { text : variableText , value : variableValue } ;
168+ } ) ;
169+ return acc . concat ( variableValues ) ;
170+ } , [ ] ) ;
171+ return { ...response , data : variableValues } ;
172+ } )
173+ ) ;
174+ }
175+ }
176+
177+
153178function variableValueFromCell ( value : string , columnType : FieldType ) : string {
154179 switch ( columnType ) {
155180 case FieldType . string :
0 commit comments