Refactor queryConfig to have field aliases and function arguments

pull/10616/head
Chris Goller 2017-10-10 17:01:50 -05:00
parent baedbf35c4
commit 3ba1a4c94d
14 changed files with 232 additions and 167 deletions

View File

@ -495,8 +495,10 @@ type TriggerValues struct {
// Field represent influxql fields and functions from the UI
type Field struct {
Field string `json:"field"`
Funcs []string `json:"funcs"`
Name string `json:"name"`
Type string `json:"type"`
Alias string `json:"alias"`
Args []string `json:"args"`
}
// GroupBy represents influxql group by tags from the UI

View File

@ -140,8 +140,7 @@ func Convert(influxQL string) (chronograf.QueryConfig, error) {
}
}
fields := make(map[string][]string)
order := make(map[string]int)
qc.Fields = []chronograf.Field{}
for _, fld := range stmt.Fields {
switch f := fld.Expr.(type) {
default:
@ -164,29 +163,22 @@ func Convert(influxQL string) (chronograf.QueryConfig, error) {
if ref.Type != influxql.Unknown {
return raw, nil
}
if call, ok := fields[ref.Val]; !ok {
order[ref.Val] = len(fields)
fields[ref.Val] = []string{f.Name}
} else {
fields[ref.Val] = append(call, f.Name)
}
qc.Fields = append(qc.Fields, chronograf.Field{
Name: f.Name,
Type: "func",
Alias: fld.Alias,
Args: []string{ref.Val},
})
case *influxql.VarRef:
if f.Type != influxql.Unknown {
return raw, nil
}
if _, ok := fields[f.Val]; !ok {
order[f.Val] = len(fields)
fields[f.Val] = []string{}
}
}
}
qc.Fields = make([]chronograf.Field, len(fields))
for fld, funcs := range fields {
i := order[fld]
qc.Fields[i] = chronograf.Field{
Field: fld,
Funcs: funcs,
qc.Fields = append(qc.Fields, chronograf.Field{
Name: f.Val,
Type: "field",
Alias: fld.Alias,
Args: []string{},
})
}
}

View File

@ -1,9 +1,9 @@
package influx
import (
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/chronograf"
)
@ -24,20 +24,24 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_idle",
Funcs: []string{},
Name: "usage_idle",
Type: "field",
Args: []string{},
},
chronograf.Field{
Field: "usage_guest_nice",
Funcs: []string{},
Name: "usage_guest_nice",
Type: "field",
Args: []string{},
},
chronograf.Field{
Field: "usage_system",
Funcs: []string{},
Name: "usage_system",
Type: "field",
Args: []string{},
},
chronograf.Field{
Field: "usage_guest",
Funcs: []string{},
Name: "usage_guest",
Type: "field",
Args: []string{},
},
},
Tags: map[string][]string{},
@ -55,18 +59,24 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_idle",
Funcs: []string{
"mean",
"median",
},
Name: "mean",
Type: "func",
Args: []string{"usage_idle"},
},
chronograf.Field{
Field: "usage_guest_nice",
Funcs: []string{
"count",
"mean",
},
Name: "median",
Type: "func",
Args: []string{"usage_idle"},
},
chronograf.Field{
Name: "count",
Type: "func",
Args: []string{"usage_guest_nice"},
},
chronograf.Field{
Name: "mean",
Type: "func",
Args: []string{"usage_guest_nice"},
},
},
Tags: map[string][]string{},
@ -108,8 +118,9 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_user",
Funcs: []string{},
Name: "usage_user",
Type: "field",
Args: []string{},
},
},
Tags: map[string][]string{"host": []string{"myhost"}},
@ -144,8 +155,9 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_user",
Funcs: []string{},
Name: "usage_user",
Type: "field",
Args: []string{},
},
},
Tags: map[string][]string{"host": []string{"myhost"}},
@ -169,8 +181,9 @@ func TestConvert(t *testing.T) {
Tags: map[string][]string{},
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_user",
Funcs: []string{},
Name: "usage_user",
Type: "field",
Args: []string{},
},
},
GroupBy: chronograf.GroupBy{
@ -216,8 +229,9 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_user",
Funcs: []string{},
Name: "usage_user",
Type: "field",
Args: []string{},
},
},
Tags: map[string][]string{},
@ -236,8 +250,9 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_user",
Funcs: []string{},
Name: "usage_user",
Type: "field",
Args: []string{},
},
},
Tags: map[string][]string{"host": []string{"myhost"}},
@ -261,8 +276,9 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_user",
Funcs: []string{},
Name: "usage_user",
Type: "field",
Args: []string{},
},
},
Tags: map[string][]string{
@ -305,8 +321,9 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_user",
Funcs: []string{},
Name: "usage_user",
Type: "field",
Args: []string{},
},
},
Tags: map[string][]string{
@ -332,20 +349,24 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_idle",
Funcs: []string{},
Name: "usage_idle",
Type: "field",
Args: []string{},
},
chronograf.Field{
Field: "usage_guest_nice",
Funcs: []string{},
Name: "usage_guest_nice",
Type: "field",
Args: []string{},
},
chronograf.Field{
Field: "usage_system",
Funcs: []string{},
Name: "usage_system",
Type: "field",
Args: []string{},
},
chronograf.Field{
Field: "usage_guest",
Funcs: []string{},
Name: "usage_guest",
Type: "field",
Args: []string{},
},
},
Tags: map[string][]string{
@ -379,20 +400,24 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_idle",
Funcs: []string{},
Name: "usage_idle",
Type: "field",
Args: []string{},
},
chronograf.Field{
Field: "usage_guest_nice",
Funcs: []string{},
Name: "usage_guest_nice",
Type: "field",
Args: []string{},
},
chronograf.Field{
Field: "usage_system",
Funcs: []string{},
Name: "usage_system",
Type: "field",
Args: []string{},
},
chronograf.Field{
Field: "usage_guest",
Funcs: []string{},
Name: "usage_guest",
Type: "field",
Args: []string{},
},
},
Tags: map[string][]string{
@ -426,9 +451,10 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_idle",
Funcs: []string{
"mean",
Name: "mean",
Type: "func",
Args: []string{
"usage_idle",
},
},
},
@ -453,9 +479,10 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_idle",
Funcs: []string{
"mean",
Name: "mean",
Type: "func",
Args: []string{
"usage_idle",
},
},
},
@ -480,9 +507,10 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_idle",
Funcs: []string{
"mean",
Name: "mean",
Type: "func",
Args: []string{
"usage_idle",
},
},
},
@ -507,9 +535,10 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_idle",
Funcs: []string{
"mean",
Name: "mean",
Type: "func",
Args: []string{
"usage_idle",
},
},
},
@ -534,9 +563,10 @@ func TestConvert(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_idle",
Funcs: []string{
"mean",
Name: "mean",
Type: "func",
Args: []string{
"usage_idle",
},
},
},
@ -573,8 +603,8 @@ func TestConvert(t *testing.T) {
t.Errorf("Convert() = %s, want %s", *got.RawText, tt.RawText)
}
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Convert() = \n%#v\n want \n%#v\n", got, tt.want)
if !cmp.Equal(got, tt.want) {
t.Errorf("Convert() = %s", cmp.Diff(got, tt.want))
}
})
}

