Skip to content
This repository was archived by the owner on Dec 11, 2022. It is now read-only.

Commit 8327eba

Browse files
authored
Handle backend Macros decoding to support alerts which includes macros (fixes #361)
Merge pull request #375 from doitintl/fix/OfirCohen/backendMacros
2 parents 8dc0fdc + 3cdf6a3 commit 8327eba

File tree

4 files changed

+81
-26
lines changed

4 files changed

+81
-26
lines changed
-332 KB
Binary file not shown.

dist/partials/query.editor.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,21 +168,28 @@
168168
<datalist id="locationList">
169169
<option value=US>United States (US)</option>
170170
<option value=EU>European Union (EU)</option>
171+
<option value=us-west1>Oregon (us-west1)</option>
171172
<option value=us-west2>Los Angeles (us-west2)</option>
172-
<option value=northamerica-northeast1>Montréal (northamerica-northeast1)</option>
173+
<option value=us-west3>Salt Lake City (us-west3)</option>
174+
<option value=us-west4>Las Vegas (us-west4)</option>
175+
<option value=us-central1>Iowa (us-central1)</option>
173176
<option value=us-east1>South Carolina (us-east1)</option>
174177
<option value=us-east4>Northern Virginia (us-east4)</option>
178+
<option value=northamerica-northeast1>Montréal (northamerica-northeast1)</option>
175179
<option value=southamerica-east1>São Paulo (southamerica-east1)</option>
176180
<option value=europe-north1>Finland (europe-north1)</option>
181+
<option value=europe-west1>Belgium (europe-west1)</option>
177182
<option value=europe-west2>London (europe-west2)</option>
178183
<option value=europe-west3>Frankfurt (europe-west3)</option>
184+
<option value=europe-west4>Netherlands (europe-west4)</option>
179185
<option value=europe-west6>Zürich (europe-west6)</option>
180186
<option value=asia-east2>Hong Kong (asia-east2)</option>
181187
<option value=asia-south1>Mumbai (asia-south1)</option>
182188
<option value=asia-northeast2>Osaka (asia-northeast2)</option>
183189
<option value=asia-east1>Taiwan (asia-east1)</option>
184190
<option value=asia-northeast1>Tokyo (asia-northeast1)</option>
185191
<option value=asia-southeast1>Singapore (asia-southeast1)</option>
192+
<option value=asia-southeast2>Jakarta (asia-southeast2)</option>
186193
<option value=australia-southeast1>Sydney (australia-southeast1)</option>
187194
<option value=asia-northeast3>Seoul (asia-northeast3)</option>
188195
</datalist>

pkg/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
func main() {
11-
// log.DefaultLogger.Info("lior test")
11+
// log.DefaultLogger.Info("Ofir test ************") // uncomment and lookup on look to verify you are using correct backend
1212
// Start listening to requests send from Grafana. This call is blocking so
1313
// it wont finish until Grafana shutsdown the process or the plugin choose
1414
// to exit close down by itself

pkg/plugin.go

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"net/http"
9+
"regexp"
910
"strings"
1011
"time"
1112

@@ -22,36 +23,36 @@ import (
2223
)
2324

2425
type QueryModel struct {
25-
Format string `json:"format"`
26+
Format string `json:"format"`
2627
// Constant string `json:"constant"`
27-
Dataset string `json:"dataset"`
28+
Dataset string `json:"dataset"`
2829
// Group []Group `json:"group"`
29-
MetricColumn string `json:"metricColumn"`
30-
OrderByCol string/*int32*/ `json:"orderByCol"`
31-
OrderBySort string/*int32*/ `json:"orderBySort"`
32-
Partitioned bool `json:"partitioned"`
33-
PartitionedField string `json:"partitionedField"`
34-
ProjectID string `json:"project"`
35-
RawQuery bool `json:"rawQuery"`
36-
RawSQL string `json:"rawSql"`
37-
RefID string `json:"refId"`
30+
MetricColumn string `json:"metricColumn"`
31+
OrderByCol string/*int32*/ `json:"orderByCol"`
32+
OrderBySort string/*int32*/ `json:"orderBySort"`
33+
Partitioned bool `json:"partitioned"`
34+
PartitionedField string `json:"partitionedField"`
35+
ProjectID string `json:"project"`
36+
RawQuery bool `json:"rawQuery"`
37+
RawSQL string `json:"rawSql"`
38+
RefID string `json:"refId"`
3839
// Select []string `json:"select"`
39-
Sharded bool `json:"sharded"`
40-
Table string `json:"table"`
41-
TimeColumn string `json:"timeColumn"`
42-
TimeColumnType string `json:"timeColumnType"`
43-
Location string `json:"location"`
40+
Sharded bool `json:"sharded"`
41+
Table string `json:"table"`
42+
TimeColumn string `json:"timeColumn"`
43+
TimeColumnType string `json:"timeColumnType"`
44+
Location string `json:"location"`
4445
// Where []string `json:"where"`
4546
}
4647

4748
// JSONData holds the req.PluginContext.DataSourceInstanceSettings.JSONData struct
4849
type JSONData struct {
49-
AuthenticationType string `json:"authenticationType"`
50-
ClientEmail string `json:"clientEmail"`
51-
DefaultProject string `json:"defaultProject"`
52-
ProcessingLocation string `json:"processingLocation"`
53-
QueryPriority string `json:"queryPriority"`
54-
TokenURI string `json:"tokenUri"`
50+
AuthenticationType string `json:"authenticationType"`
51+
ClientEmail string `json:"clientEmail"`
52+
DefaultProject string `json:"defaultProject"`
53+
ProcessingLocation string `json:"processingLocation"`
54+
QueryPriority string `json:"queryPriority"`
55+
TokenURI string `json:"tokenUri"`
5556
}
5657

5758
// BigQueryResult represents a full resultset.
@@ -149,6 +150,7 @@ func (td *BigQueryDatasource) query(ctx context.Context, query backend.DataQuery
149150
return response
150151
}
151152
values = append(values, v)
153+
break
152154
default:
153155
response.Error = fmt.Errorf("unexpected type for field '%s': %s", fieldSchema.Name, fieldSchema.Type)
154156
return response
@@ -157,6 +159,7 @@ func (td *BigQueryDatasource) query(ctx context.Context, query backend.DataQuery
157159
frame.Fields = append(frame.Fields,
158160
data.NewField(fieldSchema.Name, nil, values),
159161
)
162+
break
160163
default:
161164
switch fieldSchema.Type {
162165
case bigquery.IntegerFieldType:
@@ -173,6 +176,7 @@ func (td *BigQueryDatasource) query(ctx context.Context, query backend.DataQuery
173176
frame.Fields = append(frame.Fields,
174177
data.NewField("values", nil, values),
175178
)
179+
break
176180
case bigquery.FloatFieldType:
177181
values := make([]float64, 0)
178182
for _, row := range bigQueryResult.Rows {
@@ -187,12 +191,15 @@ func (td *BigQueryDatasource) query(ctx context.Context, query backend.DataQuery
187191
frame.Fields = append(frame.Fields,
188192
data.NewField(fieldSchema.Name, nil, values),
189193
)
194+
break
190195
default:
191196
response.Error = fmt.Errorf("unexpected type for field '%s': %s", fieldSchema.Name, fieldSchema.Type)
192197
return response
193198
}
199+
break
194200
}
195201
}
202+
break
196203
default:
197204
response.Error = fmt.Errorf("unimplemented format '%s'. expected one of ['time_series']", queryModel.Format)
198205
return response
@@ -308,7 +315,7 @@ func (td *BigQueryDatasource) executeQuery(ctx context.Context, queryModel Query
308315
}
309316

310317
// substituteVariables replaces any placeholder variables
311-
sql := substituteVariables(queryModel.RawSQL, originalQuery)
318+
sql := substituteVariables(queryModel.RawSQL, queryModel, originalQuery)
312319
log.DefaultLogger.Debug(fmt.Sprintf("query: %s\n", sql))
313320

314321
// create the query
@@ -356,7 +363,7 @@ func (td *BigQueryDatasource) executeQuery(ctx context.Context, queryModel Query
356363
}
357364

358365
// substituteVariables replaces standard grafana variables in an input string and returns the result
359-
func substituteVariables(sql string, originalQuery backend.DataQuery) string {
366+
func substituteVariables(sql string, queryModel QueryModel, originalQuery backend.DataQuery) string {
360367
// __from
361368
sql = strings.Replace(sql, "${__from}", fmt.Sprintf("%d", originalQuery.TimeRange.From.UnixNano()/int64(time.Millisecond)), -1)
362369
sql = strings.Replace(sql, "${__from:date}", originalQuery.TimeRange.From.Format(time.RFC3339), -1)
@@ -371,5 +378,46 @@ func substituteVariables(sql string, originalQuery backend.DataQuery) string {
371378
sql = strings.Replace(sql, "${__to:date:seconds}", fmt.Sprintf("%d", originalQuery.TimeRange.To.Unix()), -1)
372379
sql = strings.Replace(sql, "${__to:date:YYYY-MM}", originalQuery.TimeRange.To.Format("2006-01"), -1)
373380

381+
// Macros
382+
from := adjustTime(originalQuery.TimeRange.From, queryModel.TimeColumnType)
383+
to := adjustTime(originalQuery.TimeRange.To, queryModel.TimeColumnType)
384+
wholeRange := quoteTimeColumn(queryModel.TimeColumn) + " BETWEEN " + from + " AND " + to
385+
fromRange := quoteTimeColumn(queryModel.TimeColumn) + " > " + from + " "
386+
toRange := quoteTimeColumn(queryModel.TimeColumn) + " < " + to + " "
387+
388+
timeFilterRegex := regexp.MustCompile(`\$__timeFilter\((.*?)\)`)
389+
sql = timeFilterRegex.ReplaceAllString(sql, wholeRange)
390+
timeFromRegex := regexp.MustCompile(`\$__timeFrom\(([\w_.]+)\)`)
391+
sql = timeFromRegex.ReplaceAllString(sql, fromRange)
392+
timeToRegex := regexp.MustCompile(`\$__timeTo\(([\w_.]+)\)`)
393+
sql = timeToRegex.ReplaceAllString(sql, toRange)
394+
millisTimeToRegex := regexp.MustCompile(`\$__millisTimeTo\(([\w_.]+)\)`)
395+
sql = millisTimeToRegex.ReplaceAllString(sql, to)
396+
millisTimeFromRegex := regexp.MustCompile(`\$__millisTimeFrom\(([\w_.]+)\)`)
397+
sql = millisTimeFromRegex.ReplaceAllString(sql, from)
398+
374399
return sql
375400
}
401+
402+
func adjustTime(value time.Time, timeType string) string {
403+
switch timeType {
404+
case "DATE":
405+
return fmt.Sprintf("'%s'", value.Format("2006-01-01")) // '2006-01-01'
406+
case "DATETIME":
407+
return fmt.Sprintf("'%s'", value.Format("2006-01-01 15:04:05")) // '2006-01-01 15:04:05'
408+
default:
409+
return fmt.Sprintf("TIMESTAMP_MILLIS (%d)", value.UnixMilli()) // TIMESTAMP_MILLIS (1612056873199)
410+
}
411+
}
412+
413+
func quoteTimeColumn(value string) string {
414+
vals := strings.Split(value, ".")
415+
res := ""
416+
for i := 0; i < len(vals); i++ {
417+
res = res + "`" + vals[i] + "`"
418+
if len(vals) > 1 && i+1 < len(vals) {
419+
res = res + "."
420+
}
421+
}
422+
return res
423+
}

0 commit comments

Comments
 (0)