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
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryPart struct {
|
|
||||||
Type string
|
|
||||||
Params []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Select []QueryPart
|
type Select []QueryPart
|
||||||
|
|
||||||
type InfluxDbSelect struct {
|
type InfluxDbSelect struct {
|
||||||
|
@ -24,10 +24,10 @@ func TestInfluxdbQueryParser(t *testing.T) {
|
|||||||
"type": "time"
|
"type": "time"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "tag",
|
|
||||||
"params": [
|
"params": [
|
||||||
"datacenter"
|
"datacenter"
|
||||||
]
|
],
|
||||||
|
"type": "tag"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"params": [
|
"params": [
|
||||||
@ -43,30 +43,44 @@ func TestInfluxdbQueryParser(t *testing.T) {
|
|||||||
"select": [
|
"select": [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"type": "field",
|
||||||
"params": [
|
"params": [
|
||||||
"value"
|
"value"
|
||||||
],
|
]
|
||||||
"type": "field"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"params": [
|
"type": "count",
|
||||||
|
"params": []
|
||||||
],
|
|
||||||
"type": "count"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"type": "field",
|
||||||
"params": [
|
"params": [
|
||||||
"value"
|
"value"
|
||||||
],
|
]
|
||||||
"type": "field"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"params": [
|
"type": "mean",
|
||||||
|
"params": []
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"type": "mean"
|
[
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"params": [
|
||||||
|
"value"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "mean",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "math",
|
||||||
|
"params": [
|
||||||
|
" / 100"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -86,7 +100,7 @@ func TestInfluxdbQueryParser(t *testing.T) {
|
|||||||
res, err := parser.Parse(modelJson)
|
res, err := parser.Parse(modelJson)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(res.GroupBy), ShouldEqual, 3)
|
So(len(res.GroupBy), ShouldEqual, 3)
|
||||||
So(len(res.Selects), ShouldEqual, 2)
|
So(len(res.Selects), ShouldEqual, 3)
|
||||||
So(len(res.Tags), ShouldEqual, 1)
|
So(len(res.Tags), ShouldEqual, 1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,124 @@
|
|||||||
package influxdb
|
package influxdb
|
||||||
|
|
||||||
type Selector interface {
|
import (
|
||||||
Render(input string) string
|
"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) {
|
func TestInfluxdbQueryPart(t *testing.T) {
|
||||||
Convey("Influxdb query part builder", t, func() {
|
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