Skip to content

Commit

Permalink
feat(templating): completed work on built in template variables and , c…
Browse files Browse the repository at this point in the history
…loses grafana#7190, refactors grafana#7170
  • Loading branch information
torkelo committed Jan 11, 2017
1 parent 8c39cd3 commit b475f91
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 68 deletions.
51 changes: 41 additions & 10 deletions pkg/tsdb/influxdb/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package influxdb

import (
"fmt"
"strconv"
"strings"
"time"

"regexp"

Expand All @@ -15,22 +17,51 @@ var (
)

func (query *Query) Build(queryContext *tsdb.QueryContext) (string, error) {
var res string

if query.UseRawQuery && query.RawQuery != "" {
q := query.RawQuery
res = query.RawQuery
} else {
res = query.renderSelectors(queryContext)
res += query.renderMeasurement()
res += query.renderWhereClause()
res += query.renderTimeFilter(queryContext)
res += query.renderGroupBy(queryContext)
}

interval, err := getDefinedInterval(query, queryContext)
if err != nil {
return "", err
}

res = strings.Replace(res, "$timeFilter", query.renderTimeFilter(queryContext), 1)
res = strings.Replace(res, "$interval", interval.Text, 1)
res = strings.Replace(res, "$__interval_ms", strconv.FormatInt(interval.Value.Nanoseconds()/int64(time.Millisecond), 10), 1)
res = strings.Replace(res, "$__interval", interval.Text, 1)
return res, nil
}

q = strings.Replace(q, "$timeFilter", query.renderTimeFilter(queryContext), 1)
q = strings.Replace(q, "$interval", tsdb.CalculateInterval(queryContext.TimeRange), 1)
func getDefinedInterval(query *Query, queryContext *tsdb.QueryContext) (*tsdb.Interval, error) {
defaultInterval := tsdb.CalculateInterval(queryContext.TimeRange)

return q, nil
if query.Interval == "" {
return &defaultInterval, nil
}

res := query.renderSelectors(queryContext)
res += query.renderMeasurement()
res += query.renderWhereClause()
res += query.renderTimeFilter(queryContext)
res += query.renderGroupBy(queryContext)
setInterval := strings.Replace(strings.Replace(query.Interval, "<", "", 1), ">", "", 1)
parsedSetInterval, err := time.ParseDuration(setInterval)

return res, nil
if err != nil {
return nil, err
}

if strings.Contains(query.Interval, ">") {
if defaultInterval.Value > parsedSetInterval {
return &defaultInterval, nil
}
}

return &tsdb.Interval{Value: parsedSetInterval, Text: setInterval}, nil
}

func (query *Query) renderTags() []string {
Expand Down
25 changes: 2 additions & 23 deletions pkg/tsdb/influxdb/query_part.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package influxdb
import (
"fmt"
"strings"
"time"

"github.com/grafana/grafana/pkg/tsdb"
)
Expand Down Expand Up @@ -93,30 +92,10 @@ func fieldRenderer(query *Query, queryContext *tsdb.QueryContext, part *QueryPar
return fmt.Sprintf(`"%s"`, part.Params[0])
}

func getDefinedInterval(query *Query, queryContext *tsdb.QueryContext) string {
setInterval := strings.Replace(strings.Replace(query.Interval, "<", "", 1), ">", "", 1)
defaultInterval := tsdb.CalculateInterval(queryContext.TimeRange)

if strings.Contains(query.Interval, ">") {
parsedDefaultInterval, err := time.ParseDuration(defaultInterval)
parsedSetInterval, err2 := time.ParseDuration(setInterval)

if err == nil && err2 == nil && parsedDefaultInterval > parsedSetInterval {
return defaultInterval
}
}

return setInterval
}

func functionRenderer(query *Query, queryContext *tsdb.QueryContext, part *QueryPart, innerExpr string) string {
for i, param := range part.Params {
if param == "$interval" || param == "auto" {
if query.Interval != "" {
part.Params[i] = getDefinedInterval(query, queryContext)
} else {
part.Params[i] = tsdb.CalculateInterval(queryContext.TimeRange)
}
if part.Type == "time" && param == "auto" {
part.Params[i] = "$__interval"
}
}

Expand Down
25 changes: 2 additions & 23 deletions pkg/tsdb/influxdb/query_part_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,36 +42,15 @@ func TestInfluxdbQueryPart(t *testing.T) {
So(err, ShouldBeNil)

res := part.Render(query, queryContext, "")
So(res, ShouldEqual, "time(200ms)")
So(res, ShouldEqual, "time($interval)")
})

Convey("render time with auto", func() {
part, err := NewQueryPart("time", []string{"auto"})
So(err, ShouldBeNil)

res := part.Render(query, queryContext, "")
So(res, ShouldEqual, "time(200ms)")
})

Convey("render time interval >10s", func() {
part, err := NewQueryPart("time", []string{"$interval"})
So(err, ShouldBeNil)

query.Interval = ">10s"

res := part.Render(query, queryContext, "")
So(res, ShouldEqual, "time(10s)")
})

Convey("render time interval >1s and higher interval calculation", func() {
part, err := NewQueryPart("time", []string{"$interval"})
queryContext := &tsdb.QueryContext{TimeRange: tsdb.NewTimeRange("1y", "now")}
So(err, ShouldBeNil)

query.Interval = ">1s"

res := part.Render(query, queryContext, "")
So(res, ShouldEqual, "time(168h)")
So(res, ShouldEqual, "time($__interval)")
})

Convey("render spread", func() {
Expand Down
45 changes: 44 additions & 1 deletion pkg/tsdb/influxdb/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ func TestInfluxdbQueryBuilder(t *testing.T) {
qp1, _ := NewQueryPart("field", []string{"value"})
qp2, _ := NewQueryPart("mean", []string{})

groupBy1, _ := NewQueryPart("time", []string{"$interval"})
mathPartDivideBy100, _ := NewQueryPart("math", []string{"/ 100"})
mathPartDivideByIntervalMs, _ := NewQueryPart("math", []string{"/ $__interval_ms"})

groupBy1, _ := NewQueryPart("time", []string{"$__interval"})
groupBy2, _ := NewQueryPart("tag", []string{"datacenter"})
groupBy3, _ := NewQueryPart("fill", []string{"null"})

groupByOldInterval, _ := NewQueryPart("time", []string{"$interval"})

tag1 := &Tag{Key: "hostname", Value: "server1", Operator: "="}
tag2 := &Tag{Key: "hostname", Value: "server2", Operator: "=", Condition: "OR"}

Expand Down Expand Up @@ -55,6 +60,43 @@ func TestInfluxdbQueryBuilder(t *testing.T) {
So(rawQuery, ShouldEqual, `SELECT mean("value") FROM "cpu" WHERE "hostname" = 'server1' OR "hostname" = 'server2' AND time > now() - 5m GROUP BY time(5s), "datacenter" fill(null)`)
})

Convey("can build query with math part", func() {
query := &Query{
Selects: []*Select{{*qp1, *qp2, *mathPartDivideBy100}},
Measurement: "cpu",
Interval: "5s",
}

rawQuery, err := query.Build(queryContext)
So(err, ShouldBeNil)
So(rawQuery, ShouldEqual, `SELECT mean("value") / 100 FROM "cpu" WHERE time > now() - 5m`)
})

Convey("can build query with math part using $__interval_ms variable", func() {
query := &Query{
Selects: []*Select{{*qp1, *qp2, *mathPartDivideByIntervalMs}},
Measurement: "cpu",
Interval: "5s",
}

rawQuery, err := query.Build(queryContext)
So(err, ShouldBeNil)
So(rawQuery, ShouldEqual, `SELECT mean("value") / 5000 FROM "cpu" WHERE time > now() - 5m`)
})

Convey("can build query with old $interval variable", func() {
query := &Query{
Selects: []*Select{{*qp1, *qp2}},
Measurement: "cpu",
Policy: "",
GroupBy: []*QueryPart{groupByOldInterval},
}

rawQuery, err := query.Build(queryContext)
So(err, ShouldBeNil)
So(rawQuery, ShouldEqual, `SELECT mean("value") FROM "cpu" WHERE time > now() - 5m GROUP BY time(200ms)`)
})

Convey("can render time range", func() {
query := Query{}
Convey("render from: 2h to now-1h", func() {
Expand Down Expand Up @@ -139,4 +181,5 @@ func TestInfluxdbQueryBuilder(t *testing.T) {
So(query.renderMeasurement(), ShouldEqual, ` FROM "policy"./apa/`)
})
})

}
11 changes: 8 additions & 3 deletions pkg/tsdb/interval.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@ var (
day time.Duration = time.Hour * 24 * 365
)

func CalculateInterval(timerange *TimeRange) string {
type Interval struct {
Text string
Value time.Duration
}

func CalculateInterval(timerange *TimeRange) Interval {
interval := time.Duration((timerange.MustGetTo().UnixNano() - timerange.MustGetFrom().UnixNano()) / defaultRes)

if interval < minInterval {
return formatDuration(minInterval)
return Interval{Text: formatDuration(minInterval), Value: interval}
}

return formatDuration(roundInterval(interval))
return Interval{Text: formatDuration(roundInterval(interval)), Value: interval}
}

func formatDuration(inter time.Duration) string {
Expand Down
8 changes: 4 additions & 4 deletions pkg/tsdb/interval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,28 @@ func TestInterval(t *testing.T) {
tr := NewTimeRange("5m", "now")

interval := CalculateInterval(tr)
So(interval, ShouldEqual, "200ms")
So(interval.Text, ShouldEqual, "200ms")
})

Convey("for 15min", func() {
tr := NewTimeRange("15m", "now")

interval := CalculateInterval(tr)
So(interval, ShouldEqual, "500ms")
So(interval.Text, ShouldEqual, "500ms")
})

Convey("for 30min", func() {
tr := NewTimeRange("30m", "now")

interval := CalculateInterval(tr)
So(interval, ShouldEqual, "1s")
So(interval.Text, ShouldEqual, "1s")
})

Convey("for 1h", func() {
tr := NewTimeRange("1h", "now")

interval := CalculateInterval(tr)
So(interval, ShouldEqual, "2s")
So(interval.Text, ShouldEqual, "2s")
})

Convey("Round interval", func() {
Expand Down
10 changes: 6 additions & 4 deletions public/app/features/templating/templateSrv.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ function (angular, _, kbn) {
this._grafanaVariables = {};
this._adhocVariables = {};

// default built ins
this._builtIns = {};
this._builtIns['__interval'] = {text: '1s', value: '1s'};
this._builtIns['__interval_ms'] = {text: '100', value: '100'};

this.init = function(variables) {
this.variables = variables;
this.updateTemplateData();
Expand All @@ -43,9 +48,6 @@ function (angular, _, kbn) {
this._index[variable.name] = variable;
}

// default built ins
this._index['__interval'] = {text: '1s', value: '1s'};
this._index['__interval_ms'] = {text: '100', value: '100'};
};

this.variableInitialized = function(variable) {
Expand Down Expand Up @@ -136,7 +138,7 @@ function (angular, _, kbn) {
str = _.escape(str);
this._regex.lastIndex = 0;
return str.replace(this._regex, function(match, g1, g2) {
if (self._index[g1 || g2]) {
if (self._index[g1 || g2] || self._builtIns[g1 || g2]) {
return '<span class="template-variable">' + match + '</span>';
}
return match;
Expand Down

0 comments on commit b475f91

Please sign in to comment.