diff --git a/pkg/tsdb/prometheus/models/query.go b/pkg/tsdb/prometheus/models/query.go index 384648bc837..91c7caf60bc 100644 --- a/pkg/tsdb/prometheus/models/query.go +++ b/pkg/tsdb/prometheus/models/query.go @@ -14,23 +14,25 @@ import ( // Internal interval and range variables const ( - varInterval = "$__interval" - varIntervalMs = "$__interval_ms" - varRange = "$__range" - varRangeS = "$__range_s" - varRangeMs = "$__range_ms" - varRateInterval = "$__rate_interval" + varInterval = "$__interval" + varIntervalMs = "$__interval_ms" + varRange = "$__range" + varRangeS = "$__range_s" + varRangeMs = "$__range_ms" + varRateInterval = "$__rate_interval" + varRateIntervalMs = "$__rate_interval_ms" ) // Internal interval and range variables with {} syntax // Repetitive code, we should have functionality to unify these const ( - varIntervalAlt = "${__interval}" - varIntervalMsAlt = "${__interval_ms}" - varRangeAlt = "${__range}" - varRangeSAlt = "${__range_s}" - varRangeMsAlt = "${__range_ms}" - varRateIntervalAlt = "${__rate_interval}" + varIntervalAlt = "${__interval}" + varIntervalMsAlt = "${__interval_ms}" + varRangeAlt = "${__range}" + varRangeSAlt = "${__range_s}" + varRangeMsAlt = "${__range_ms}" + varRateIntervalAlt = "${__rate_interval}" + varRateIntervalMsAlt = "${__rate_interval_ms}" ) type TimeSeriesQueryType string @@ -227,6 +229,7 @@ func interpolateVariables(expr, queryInterval string, interval time.Duration, expr = strings.ReplaceAll(expr, varRangeMs, strconv.FormatInt(rangeMs, 10)) expr = strings.ReplaceAll(expr, varRangeS, strconv.FormatInt(rangeSRounded, 10)) expr = strings.ReplaceAll(expr, varRange, strconv.FormatInt(rangeSRounded, 10)+"s") + expr = strings.ReplaceAll(expr, varRateIntervalMs, strconv.FormatInt(int64(rateInterval/time.Millisecond), 10)) expr = strings.ReplaceAll(expr, varRateInterval, rateInterval.String()) // Repetitive code, we should have functionality to unify these @@ -235,16 +238,17 @@ func interpolateVariables(expr, queryInterval string, interval time.Duration, expr = strings.ReplaceAll(expr, varRangeMsAlt, strconv.FormatInt(rangeMs, 10)) expr = strings.ReplaceAll(expr, varRangeSAlt, strconv.FormatInt(rangeSRounded, 10)) expr = strings.ReplaceAll(expr, varRangeAlt, strconv.FormatInt(rangeSRounded, 10)+"s") + expr = strings.ReplaceAll(expr, varRateIntervalMsAlt, strconv.FormatInt(int64(rateInterval/time.Millisecond), 10)) expr = strings.ReplaceAll(expr, varRateIntervalAlt, rateInterval.String()) return expr } func isVariableInterval(interval string) bool { - if interval == varInterval || interval == varIntervalMs || interval == varRateInterval { + if interval == varInterval || interval == varIntervalMs || interval == varRateInterval || interval == varRateIntervalMs { return true } // Repetitive code, we should have functionality to unify these - if interval == varIntervalAlt || interval == varIntervalMsAlt || interval == varRateIntervalAlt { + if interval == varIntervalAlt || interval == varIntervalMsAlt || interval == varRateIntervalAlt || interval == varRateIntervalMsAlt { return true } return false diff --git a/pkg/tsdb/prometheus/models/query_test.go b/pkg/tsdb/prometheus/models/query_test.go index dd30c5f0b24..4322ed802a1 100644 --- a/pkg/tsdb/prometheus/models/query_test.go +++ b/pkg/tsdb/prometheus/models/query_test.go @@ -385,6 +385,60 @@ func TestParse(t *testing.T) { require.Equal(t, 1*time.Minute, res.Step) }) + t.Run("parsing query model with $__rate_interval_ms variable", func(t *testing.T) { + timeRange := backend.TimeRange{ + From: now, + To: now.Add(48 * time.Hour), + } + + q := queryContext(`{ + "expr": "rate(ALERTS{job=\"test\" [$__rate_interval_ms]})", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + }`, timeRange) + + res, err := models.Parse(q, "15s", intervalCalculator, false) + require.NoError(t, err) + require.Equal(t, "rate(ALERTS{job=\"test\" [135000]})", res.Expr) + }) + + t.Run("parsing query model with $__rate_interval_ms and $__rate_interval variable", func(t *testing.T) { + timeRange := backend.TimeRange{ + From: now, + To: now.Add(48 * time.Hour), + } + + q := queryContext(`{ + "expr": "rate(ALERTS{job=\"test\" [$__rate_interval_ms]}) + rate(ALERTS{job=\"test\" [$__rate_interval]})", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + }`, timeRange) + + res, err := models.Parse(q, "15s", intervalCalculator, false) + require.NoError(t, err) + require.Equal(t, "rate(ALERTS{job=\"test\" [135000]}) + rate(ALERTS{job=\"test\" [2m15s]})", res.Expr) + }) + + t.Run("parsing query model with ${__rate_interval_ms} and ${__rate_interval} variable", func(t *testing.T) { + timeRange := backend.TimeRange{ + From: now, + To: now.Add(48 * time.Hour), + } + + q := queryContext(`{ + "expr": "rate(ALERTS{job=\"test\" [${__rate_interval_ms}]}) + rate(ALERTS{job=\"test\" [${__rate_interval}]})", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + }`, timeRange) + + res, err := models.Parse(q, "15s", intervalCalculator, false) + require.NoError(t, err) + require.Equal(t, "rate(ALERTS{job=\"test\" [135000]}) + rate(ALERTS{job=\"test\" [2m15s]})", res.Expr) + }) + t.Run("parsing query model of range query", func(t *testing.T) { timeRange := backend.TimeRange{ From: now,