1
1
import { cloneDeep , first as _first , map as _map , groupBy } from 'lodash' ;
2
2
import { Observable , lastValueFrom , from , isObservable , of } from 'rxjs' ;
3
- import { catchError , mergeMap , map } from 'rxjs/operators' ;
3
+ import { catchError , mergeMap , map , shareReplay } from 'rxjs/operators' ;
4
4
5
5
import {
6
6
AbstractQuery ,
@@ -27,6 +27,7 @@ import {
27
27
QueryFixAction ,
28
28
ScopedVars ,
29
29
SupplementaryQueryType ,
30
+ TestDataSourceResponse ,
30
31
TimeRange ,
31
32
} from '@grafana/data' ;
32
33
import { BucketAggregation , DataLinkConfig , ElasticsearchQuery , Field as QuickwitField , FieldMapping , IndexMetadata , TermsQuery , FieldCapabilitiesResponse } from './types' ;
@@ -61,6 +62,15 @@ type FieldCapsSpec = {
61
62
_range ?: TimeRange
62
63
}
63
64
65
+ function getTimeFieldInfoFromIndexMetadata ( indexMetadata : any ) {
66
+ let fields = getAllFields ( indexMetadata . index_config . doc_mapping . field_mappings ) ;
67
+ let timestampFieldName = indexMetadata . index_config . doc_mapping . timestamp_field
68
+ let timestampField = fields . find ( ( field ) => field . json_path === timestampFieldName ) ;
69
+ let timestampFormat = timestampField ? timestampField . field_mapping . output_format || '' : ''
70
+ let timestampFieldInfos = { 'field' : timestampFieldName , 'format' : timestampFormat }
71
+ return timestampFieldInfos
72
+ }
73
+
64
74
export class QuickwitDataSource
65
75
extends DataSourceWithBackend < ElasticsearchQuery , QuickwitOptions >
66
76
implements
@@ -73,12 +83,17 @@ export class QuickwitDataSource
73
83
timeOutputFormat : string ;
74
84
logMessageField ?: string ;
75
85
logLevelField ?: string ;
76
- queryBuilder : ElasticQueryBuilder ;
77
86
dataLinks : DataLinkConfig [ ] ;
78
87
languageProvider : ElasticsearchLanguageProvider ;
79
88
80
89
private logContextProvider : LogContextProvider ;
81
90
91
+
92
+ // Observables from index metadata
93
+ indexMetadata$ : any ;
94
+ timeFieldInfo$ : Observable < { field : string , format : string } >
95
+ queryBuilder$ : Observable < ElasticQueryBuilder > ;
96
+
82
97
constructor (
83
98
instanceSettings : DataSourceInstanceSettings < QuickwitOptions > ,
84
99
private readonly templateSrv : TemplateSrv = getTemplateSrv ( )
@@ -88,37 +103,32 @@ export class QuickwitDataSource
88
103
this . index = settingsData . index || '' ;
89
104
this . timeField = ''
90
105
this . timeOutputFormat = ''
91
- this . queryBuilder = new ElasticQueryBuilder ( {
92
- timeField : this . timeField ,
93
- } ) ;
94
- from ( this . getResource ( 'indexes/' + this . index ) ) . pipe (
95
- map ( ( indexMetadata ) => {
96
- let fields = getAllFields ( indexMetadata . index_config . doc_mapping . field_mappings ) ;
97
- let timestampFieldName = indexMetadata . index_config . doc_mapping . timestamp_field
98
- let timestampField = fields . find ( ( field ) => field . json_path === timestampFieldName ) ;
99
- let timestampFormat = timestampField ? timestampField . field_mapping . output_format || '' : ''
100
- let timestampFieldInfos = { 'field' : timestampFieldName , 'format' : timestampFormat }
101
- return timestampFieldInfos
102
- } ) ,
106
+
107
+ this . indexMetadata$ = from ( this . getResource ( 'indexes/' + this . index ) )
108
+ this . timeFieldInfo$ = this . indexMetadata$ . pipe (
109
+ map ( getTimeFieldInfoFromIndexMetadata ) ,
103
110
catchError ( ( err ) => {
104
111
if ( ! err . data || ! err . data . error ) {
105
112
let err_source = extractJsonPayload ( err . data . error )
106
113
if ( ! err_source ) {
107
114
throw err
108
115
}
109
116
}
110
-
111
117
// the error will be handle in the testDatasource function
112
118
return of ( { 'field' : '' , 'format' : '' } )
113
- } )
114
- ) . subscribe ( result => {
115
- this . timeField = result . field ;
116
- this . timeOutputFormat = result . format ;
117
- this . queryBuilder = new ElasticQueryBuilder ( {
118
- timeField : this . timeField ,
119
- } ) ;
120
- } ) ;
121
-
119
+ } ) ,
120
+ shareReplay ( 1 ) ,
121
+ )
122
+ this . timeFieldInfo$ . subscribe ( ( timeFieldInfo ) => {
123
+ this . timeField = timeFieldInfo . field ;
124
+ this . timeOutputFormat = timeFieldInfo . format
125
+ } )
126
+ this . queryBuilder$ = this . timeFieldInfo$ . pipe (
127
+ map ( ( timeFieldInfo ) => {
128
+ return new ElasticQueryBuilder ( { timeField :timeFieldInfo . field } )
129
+ } ) ,
130
+ shareReplay ( 1 ) ,
131
+ )
122
132
this . logMessageField = settingsData . logMessageField || '' ;
123
133
this . logLevelField = settingsData . logLevelField || '' ;
124
134
this . dataLinks = settingsData . dataLinks || [ ] ;
@@ -147,10 +157,8 @@ export class QuickwitDataSource
147
157
message : 'Cannot save datasource, `index` is required' ,
148
158
} ;
149
159
}
150
-
151
- return lastValueFrom (
152
- from ( this . getResource ( 'indexes/' + this . index ) ) . pipe (
153
- mergeMap ( ( indexMetadata ) => {
160
+ const validation$ : Observable < TestDataSourceResponse > = this . indexMetadata$ . pipe (
161
+ mergeMap ( ( indexMetadata : IndexMetadata ) : Observable < TestDataSourceResponse > => {
154
162
let error = this . validateIndexConfig ( indexMetadata ) ;
155
163
if ( error ) {
156
164
return of ( {
@@ -160,7 +168,7 @@ export class QuickwitDataSource
160
168
}
161
169
return of ( { status : 'success' , message : `Index OK. Time field name OK` } ) ;
162
170
} ) ,
163
- catchError ( ( err ) => {
171
+ catchError ( ( err ) : Observable < TestDataSourceResponse > => {
164
172
if ( err . data && err . data . error ) {
165
173
let err_source = extractJsonPayload ( err . data . error )
166
174
if ( err_source ) {
@@ -177,7 +185,8 @@ export class QuickwitDataSource
177
185
}
178
186
} )
179
187
)
180
- ) ;
188
+
189
+ return lastValueFrom ( validation$ ) ;
181
190
}
182
191
183
192
validateIndexConfig ( indexMetadata : IndexMetadata ) : string | undefined {
@@ -347,18 +356,18 @@ export class QuickwitDataSource
347
356
index : this . index ,
348
357
} ) ;
349
358
350
- let esQuery = JSON . stringify ( this . queryBuilder . getTermsQuery ( queryDef ) ) ;
351
- esQuery = esQuery . replace ( / \$ t i m e F r o m / g, range . from . valueOf ( ) . toString ( ) ) ;
352
- esQuery = esQuery . replace ( / \$ t i m e T o / g, range . to . valueOf ( ) . toString ( ) ) ;
353
- esQuery = header + '\n' + esQuery + '\n' ;
354
359
const resourceOptions = {
355
- headers : {
356
- 'content-type' : 'application/x-ndjson'
357
- }
360
+ headers : { 'content-type' : 'application/x-ndjson' }
358
361
} ;
359
- const termsObservable = from ( this . postResource ( "_elastic/_msearch" , esQuery , resourceOptions ) ) ;
360
362
361
- return termsObservable . pipe (
363
+ return this . queryBuilder$ . pipe (
364
+ mergeMap ( ( queryBuilder ) => {
365
+ let esQuery = JSON . stringify ( queryBuilder . getTermsQuery ( queryDef ) ) ;
366
+ esQuery = esQuery . replace ( / \$ t i m e F r o m / g, range . from . valueOf ( ) . toString ( ) ) ;
367
+ esQuery = esQuery . replace ( / \$ t i m e T o / g, range . to . valueOf ( ) . toString ( ) ) ;
368
+ esQuery = header + '\n' + esQuery + '\n' ;
369
+ return from ( this . postResource ( "_elastic/_msearch" , esQuery , resourceOptions ) )
370
+ } ) ,
362
371
map ( ( res ) => {
363
372
if ( ! res . responses [ 0 ] . aggregations ) {
364
373
return [ ] ;
0 commit comments