mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(influxdb): add querypart renderers
This commit is contained in:
parent
d0e6a9559e
commit
c7abd3ba4e
@ -15,11 +15,6 @@ type Tag struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
type QueryPart struct {
|
||||
Type string
|
||||
Params []string
|
||||
}
|
||||
|
||||
type Select []QueryPart
|
||||
|
||||
type InfluxDbSelect struct {
|
||||
|
@ -14,70 +14,84 @@ func TestInfluxdbQueryParser(t *testing.T) {
|
||||
|
||||
Convey("converting metric name", func() {
|
||||
json := `
|
||||
{
|
||||
"dsType": "influxdb",
|
||||
"groupBy": [
|
||||
{
|
||||
"params": [
|
||||
"$interval"
|
||||
],
|
||||
"type": "time"
|
||||
},
|
||||
{
|
||||
"type": "tag",
|
||||
"params": [
|
||||
"datacenter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"null"
|
||||
],
|
||||
"type": "fill"
|
||||
}
|
||||
],
|
||||
"measurement": "logins.count",
|
||||
"policy": "default",
|
||||
"refId": "B",
|
||||
"resultFormat": "time_series",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"value"
|
||||
{
|
||||
"dsType": "influxdb",
|
||||
"groupBy": [
|
||||
{
|
||||
"params": [
|
||||
"$interval"
|
||||
],
|
||||
"type": "time"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"datacenter"
|
||||
],
|
||||
"type": "tag"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"null"
|
||||
],
|
||||
"type": "fill"
|
||||
}
|
||||
],
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
|
||||
"measurement": "logins.count",
|
||||
"policy": "default",
|
||||
"refId": "B",
|
||||
"resultFormat": "time_series",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"type": "field",
|
||||
"params": [
|
||||
"value"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "count",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"type": "field",
|
||||
"params": [
|
||||
"value"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "mean",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"type": "field",
|
||||
"params": [
|
||||
"value"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "mean",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"type": "math",
|
||||
"params": [
|
||||
" / 100"
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"type": "count"
|
||||
"tags": [
|
||||
{
|
||||
"key": "datacenter",
|
||||
"operator": "=",
|
||||
"value": "America"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"value"
|
||||
],
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
|
||||
],
|
||||
"type": "mean"
|
||||
}
|
||||
]
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"key": "datacenter",
|
||||
"operator": "=",
|
||||
"value": "America"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
modelJson, err := simplejson.NewJson([]byte(json))
|
||||
@ -86,7 +100,7 @@ func TestInfluxdbQueryParser(t *testing.T) {
|
||||
res, err := parser.Parse(modelJson)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(res.GroupBy), ShouldEqual, 3)
|
||||
So(len(res.Selects), ShouldEqual, 2)
|
||||
So(len(res.Selects), ShouldEqual, 3)
|
||||
So(len(res.Tags), ShouldEqual, 1)
|
||||
})
|
||||
})
|
||||
|
@ -1,5 +1,124 @@
|
||||
package influxdb
|
||||
|
||||
type Selector interface {
|
||||
Render(input string) string
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var renders map[string]QueryDefinition
|
||||
|
||||
type QueryDefinition struct {
|
||||
Renderer func(part *QueryPart, innerExpr string) string
|
||||
}
|
||||
|
||||
func init() {
|
||||
renders = make(map[string]QueryDefinition)
|
||||
|
||||
renders["field"] = QueryDefinition{Renderer: fieldRenderer}
|
||||
|
||||
renders["spread"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["count"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["distinct"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["integral"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["mean"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["median"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["sum"] = QueryDefinition{Renderer: functionRenderer}
|
||||
|
||||
renders["derivative"] = QueryDefinition{
|
||||
Renderer: functionRenderer,
|
||||
//params: [{ name: "duration", type: "interval", options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h']}],
|
||||
}
|
||||
|
||||
renders["non_negative_derivative"] = QueryDefinition{
|
||||
Renderer: functionRenderer,
|
||||
//params: [{ name: "duration", type: "interval", options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h']}],
|
||||
}
|
||||
renders["difference"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["moving_average"] = QueryDefinition{
|
||||
Renderer: functionRenderer,
|
||||
//params: [{ name: "window", type: "number", options: [5, 10, 20, 30, 40]}]
|
||||
}
|
||||
renders["stddev"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["time"] = QueryDefinition{
|
||||
Renderer: functionRenderer,
|
||||
//params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }],
|
||||
}
|
||||
renders["fill"] = QueryDefinition{
|
||||
Renderer: functionRenderer,
|
||||
//params: [{ name: "fill", type: "string", options: ['none', 'null', '0', 'previous'] }],
|
||||
}
|
||||
renders["elapsed"] = QueryDefinition{
|
||||
Renderer: functionRenderer,
|
||||
//params: [{ name: "duration", type: "interval", options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h']}],
|
||||
}
|
||||
renders["bottom"] = QueryDefinition{
|
||||
Renderer: functionRenderer,
|
||||
//params: [{name: 'count', type: 'int'}],
|
||||
}
|
||||
|
||||
renders["first"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["last"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["max"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["min"] = QueryDefinition{Renderer: functionRenderer}
|
||||
renders["percentile"] = QueryDefinition{
|
||||
Renderer: functionRenderer,
|
||||
//params: [{name: 'nth', type: 'int'}],
|
||||
}
|
||||
renders["top"] = QueryDefinition{
|
||||
Renderer: functionRenderer,
|
||||
//params: [{name: 'count', type: 'int'}],
|
||||
}
|
||||
renders["tag"] = QueryDefinition{
|
||||
Renderer: fieldRenderer,
|
||||
//params: [{name: 'tag', type: 'string', dynamicLookup: true}],
|
||||
}
|
||||
|
||||
renders["math"] = QueryDefinition{Renderer: suffixRenderer}
|
||||
renders["alias"] = QueryDefinition{Renderer: aliasRenderer}
|
||||
}
|
||||
|
||||
func fieldRenderer(part *QueryPart, innerExpr string) string {
|
||||
if part.Params[0] == "*" {
|
||||
return "*"
|
||||
}
|
||||
return fmt.Sprintf(`"%v"`, part.Params[0])
|
||||
}
|
||||
|
||||
func functionRenderer(part *QueryPart, innerExpr string) string {
|
||||
params := strings.Join(part.Params, ", ")
|
||||
|
||||
if len(part.Params) > 0 {
|
||||
return fmt.Sprintf("%s(%s, %s)", part.Type, innerExpr, params)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s(%s)", part.Type, innerExpr)
|
||||
}
|
||||
|
||||
func suffixRenderer(part *QueryPart, innerExpr string) string {
|
||||
return fmt.Sprintf("%s %s", innerExpr, part.Params[0])
|
||||
}
|
||||
|
||||
func aliasRenderer(part *QueryPart, innerExpr string) string {
|
||||
return fmt.Sprintf(`%s AS "%s"`, innerExpr, part.Params[0])
|
||||
}
|
||||
|
||||
func (r QueryDefinition) Render(part *QueryPart, innerExpr string) string {
|
||||
return r.Renderer(part, innerExpr)
|
||||
}
|
||||
|
||||
type QueryPartDefinition struct {
|
||||
}
|
||||
|
||||
type QueryPart struct {
|
||||
Type string
|
||||
Params []string
|
||||
}
|
||||
|
||||
func (qp *QueryPart) Render(expr string) (string, error) {
|
||||
renderFn, exist := renders[qp.Type]
|
||||
if !exist {
|
||||
return "", fmt.Errorf("could not find render strategy %s", qp.Type)
|
||||
}
|
||||
|
||||
return renderFn.Renderer(qp, expr), nil
|
||||
}
|
||||
|
@ -9,55 +9,54 @@ import (
|
||||
func TestInfluxdbQueryPart(t *testing.T) {
|
||||
Convey("Influxdb query part builder", t, func() {
|
||||
|
||||
Convey("can build query", func() {
|
||||
Convey("should handle field renderer parts", func() {
|
||||
part := QueryPart{
|
||||
Type: "field",
|
||||
Params: []string{"value"},
|
||||
}
|
||||
|
||||
res, _ := part.Render("value")
|
||||
So(res, ShouldEqual, `"value"`)
|
||||
})
|
||||
|
||||
Convey("empty queries should return error", func() {
|
||||
Convey("should handle nested function parts", func() {
|
||||
part := QueryPart{
|
||||
Type: "derivative",
|
||||
Params: []string{"10s"},
|
||||
}
|
||||
|
||||
res, _ := part.Render("mean(value)")
|
||||
So(res, ShouldEqual, "derivative(mean(value), 10s)")
|
||||
})
|
||||
|
||||
Convey("should nest spread function", func() {
|
||||
part := QueryPart{
|
||||
Type: "spread",
|
||||
}
|
||||
|
||||
res, err := part.Render("value")
|
||||
So(err, ShouldBeNil)
|
||||
So(res, ShouldEqual, "spread(value)")
|
||||
})
|
||||
|
||||
Convey("should handle suffix parts", func() {
|
||||
part := QueryPart{
|
||||
Type: "math",
|
||||
Params: []string{"/ 100"},
|
||||
}
|
||||
|
||||
res, _ := part.Render("mean(value)")
|
||||
So(res, ShouldEqual, "mean(value) / 100")
|
||||
})
|
||||
|
||||
Convey("should handle alias parts", func() {
|
||||
part := QueryPart{
|
||||
Type: "alias",
|
||||
Params: []string{"test"},
|
||||
}
|
||||
|
||||
res, _ := part.Render("mean(value)")
|
||||
So(res, ShouldEqual, `mean(value) AS "test"`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
describe('series with mesurement only', () => {
|
||||
it('should handle nested function parts', () => {
|
||||
var part = queryPart.create({
|
||||
type: 'derivative',
|
||||
params: ['10s'],
|
||||
});
|
||||
|
||||
expect(part.text).to.be('derivative(10s)');
|
||||
expect(part.render('mean(value)')).to.be('derivative(mean(value), 10s)');
|
||||
});
|
||||
|
||||
it('should nest spread function', () => {
|
||||
var part = queryPart.create({
|
||||
type: 'spread'
|
||||
});
|
||||
|
||||
expect(part.text).to.be('spread()');
|
||||
expect(part.render('value')).to.be('spread(value)');
|
||||
});
|
||||
|
||||
it('should handle suffirx parts', () => {
|
||||
var part = queryPart.create({
|
||||
type: 'math',
|
||||
params: ['/ 100'],
|
||||
});
|
||||
|
||||
expect(part.text).to.be('math(/ 100)');
|
||||
expect(part.render('mean(value)')).to.be('mean(value) / 100');
|
||||
});
|
||||
|
||||
it('should handle alias parts', () => {
|
||||
var part = queryPart.create({
|
||||
type: 'alias',
|
||||
params: ['test'],
|
||||
});
|
||||
|
||||
expect(part.text).to.be('alias(test)');
|
||||
expect(part.render('mean(value)')).to.be('mean(value) AS "test"');
|
||||
});
|
||||
|
||||
});
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user