View File

@ -422,15 +422,22 @@ func Reverse(script chronograf.TICKScript) (chronograf.AlertRule, error) {
} else {
rule.Query.GroupBy.Time = commonVars.Period
rule.Every = commonVars.Every
funcs := []string{}
if fieldFunc.Func != "" {
funcs = append(funcs, fieldFunc.Func)
}
rule.Query.Fields = []chronograf.Field{
{
Field: fieldFunc.Field,
Funcs: funcs,
},
rule.Query.Fields = []chronograf.Field{
{
Type: "func",
Name: fieldFunc.Func,
Args: []string{fieldFunc.Field},
},
}
} else {
rule.Query.Fields = []chronograf.Field{
{
Type: "field",
Name: fieldFunc.Field,
Args: []string{},
},
}
}
}

View File

@ -4,6 +4,7 @@ import (
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/chronograf"
)
@ -112,10 +113,9 @@ func TestReverse(t *testing.T) {
Measurement: "cpu",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{
"mean",
},
Name: "mean",
Args: []string{"usage_user"},
Type: "func",
},
},
GroupBy: chronograf.GroupBy{
@ -220,9 +220,10 @@ func TestReverse(t *testing.T) {
Measurement: "cpu",
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
chronograf.Field{
Field: "usage_user",
Funcs: []string{"mean"},
{
Name: "mean",
Args: []string{"usage_user"},
Type: "func",
},
},
Tags: map[string][]string{
@ -360,8 +361,9 @@ func TestReverse(t *testing.T) {
Measurement: "haproxy",
Fields: []chronograf.Field{
{
Field: "status",
Funcs: []string{"last"},
Name: "last",
Args: []string{"status"},
Type: "func",
},
},
GroupBy: chronograf.GroupBy{
@ -475,8 +477,9 @@ func TestReverse(t *testing.T) {
Measurement: "haproxy",
Fields: []chronograf.Field{
{
Field: "status",
Funcs: []string{"last"},
Name: "last",
Args: []string{"status"},
Type: "func",
},
},
GroupBy: chronograf.GroupBy{
@ -592,8 +595,9 @@ func TestReverse(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Args: []string{"usage_user"},
Type: "func",
},
},
Tags: map[string][]string{
@ -717,8 +721,9 @@ func TestReverse(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Args: []string{"usage_user"},
Type: "func",
},
},
Tags: map[string][]string{
@ -842,8 +847,9 @@ func TestReverse(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Args: []string{"usage_user"},
Type: "func",
},
},
Tags: map[string][]string{
@ -955,8 +961,9 @@ func TestReverse(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{},
Name: "usage_user",
Args: []string{},
Type: "field",
},
},
Tags: map[string][]string{
@ -1090,8 +1097,9 @@ trigger
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Args: []string{"usage_user"},
Type: "func",
},
},
Tags: map[string][]string{
@ -1226,8 +1234,9 @@ trigger
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Args: []string{"usage_user"},
Type: "func",
},
},
Tags: map[string][]string{
@ -1441,8 +1450,9 @@ trigger
Measurement: "cq",
Fields: []chronograf.Field{
{
Field: "queryOk",
Funcs: []string{},
Name: "queryOk",
Args: []string{},
Type: "field",
},
},
GroupBy: chronograf.GroupBy{
@ -1465,8 +1475,8 @@ trigger
if tt.want.Query != nil {
if got.Query == nil {
t.Errorf("Reverse() = got nil QueryConfig")
} else if !reflect.DeepEqual(*got.Query, *tt.want.Query) {
t.Errorf("Reverse() = QueryConfig not equal\n%#v\n, want \n%#v\n", *got.Query, *tt.want.Query)
} else if !cmp.Equal(*got.Query, *tt.want.Query) {
t.Errorf("Reverse() = QueryConfig not equal %s", cmp.Diff(*got.Query, *tt.want.Query))
}
}
}

View File

@ -322,8 +322,9 @@ trigger
Measurement: "cq",
Fields: []chronograf.Field{
{
Field: "queryOk",
Funcs: []string{},
Name: "queryOk",
Type: "field",
Args: []string{},
},
},
GroupBy: chronograf.GroupBy{
@ -645,8 +646,9 @@ trigger
Measurement: "cq",
Fields: []chronograf.Field{
{
Field: "queryOk",
Funcs: []string{},
Name: "queryOk",
Type: "field",
Args: []string{},
},
},
GroupBy: chronograf.GroupBy{

View File

@ -41,15 +41,18 @@ func Data(rule chronograf.AlertRule) (string, error) {
}
value := ""
for _, field := range rule.Query.Fields {
for _, fnc := range field.Funcs {
if field.Type == "func" && len(field.Args) > 0 {
// Only need a window if we have an aggregate function
value = value + "|window().period(period).every(every).align()\n"
value = value + fmt.Sprintf(`|%s('%s').as('value')`, fnc, fld)
value = value + fmt.Sprintf(`|%s('%s').as('value')`, field.Name, field.Args[0])
break // only support a single field
}
if value != "" {
break // only support a single field
}
if field.Type == "field" {
value = fmt.Sprintf(`|eval(lambda: "%s").as('value')`, field.Name)
}
}
if value == "" {
value = fmt.Sprintf(`|eval(lambda: "%s").as('value')`, fld)

View File

@ -31,8 +31,9 @@ func TestInfluxOut(t *testing.T) {
Query: &chronograf.QueryConfig{
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Type: "func",
Args: []string{"usage_user"},
},
},
},

View File

@ -26,8 +26,9 @@ func TestGenerate(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Type: "func",
Args: []string{"usage_user"},
},
},
Tags: map[string][]string{
@ -71,8 +72,9 @@ func TestThreshold(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Type: "func",
Args: []string{"usage_user"},
},
},
Tags: map[string][]string{
@ -215,8 +217,9 @@ func TestThresholdStringCrit(t *testing.T) {
Measurement: "haproxy",
Fields: []chronograf.Field{
{
Field: "status",
Funcs: []string{"last"},
Name: "last",
Type: "func",
Args: []string{"status"},
},
},
GroupBy: chronograf.GroupBy{
@ -353,8 +356,9 @@ func TestThresholdStringCritGreater(t *testing.T) {
Measurement: "haproxy",
Fields: []chronograf.Field{
{
Field: "status",
Funcs: []string{"last"},
Name: "last",
Type: "func",
Args: []string{"status"},
},
},
GroupBy: chronograf.GroupBy{
@ -489,8 +493,9 @@ func TestThresholdDetail(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Type: "func",
Args: []string{"usage_user"},
},
},
Tags: map[string][]string{
@ -636,8 +641,9 @@ func TestThresholdInsideRange(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Type: "func",
Args: []string{"usage_user"},
},
},
Tags: map[string][]string{
@ -782,8 +788,9 @@ func TestThresholdOutsideRange(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Type: "func",
Args: []string{"usage_user"},
},
},
Tags: map[string][]string{
@ -927,8 +934,9 @@ func TestThresholdNoAggregate(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{},
Name: "usage_user",
Type: "field",
Args: []string{},
},
},
Tags: map[string][]string{
@ -1064,8 +1072,9 @@ func TestRelative(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Type: "func",
Args: []string{"usage_user"},
},
},
Tags: map[string][]string{
@ -1221,8 +1230,9 @@ func TestRelativeChange(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Type: "func",
Args: []string{"usage_user"},
},
},
Tags: map[string][]string{
@ -1375,8 +1385,9 @@ func TestDeadman(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "usage_user",
Funcs: []string{"mean"},
Name: "mean",
Type: "func",
Args: []string{"usage_user"},
},
},
Tags: map[string][]string{

View File

@ -133,7 +133,7 @@ func window(rule chronograf.AlertRule) string {
}
// Period only makes sense if the field has a been grouped via a time duration.
for _, field := range rule.Query.Fields {
if len(field.Funcs) > 0 {
if field.Type == "func" {
return fmt.Sprintf("var period = %s\nvar every = %s", rule.Query.GroupBy.Time, rule.Every)
}
}
@ -153,7 +153,10 @@ func groupBy(q *chronograf.QueryConfig) string {
func field(q *chronograf.QueryConfig) (string, error) {
if q != nil {
for _, field := range q.Fields {
return field.Field, nil
if field.Type == "func" && len(field.Args) > 0 {
return field.Args[0], nil
}
return field.Name, nil
}
}
return "", fmt.Errorf("No fields set in query")

View File

@ -22,7 +22,9 @@ func TestVarsCritStringEqual(t *testing.T) {
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Field: "status",
Name: "status",
Type: "field",
Args: []string{},
},
},
GroupBy: chronograf.GroupBy{

View File

@ -311,8 +311,9 @@ func Test_newDashboardResponse(t *testing.T) {
Measurement: "grays_sports_alamanc",
Fields: []chronograf.Field{
{
Field: "winning_horses",
Funcs: []string{},
Type: "field",
Name: "winning_horses",
Args: []string{},
},
},
GroupBy: chronograf.GroupBy{

View File

@ -382,8 +382,8 @@ func newAlertResponse(task *kapa.Task, srcID, kapaID int) *alertResponse {
}
for _, f := range res.Query.Fields {
if f.Funcs == nil {
f.Funcs = make([]string, 0)
if f.Type == "func" && f.Args == nil {
f.Args = make([]string, 0)
}
}
@ -405,9 +405,8 @@ func ValidRuleRequest(rule chronograf.AlertRule) error {
}
var hasFuncs bool
for _, f := range rule.Query.Fields {
if len(f.Funcs) > 0 {
if f.Type == "func" && len(f.Args) > 0 {
hasFuncs = true
break
}
}
// All kapacitor rules with functions must have a window that is applied

View File

@ -37,8 +37,9 @@ func TestValidRuleRequest(t *testing.T) {
Query: &chronograf.QueryConfig{
Fields: []chronograf.Field{
{
Field: "oldmanpeabody",
Funcs: []string{"max"},
Name: "max",
Type: "func",
Args: []string{"oldmanpeabody"},
},
},
},
@ -52,8 +53,9 @@ func TestValidRuleRequest(t *testing.T) {
Query: &chronograf.QueryConfig{
Fields: []chronograf.Field{
{
Field: "oldmanpeabody",
Funcs: []string{"max"},
Name: "max",
Type: "func",
Args: []string{"oldmanpeabody"},
},
},
},