Merge branch 'master' into bugfix/hosts-page-error-2255
commit
dc75734080
|
@ -8,11 +8,17 @@
|
|||
1. [#2291](https://github.com/influxdata/chronograf/pull/2291): Fix several kapacitor alert creation panics.
|
||||
1. [#2303](https://github.com/influxdata/chronograf/pull/2303): Add shadow-utils to RPM release packages
|
||||
1. [#2292](https://github.com/influxdata/chronograf/pull/2292): Source extra command line options from defaults file
|
||||
1. [#2327](https://github.com/influxdata/chronograf/pull/2327): After CREATE/DELETE queries, refresh list of databases in Data Explorer
|
||||
1. [#2327](https://github.com/influxdata/chronograf/pull/2327): Visualize CREATE/DELETE queries with Table view in Data Explorer
|
||||
1. [#2329](https://github.com/influxdata/chronograf/pull/2329): Include tag values alongside measurement name in Data Explorer result tabs
|
||||
1. [#2386](https://github.com/influxdata/chronograf/pull/2386): Fix queries that include regex, numbers and wildcard
|
||||
1. [#2398](https://github.com/influxdata/chronograf/pull/2398): Fix apps on hosts page from parsing tags with null values
|
||||
1. [#2408](https://github.com/influxdata/chronograf/pull/2408): Fix updated Dashboard names not updating dashboard list
|
||||
|
||||
### Features
|
||||
1. [#2384](https://github.com/influxdata/chronograf/pull/2384): Add filtering by name to Dashboard index page
|
||||
1. [#2385](https://github.com/influxdata/chronograf/pull/2385): Add time shift feature to DataExplorer and Dashboards
|
||||
|
||||
### UI Improvements
|
||||
|
||||
## v1.3.10.0 [2017-10-24]
|
||||
|
|
25
Makefile
25
Makefile
|
@ -23,23 +23,14 @@ ${BINARY}: $(SOURCES) .bindata .jsdep .godep
|
|||
go build -o ${BINARY} ${LDFLAGS} ./cmd/chronograf/main.go
|
||||
|
||||
define CHRONOGIRAFFE
|
||||
.-. .-.
|
||||
| \/ |
|
||||
/, ,_ `'-.
|
||||
.-|\ /`\ '.
|
||||
.' 0/ | 0\ \_ `".
|
||||
.-' _,/ '--'.'|#''---'
|
||||
`--' | / \#
|
||||
| / \#
|
||||
\ ;|\ .\#
|
||||
|' ' // \ ::\#
|
||||
\ /` \ ':\#
|
||||
`"` \.. \#
|
||||
\::. \#
|
||||
\:: \#
|
||||
\' .:\#
|
||||
\ :::\#
|
||||
\ '::\#
|
||||
._ o o
|
||||
\_`-)|_
|
||||
,"" _\_
|
||||
," ## | 0 0.
|
||||
," ## ,-\__ `.
|
||||
," / `--._;) - "HAI, I'm Chronogiraffe. Let's be friends!"
|
||||
," ## /
|
||||
," ## /
|
||||
endef
|
||||
export CHRONOGIRAFFE
|
||||
chronogiraffe: ${BINARY}
|
||||
|
|
|
@ -86,6 +86,7 @@ func (d *DashboardsStore) Add(ctx context.Context, src chronograf.Dashboard) (ch
|
|||
id, _ := b.NextSequence()
|
||||
|
||||
src.ID = chronograf.DashboardID(id)
|
||||
// TODO: use FormatInt
|
||||
strID := strconv.Itoa(int(id))
|
||||
for i, cell := range src.Cells {
|
||||
cid, err := d.IDs.Generate()
|
||||
|
@ -95,12 +96,11 @@ func (d *DashboardsStore) Add(ctx context.Context, src chronograf.Dashboard) (ch
|
|||
cell.ID = cid
|
||||
src.Cells[i] = cell
|
||||
}
|
||||
if v, err := internal.MarshalDashboard(src); err != nil {
|
||||
return err
|
||||
} else if err := b.Put([]byte(strID), v); err != nil {
|
||||
v, err := internal.MarshalDashboard(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return b.Put([]byte(strID), v)
|
||||
}); err != nil {
|
||||
return chronograf.Dashboard{}, err
|
||||
}
|
||||
|
|
|
@ -191,12 +191,26 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) {
|
|||
if q.Range != nil {
|
||||
r.Upper, r.Lower = q.Range.Upper, q.Range.Lower
|
||||
}
|
||||
q.Shifts = q.QueryConfig.Shifts
|
||||
queries[j] = &Query{
|
||||
Command: q.Command,
|
||||
Label: q.Label,
|
||||
Range: r,
|
||||
Source: q.Source,
|
||||
}
|
||||
|
||||
shifts := make([]*TimeShift, len(q.Shifts))
|
||||
for k := range q.Shifts {
|
||||
shift := &TimeShift{
|
||||
Label: q.Shifts[k].Label,
|
||||
Unit: q.Shifts[k].Unit,
|
||||
Quantity: q.Shifts[k].Quantity,
|
||||
}
|
||||
|
||||
shifts[k] = shift
|
||||
}
|
||||
|
||||
queries[j].Shifts = shifts
|
||||
}
|
||||
|
||||
axes := make(map[string]*Axis, len(c.Axes))
|
||||
|
@ -277,12 +291,26 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error {
|
|||
Label: q.Label,
|
||||
Source: q.Source,
|
||||
}
|
||||
|
||||
if q.Range.Upper != q.Range.Lower {
|
||||
queries[j].Range = &chronograf.Range{
|
||||
Upper: q.Range.Upper,
|
||||
Lower: q.Range.Lower,
|
||||
}
|
||||
}
|
||||
|
||||
shifts := make([]chronograf.TimeShift, len(q.Shifts))
|
||||
for k := range q.Shifts {
|
||||
shift := chronograf.TimeShift{
|
||||
Label: q.Shifts[k].Label,
|
||||
Unit: q.Shifts[k].Unit,
|
||||
Quantity: q.Shifts[k].Quantity,
|
||||
}
|
||||
|
||||
shifts[k] = shift
|
||||
}
|
||||
|
||||
queries[j].Shifts = shifts
|
||||
}
|
||||
|
||||
axes := make(map[string]chronograf.Axis, len(c.Axes))
|
||||
|
|
|
@ -20,6 +20,7 @@ It has these top-level messages:
|
|||
Layout
|
||||
Cell
|
||||
Query
|
||||
TimeShift
|
||||
Range
|
||||
AlertRule
|
||||
User
|
||||
|
@ -60,6 +61,83 @@ func (m *Source) String() string { return proto.CompactTextString(m)
|
|||
func (*Source) ProtoMessage() {}
|
||||
func (*Source) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{0} }
|
||||
|
||||
func (m *Source) GetID() int64 {
|
||||
if m != nil {
|
||||
return m.ID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Source) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Source) GetType() string {
|
||||
if m != nil {
|
||||
return m.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Source) GetUsername() string {
|
||||
if m != nil {
|
||||
return m.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Source) GetPassword() string {
|
||||
if m != nil {
|
||||
return m.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Source) GetURL() string {
|
||||
if m != nil {
|
||||
return m.URL
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Source) GetDefault() bool {
|
||||
if m != nil {
|
||||
return m.Default
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Source) GetTelegraf() string {
|
||||
if m != nil {
|
||||
return m.Telegraf
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Source) GetInsecureSkipVerify() bool {
|
||||
if m != nil {
|
||||
return m.InsecureSkipVerify
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Source) GetMetaURL() string {
|
||||
if m != nil {
|
||||
return m.MetaURL
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Source) GetSharedSecret() string {
|
||||
if m != nil {
|
||||
return m.SharedSecret
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Dashboard struct {
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||
|
@ -72,6 +150,20 @@ func (m *Dashboard) String() string { return proto.CompactTextString(
|
|||
func (*Dashboard) ProtoMessage() {}
|
||||
func (*Dashboard) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{1} }
|
||||
|
||||
func (m *Dashboard) GetID() int64 {
|
||||
if m != nil {
|
||||
return m.ID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Dashboard) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Dashboard) GetCells() []*DashboardCell {
|
||||
if m != nil {
|
||||
return m.Cells
|
||||
|
@ -103,6 +195,34 @@ func (m *DashboardCell) String() string { return proto.CompactTextStr
|
|||
func (*DashboardCell) ProtoMessage() {}
|
||||
func (*DashboardCell) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{2} }
|
||||
|
||||
func (m *DashboardCell) GetX() int32 {
|
||||
if m != nil {
|
||||
return m.X
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *DashboardCell) GetY() int32 {
|
||||
if m != nil {
|
||||
return m.Y
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *DashboardCell) GetW() int32 {
|
||||
if m != nil {
|
||||
return m.W
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *DashboardCell) GetH() int32 {
|
||||
if m != nil {
|
||||
return m.H
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *DashboardCell) GetQueries() []*Query {
|
||||
if m != nil {
|
||||
return m.Queries
|
||||
|
@ -110,6 +230,27 @@ func (m *DashboardCell) GetQueries() []*Query {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *DashboardCell) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *DashboardCell) GetType() string {
|
||||
if m != nil {
|
||||
return m.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *DashboardCell) GetID() string {
|
||||
if m != nil {
|
||||
return m.ID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *DashboardCell) GetAxes() map[string]*Axis {
|
||||
if m != nil {
|
||||
return m.Axes
|
||||
|
@ -118,7 +259,7 @@ func (m *DashboardCell) GetAxes() map[string]*Axis {
|
|||
}
|
||||
|
||||
type Axis struct {
|
||||
LegacyBounds []int64 `protobuf:"varint,1,rep,name=legacyBounds" json:"legacyBounds,omitempty"`
|
||||
LegacyBounds []int64 `protobuf:"varint,1,rep,packed,name=legacyBounds" json:"legacyBounds,omitempty"`
|
||||
Bounds []string `protobuf:"bytes,2,rep,name=bounds" json:"bounds,omitempty"`
|
||||
Label string `protobuf:"bytes,3,opt,name=label,proto3" json:"label,omitempty"`
|
||||
Prefix string `protobuf:"bytes,4,opt,name=prefix,proto3" json:"prefix,omitempty"`
|
||||
|
@ -132,6 +273,55 @@ func (m *Axis) String() string { return proto.CompactTextString(m) }
|
|||
func (*Axis) ProtoMessage() {}
|
||||
func (*Axis) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{3} }
|
||||
|
||||
func (m *Axis) GetLegacyBounds() []int64 {
|
||||
if m != nil {
|
||||
return m.LegacyBounds
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Axis) GetBounds() []string {
|
||||
if m != nil {
|
||||
return m.Bounds
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Axis) GetLabel() string {
|
||||
if m != nil {
|
||||
return m.Label
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Axis) GetPrefix() string {
|
||||
if m != nil {
|
||||
return m.Prefix
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Axis) GetSuffix() string {
|
||||
if m != nil {
|
||||
return m.Suffix
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Axis) GetBase() string {
|
||||
if m != nil {
|
||||
return m.Base
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Axis) GetScale() string {
|
||||
if m != nil {
|
||||
return m.Scale
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
TempVar string `protobuf:"bytes,2,opt,name=temp_var,json=tempVar,proto3" json:"temp_var,omitempty"`
|
||||
|
@ -146,6 +336,20 @@ func (m *Template) String() string { return proto.CompactTextString(m
|
|||
func (*Template) ProtoMessage() {}
|
||||
func (*Template) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{4} }
|
||||
|
||||
func (m *Template) GetID() string {
|
||||
if m != nil {
|
||||
return m.ID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Template) GetTempVar() string {
|
||||
if m != nil {
|
||||
return m.TempVar
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Template) GetValues() []*TemplateValue {
|
||||
if m != nil {
|
||||
return m.Values
|
||||
|
@ -153,6 +357,20 @@ func (m *Template) GetValues() []*TemplateValue {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *Template) GetType() string {
|
||||
if m != nil {
|
||||
return m.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Template) GetLabel() string {
|
||||
if m != nil {
|
||||
return m.Label
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Template) GetQuery() *TemplateQuery {
|
||||
if m != nil {
|
||||
return m.Query
|
||||
|
@ -171,6 +389,27 @@ func (m *TemplateValue) String() string { return proto.CompactTextStr
|
|||
func (*TemplateValue) ProtoMessage() {}
|
||||
func (*TemplateValue) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{5} }
|
||||
|
||||
func (m *TemplateValue) GetType() string {
|
||||
if m != nil {
|
||||
return m.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TemplateValue) GetValue() string {
|
||||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TemplateValue) GetSelected() bool {
|
||||
if m != nil {
|
||||
return m.Selected
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type TemplateQuery struct {
|
||||
Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"`
|
||||
Db string `protobuf:"bytes,2,opt,name=db,proto3" json:"db,omitempty"`
|
||||
|
@ -185,6 +424,48 @@ func (m *TemplateQuery) String() string { return proto.CompactTextStr
|
|||
func (*TemplateQuery) ProtoMessage() {}
|
||||
func (*TemplateQuery) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{6} }
|
||||
|
||||
func (m *TemplateQuery) GetCommand() string {
|
||||
if m != nil {
|
||||
return m.Command
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TemplateQuery) GetDb() string {
|
||||
if m != nil {
|
||||
return m.Db
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TemplateQuery) GetRp() string {
|
||||
if m != nil {
|
||||
return m.Rp
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TemplateQuery) GetMeasurement() string {
|
||||
if m != nil {
|
||||
return m.Measurement
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TemplateQuery) GetTagKey() string {
|
||||
if m != nil {
|
||||
return m.TagKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TemplateQuery) GetFieldKey() string {
|
||||
if m != nil {
|
||||
return m.FieldKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||
|
@ -200,6 +481,55 @@ func (m *Server) String() string { return proto.CompactTextString(m)
|
|||
func (*Server) ProtoMessage() {}
|
||||
func (*Server) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{7} }
|
||||
|
||||
func (m *Server) GetID() int64 {
|
||||
if m != nil {
|
||||
return m.ID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Server) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Server) GetUsername() string {
|
||||
if m != nil {
|
||||
return m.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Server) GetPassword() string {
|
||||
if m != nil {
|
||||
return m.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Server) GetURL() string {
|
||||
if m != nil {
|
||||
return m.URL
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Server) GetSrcID() int64 {
|
||||
if m != nil {
|
||||
return m.SrcID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Server) GetActive() bool {
|
||||
if m != nil {
|
||||
return m.Active
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Layout struct {
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Application string `protobuf:"bytes,2,opt,name=Application,proto3" json:"Application,omitempty"`
|
||||
|
@ -213,6 +543,27 @@ func (m *Layout) String() string { return proto.CompactTextString(m)
|
|||
func (*Layout) ProtoMessage() {}
|
||||
func (*Layout) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{8} }
|
||||
|
||||
func (m *Layout) GetID() string {
|
||||
if m != nil {
|
||||
return m.ID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Layout) GetApplication() string {
|
||||
if m != nil {
|
||||
return m.Application
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Layout) GetMeasurement() string {
|
||||
if m != nil {
|
||||
return m.Measurement
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Layout) GetCells() []*Cell {
|
||||
if m != nil {
|
||||
return m.Cells
|
||||
|
@ -220,6 +571,13 @@ func (m *Layout) GetCells() []*Cell {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *Layout) GetAutoflow() bool {
|
||||
if m != nil {
|
||||
return m.Autoflow
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Cell struct {
|
||||
X int32 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"`
|
||||
Y int32 `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"`
|
||||
|
@ -228,7 +586,7 @@ type Cell struct {
|
|||
Queries []*Query `protobuf:"bytes,5,rep,name=queries" json:"queries,omitempty"`
|
||||
I string `protobuf:"bytes,6,opt,name=i,proto3" json:"i,omitempty"`
|
||||
Name string `protobuf:"bytes,7,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Yranges []int64 `protobuf:"varint,8,rep,name=yranges" json:"yranges,omitempty"`
|
||||
Yranges []int64 `protobuf:"varint,8,rep,packed,name=yranges" json:"yranges,omitempty"`
|
||||
Ylabels []string `protobuf:"bytes,9,rep,name=ylabels" json:"ylabels,omitempty"`
|
||||
Type string `protobuf:"bytes,10,opt,name=type,proto3" json:"type,omitempty"`
|
||||
Axes map[string]*Axis `protobuf:"bytes,11,rep,name=axes" json:"axes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
|
||||
|
@ -239,6 +597,34 @@ func (m *Cell) String() string { return proto.CompactTextString(m) }
|
|||
func (*Cell) ProtoMessage() {}
|
||||
func (*Cell) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{9} }
|
||||
|
||||
func (m *Cell) GetX() int32 {
|
||||
if m != nil {
|
||||
return m.X
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Cell) GetY() int32 {
|
||||
if m != nil {
|
||||
return m.Y
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Cell) GetW() int32 {
|
||||
if m != nil {
|
||||
return m.W
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Cell) GetH() int32 {
|
||||
if m != nil {
|
||||
return m.H
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Cell) GetQueries() []*Query {
|
||||
if m != nil {
|
||||
return m.Queries
|
||||
|
@ -246,6 +632,41 @@ func (m *Cell) GetQueries() []*Query {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *Cell) GetI() string {
|
||||
if m != nil {
|
||||
return m.I
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Cell) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Cell) GetYranges() []int64 {
|
||||
if m != nil {
|
||||
return m.Yranges
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Cell) GetYlabels() []string {
|
||||
if m != nil {
|
||||
return m.Ylabels
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Cell) GetType() string {
|
||||
if m != nil {
|
||||
return m.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Cell) GetAxes() map[string]*Axis {
|
||||
if m != nil {
|
||||
return m.Axes
|
||||
|
@ -254,14 +675,15 @@ func (m *Cell) GetAxes() map[string]*Axis {
|
|||
}
|
||||
|
||||
type Query struct {
|
||||
Command string `protobuf:"bytes,1,opt,name=Command,proto3" json:"Command,omitempty"`
|
||||
DB string `protobuf:"bytes,2,opt,name=DB,proto3" json:"DB,omitempty"`
|
||||
RP string `protobuf:"bytes,3,opt,name=RP,proto3" json:"RP,omitempty"`
|
||||
GroupBys []string `protobuf:"bytes,4,rep,name=GroupBys" json:"GroupBys,omitempty"`
|
||||
Wheres []string `protobuf:"bytes,5,rep,name=Wheres" json:"Wheres,omitempty"`
|
||||
Label string `protobuf:"bytes,6,opt,name=Label,proto3" json:"Label,omitempty"`
|
||||
Range *Range `protobuf:"bytes,7,opt,name=Range" json:"Range,omitempty"`
|
||||
Source string `protobuf:"bytes,8,opt,name=Source,proto3" json:"Source,omitempty"`
|
||||
Command string `protobuf:"bytes,1,opt,name=Command,proto3" json:"Command,omitempty"`
|
||||
DB string `protobuf:"bytes,2,opt,name=DB,proto3" json:"DB,omitempty"`
|
||||
RP string `protobuf:"bytes,3,opt,name=RP,proto3" json:"RP,omitempty"`
|
||||
GroupBys []string `protobuf:"bytes,4,rep,name=GroupBys" json:"GroupBys,omitempty"`
|
||||
Wheres []string `protobuf:"bytes,5,rep,name=Wheres" json:"Wheres,omitempty"`
|
||||
Label string `protobuf:"bytes,6,opt,name=Label,proto3" json:"Label,omitempty"`
|
||||
Range *Range `protobuf:"bytes,7,opt,name=Range" json:"Range,omitempty"`
|
||||
Source string `protobuf:"bytes,8,opt,name=Source,proto3" json:"Source,omitempty"`
|
||||
Shifts []*TimeShift `protobuf:"bytes,9,rep,name=Shifts" json:"Shifts,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Query) Reset() { *m = Query{} }
|
||||
|
@ -269,6 +691,48 @@ func (m *Query) String() string { return proto.CompactTextString(m) }
|
|||
func (*Query) ProtoMessage() {}
|
||||
func (*Query) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{10} }
|
||||
|
||||
func (m *Query) GetCommand() string {
|
||||
if m != nil {
|
||||
return m.Command
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Query) GetDB() string {
|
||||
if m != nil {
|
||||
return m.DB
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Query) GetRP() string {
|
||||
if m != nil {
|
||||
return m.RP
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Query) GetGroupBys() []string {
|
||||
if m != nil {
|
||||
return m.GroupBys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Query) GetWheres() []string {
|
||||
if m != nil {
|
||||
return m.Wheres
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Query) GetLabel() string {
|
||||
if m != nil {
|
||||
return m.Label
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Query) GetRange() *Range {
|
||||
if m != nil {
|
||||
return m.Range
|
||||
|
@ -276,6 +740,52 @@ func (m *Query) GetRange() *Range {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *Query) GetSource() string {
|
||||
if m != nil {
|
||||
return m.Source
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Query) GetShifts() []*TimeShift {
|
||||
if m != nil {
|
||||
return m.Shifts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TimeShift struct {
|
||||
Label string `protobuf:"bytes,1,opt,name=Label,proto3" json:"Label,omitempty"`
|
||||
Unit string `protobuf:"bytes,2,opt,name=Unit,proto3" json:"Unit,omitempty"`
|
||||
Quantity string `protobuf:"bytes,3,opt,name=Quantity,proto3" json:"Quantity,omitempty"`
|
||||
}
|
||||
|
||||
func (m *TimeShift) Reset() { *m = TimeShift{} }
|
||||
func (m *TimeShift) String() string { return proto.CompactTextString(m) }
|
||||
func (*TimeShift) ProtoMessage() {}
|
||||
func (*TimeShift) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{11} }
|
||||
|
||||
func (m *TimeShift) GetLabel() string {
|
||||
if m != nil {
|
||||
return m.Label
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TimeShift) GetUnit() string {
|
||||
if m != nil {
|
||||
return m.Unit
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TimeShift) GetQuantity() string {
|
||||
if m != nil {
|
||||
return m.Quantity
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Range struct {
|
||||
Upper int64 `protobuf:"varint,1,opt,name=Upper,proto3" json:"Upper,omitempty"`
|
||||
Lower int64 `protobuf:"varint,2,opt,name=Lower,proto3" json:"Lower,omitempty"`
|
||||
|
@ -284,7 +794,21 @@ type Range struct {
|
|||
func (m *Range) Reset() { *m = Range{} }
|
||||
func (m *Range) String() string { return proto.CompactTextString(m) }
|
||||
func (*Range) ProtoMessage() {}
|
||||
func (*Range) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{11} }
|
||||
func (*Range) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{12} }
|
||||
|
||||
func (m *Range) GetUpper() int64 {
|
||||
if m != nil {
|
||||
return m.Upper
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Range) GetLower() int64 {
|
||||
if m != nil {
|
||||
return m.Lower
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type AlertRule struct {
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
|
@ -296,7 +820,35 @@ type AlertRule struct {
|
|||
func (m *AlertRule) Reset() { *m = AlertRule{} }
|
||||
func (m *AlertRule) String() string { return proto.CompactTextString(m) }
|
||||
func (*AlertRule) ProtoMessage() {}
|
||||
func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{12} }
|
||||
func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{13} }
|
||||
|
||||
func (m *AlertRule) GetID() string {
|
||||
if m != nil {
|
||||
return m.ID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *AlertRule) GetJSON() string {
|
||||
if m != nil {
|
||||
return m.JSON
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *AlertRule) GetSrcID() int64 {
|
||||
if m != nil {
|
||||
return m.SrcID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *AlertRule) GetKapaID() int64 {
|
||||
if m != nil {
|
||||
return m.KapaID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
|
@ -306,7 +858,21 @@ type User struct {
|
|||
func (m *User) Reset() { *m = User{} }
|
||||
func (m *User) String() string { return proto.CompactTextString(m) }
|
||||
func (*User) ProtoMessage() {}
|
||||
func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{13} }
|
||||
func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{14} }
|
||||
|
||||
func (m *User) GetID() uint64 {
|
||||
if m != nil {
|
||||
return m.ID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *User) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Source)(nil), "internal.Source")
|
||||
|
@ -320,6 +886,7 @@ func init() {
|
|||
proto.RegisterType((*Layout)(nil), "internal.Layout")
|
||||
proto.RegisterType((*Cell)(nil), "internal.Cell")
|
||||
proto.RegisterType((*Query)(nil), "internal.Query")
|
||||
proto.RegisterType((*TimeShift)(nil), "internal.TimeShift")
|
||||
proto.RegisterType((*Range)(nil), "internal.Range")
|
||||
proto.RegisterType((*AlertRule)(nil), "internal.AlertRule")
|
||||
proto.RegisterType((*User)(nil), "internal.User")
|
||||
|
@ -328,70 +895,73 @@ func init() {
|
|||
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
|
||||
|
||||
var fileDescriptorInternal = []byte{
|
||||
// 1028 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0x4f, 0x6f, 0xe3, 0x44,
|
||||
0x14, 0xd7, 0xf8, 0x4f, 0x12, 0xbf, 0x74, 0x0b, 0x1a, 0xad, 0x58, 0xb3, 0x5c, 0x82, 0x05, 0x52,
|
||||
0x40, 0x6c, 0x41, 0xbb, 0x42, 0x42, 0xdc, 0xd2, 0x06, 0xad, 0x4a, 0xbb, 0x4b, 0x99, 0xb4, 0xe5,
|
||||
0x84, 0x56, 0x13, 0xe7, 0xa5, 0xb5, 0xd6, 0x89, 0xcd, 0xd8, 0x6e, 0xe3, 0x6f, 0xc1, 0x27, 0x40,
|
||||
0x42, 0xe2, 0xc4, 0x81, 0x03, 0x5f, 0x80, 0xfb, 0x7e, 0x2a, 0xf4, 0x66, 0xc6, 0x8e, 0xc3, 0x76,
|
||||
0xd1, 0x5e, 0xe0, 0x36, 0xbf, 0xf7, 0xc6, 0x6f, 0x66, 0xde, 0xef, 0xfd, 0x7e, 0x09, 0xec, 0x27,
|
||||
0xeb, 0x12, 0xd5, 0x5a, 0xa6, 0x07, 0xb9, 0xca, 0xca, 0x8c, 0x0f, 0x1a, 0x1c, 0xfd, 0xe1, 0x40,
|
||||
0x6f, 0x96, 0x55, 0x2a, 0x46, 0xbe, 0x0f, 0xce, 0xf1, 0x34, 0x64, 0x23, 0x36, 0x76, 0x85, 0x73,
|
||||
0x3c, 0xe5, 0x1c, 0xbc, 0xe7, 0x72, 0x85, 0xa1, 0x33, 0x62, 0xe3, 0x40, 0xe8, 0x35, 0xc5, 0xce,
|
||||
0xeb, 0x1c, 0x43, 0xd7, 0xc4, 0x68, 0xcd, 0x1f, 0xc2, 0xe0, 0xa2, 0xa0, 0x6a, 0x2b, 0x0c, 0x3d,
|
||||
0x1d, 0x6f, 0x31, 0xe5, 0xce, 0x64, 0x51, 0xdc, 0x66, 0x6a, 0x11, 0xfa, 0x26, 0xd7, 0x60, 0xfe,
|
||||
0x2e, 0xb8, 0x17, 0xe2, 0x34, 0xec, 0xe9, 0x30, 0x2d, 0x79, 0x08, 0xfd, 0x29, 0x2e, 0x65, 0x95,
|
||||
0x96, 0x61, 0x7f, 0xc4, 0xc6, 0x03, 0xd1, 0x40, 0xaa, 0x73, 0x8e, 0x29, 0x5e, 0x29, 0xb9, 0x0c,
|
||||
0x07, 0xa6, 0x4e, 0x83, 0xf9, 0x01, 0xf0, 0xe3, 0x75, 0x81, 0x71, 0xa5, 0x70, 0xf6, 0x32, 0xc9,
|
||||
0x2f, 0x51, 0x25, 0xcb, 0x3a, 0x0c, 0x74, 0x81, 0x3b, 0x32, 0x74, 0xca, 0x33, 0x2c, 0x25, 0x9d,
|
||||
0x0d, 0xba, 0x54, 0x03, 0x79, 0x04, 0x7b, 0xb3, 0x6b, 0xa9, 0x70, 0x31, 0xc3, 0x58, 0x61, 0x19,
|
||||
0x0e, 0x75, 0x7a, 0x27, 0x16, 0xfd, 0xcc, 0x20, 0x98, 0xca, 0xe2, 0x7a, 0x9e, 0x49, 0xb5, 0x78,
|
||||
0xab, 0x9e, 0x3d, 0x02, 0x3f, 0xc6, 0x34, 0x2d, 0x42, 0x77, 0xe4, 0x8e, 0x87, 0x8f, 0x1f, 0x1c,
|
||||
0xb4, 0x64, 0xb4, 0x75, 0x8e, 0x30, 0x4d, 0x85, 0xd9, 0xc5, 0xbf, 0x80, 0xa0, 0xc4, 0x55, 0x9e,
|
||||
0xca, 0x12, 0x8b, 0xd0, 0xd3, 0x9f, 0xf0, 0xed, 0x27, 0xe7, 0x36, 0x25, 0xb6, 0x9b, 0xa2, 0xdf,
|
||||
0x1d, 0xb8, 0xb7, 0x53, 0x8a, 0xef, 0x01, 0xdb, 0xe8, 0x5b, 0xf9, 0x82, 0x6d, 0x08, 0xd5, 0xfa,
|
||||
0x46, 0xbe, 0x60, 0x35, 0xa1, 0x5b, 0xcd, 0x9f, 0x2f, 0xd8, 0x2d, 0xa1, 0x6b, 0xcd, 0x9a, 0x2f,
|
||||
0xd8, 0x35, 0xff, 0x04, 0xfa, 0x3f, 0x55, 0xa8, 0x12, 0x2c, 0x42, 0x5f, 0x9f, 0xfc, 0xce, 0xf6,
|
||||
0xe4, 0xef, 0x2b, 0x54, 0xb5, 0x68, 0xf2, 0xf4, 0x52, 0xcd, 0xb8, 0xa1, 0x4f, 0xaf, 0x29, 0x56,
|
||||
0xd2, 0x74, 0xf4, 0x4d, 0x8c, 0xd6, 0xb6, 0x43, 0x86, 0x33, 0xea, 0xd0, 0x97, 0xe0, 0xc9, 0x0d,
|
||||
0x16, 0x61, 0xa0, 0xeb, 0x7f, 0xf8, 0x86, 0x66, 0x1c, 0x4c, 0x36, 0x58, 0x7c, 0xb3, 0x2e, 0x55,
|
||||
0x2d, 0xf4, 0xf6, 0x87, 0x4f, 0x21, 0x68, 0x43, 0x34, 0x39, 0x2f, 0xb1, 0xd6, 0x0f, 0x0c, 0x04,
|
||||
0x2d, 0xf9, 0x47, 0xe0, 0xdf, 0xc8, 0xb4, 0x32, 0x8d, 0x1f, 0x3e, 0xde, 0xdf, 0x96, 0x9d, 0x6c,
|
||||
0x92, 0x42, 0x98, 0xe4, 0xd7, 0xce, 0x57, 0x2c, 0xfa, 0x93, 0x81, 0x47, 0x31, 0x22, 0x3b, 0xc5,
|
||||
0x2b, 0x19, 0xd7, 0x87, 0x59, 0xb5, 0x5e, 0x14, 0x21, 0x1b, 0xb9, 0x63, 0x57, 0xec, 0xc4, 0xf8,
|
||||
0x7b, 0xd0, 0x9b, 0x9b, 0xac, 0x33, 0x72, 0xc7, 0x81, 0xb0, 0x88, 0xdf, 0x07, 0x3f, 0x95, 0x73,
|
||||
0x4c, 0xad, 0x0e, 0x0c, 0xa0, 0xdd, 0xb9, 0xc2, 0x65, 0xb2, 0xb1, 0x32, 0xb0, 0x88, 0xe2, 0x45,
|
||||
0xb5, 0xa4, 0xb8, 0x91, 0x80, 0x45, 0xd4, 0xae, 0xb9, 0x2c, 0xda, 0x16, 0xd2, 0x9a, 0x2a, 0x17,
|
||||
0xb1, 0x4c, 0x9b, 0x1e, 0x1a, 0x10, 0xfd, 0xc5, 0x68, 0xfe, 0x0d, 0xdf, 0x9d, 0x99, 0x33, 0x1d,
|
||||
0x7d, 0x1f, 0x06, 0x34, 0x0b, 0x2f, 0x6e, 0xa4, 0xb2, 0x73, 0xd7, 0x27, 0x7c, 0x29, 0x15, 0xff,
|
||||
0x1c, 0x7a, 0xfa, 0xe5, 0x77, 0xcc, 0x5e, 0x53, 0xee, 0x92, 0xf2, 0xc2, 0x6e, 0x6b, 0x19, 0xf4,
|
||||
0x3a, 0x0c, 0xb6, 0x8f, 0xf5, 0xbb, 0x8f, 0x7d, 0x04, 0x3e, 0x8d, 0x42, 0xad, 0x6f, 0x7f, 0x67,
|
||||
0x65, 0x33, 0x30, 0x66, 0x57, 0x74, 0x01, 0xf7, 0x76, 0x4e, 0x6c, 0x4f, 0x62, 0xbb, 0x27, 0x6d,
|
||||
0x59, 0x0c, 0x2c, 0x6b, 0xa4, 0xfd, 0x02, 0x53, 0x8c, 0x4b, 0x5c, 0xe8, 0x7e, 0x0f, 0x44, 0x8b,
|
||||
0xa3, 0x5f, 0xd9, 0xb6, 0xae, 0x3e, 0x8f, 0xd4, 0x1d, 0x67, 0xab, 0x95, 0x5c, 0x2f, 0x6c, 0xe9,
|
||||
0x06, 0x52, 0xdf, 0x16, 0x73, 0x5b, 0xda, 0x59, 0xcc, 0x09, 0xab, 0xdc, 0x32, 0xe8, 0xa8, 0x9c,
|
||||
0x8f, 0x60, 0xb8, 0x42, 0x59, 0x54, 0x0a, 0x57, 0xb8, 0x2e, 0x6d, 0x0b, 0xba, 0x21, 0xfe, 0x00,
|
||||
0xfa, 0xa5, 0xbc, 0x7a, 0x41, 0xb3, 0x67, 0x99, 0x2c, 0xe5, 0xd5, 0x09, 0xd6, 0xfc, 0x03, 0x08,
|
||||
0x96, 0x09, 0xa6, 0x0b, 0x9d, 0x32, 0x74, 0x0e, 0x74, 0xe0, 0x04, 0xeb, 0xe8, 0x37, 0x06, 0xbd,
|
||||
0x19, 0xaa, 0x1b, 0x54, 0x6f, 0x65, 0x17, 0x5d, 0x3b, 0x75, 0xff, 0xc5, 0x4e, 0xbd, 0xbb, 0xed,
|
||||
0xd4, 0xdf, 0xda, 0xe9, 0x7d, 0xf0, 0x67, 0x2a, 0x3e, 0x9e, 0xea, 0x1b, 0xb9, 0xc2, 0x00, 0x9a,
|
||||
0xc6, 0x49, 0x5c, 0x26, 0x37, 0x68, 0x3d, 0xd6, 0xa2, 0xe8, 0x17, 0x06, 0xbd, 0x53, 0x59, 0x67,
|
||||
0x55, 0xf9, 0xda, 0x84, 0x8d, 0x60, 0x38, 0xc9, 0xf3, 0x34, 0x89, 0x65, 0x99, 0x64, 0x6b, 0x7b,
|
||||
0xdb, 0x6e, 0x88, 0x76, 0x3c, 0xeb, 0xf4, 0xce, 0xdc, 0xbb, 0x1b, 0x22, 0x85, 0x1e, 0x69, 0x17,
|
||||
0x34, 0x96, 0xd6, 0x51, 0xa8, 0x31, 0x3f, 0x9d, 0xa4, 0x07, 0x4e, 0xaa, 0x32, 0x5b, 0xa6, 0xd9,
|
||||
0xad, 0x7e, 0xc9, 0x40, 0xb4, 0x38, 0x7a, 0xe5, 0x80, 0xf7, 0x7f, 0xb9, 0xdb, 0x1e, 0xb0, 0xc4,
|
||||
0x12, 0xc9, 0x92, 0xd6, 0xeb, 0xfa, 0x1d, 0xaf, 0x0b, 0xa1, 0x5f, 0x2b, 0xb9, 0xbe, 0xc2, 0x22,
|
||||
0x1c, 0x68, 0xe7, 0x68, 0xa0, 0xce, 0x68, 0x8d, 0x18, 0x93, 0x0b, 0x44, 0x03, 0xdb, 0x99, 0x87,
|
||||
0xce, 0xcc, 0x7f, 0x66, 0xfd, 0x70, 0xa8, 0x6f, 0x14, 0xee, 0xb6, 0xe5, 0xbf, 0xb3, 0xc1, 0x57,
|
||||
0x0c, 0xfc, 0x56, 0x30, 0x47, 0xbb, 0x82, 0x39, 0xda, 0x0a, 0x66, 0x7a, 0xd8, 0x08, 0x66, 0x7a,
|
||||
0x48, 0x58, 0x9c, 0x35, 0x82, 0x11, 0x67, 0x44, 0xd6, 0x53, 0x95, 0x55, 0xf9, 0x61, 0x6d, 0x58,
|
||||
0x0d, 0x44, 0x8b, 0x69, 0xca, 0x7e, 0xb8, 0x46, 0x65, 0x5b, 0x1d, 0x08, 0x8b, 0x68, 0x26, 0x4f,
|
||||
0xb5, 0x99, 0x98, 0xe6, 0x1a, 0xc0, 0x3f, 0x06, 0x5f, 0x50, 0xf3, 0x74, 0x87, 0x77, 0x78, 0xd1,
|
||||
0x61, 0x61, 0xb2, 0x54, 0xd4, 0xfc, 0x57, 0xb1, 0xbf, 0x27, 0x16, 0x45, 0x4f, 0xec, 0xe7, 0x54,
|
||||
0xfd, 0x22, 0xcf, 0x51, 0x59, 0x89, 0x19, 0xa0, 0xcf, 0xcc, 0x6e, 0xd1, 0xb8, 0xa3, 0x2b, 0x0c,
|
||||
0x88, 0x7e, 0x84, 0x60, 0x92, 0xa2, 0x2a, 0x45, 0x95, 0xbe, 0xee, 0xa9, 0x1c, 0xbc, 0x6f, 0x67,
|
||||
0xdf, 0x3d, 0x6f, 0x84, 0x49, 0xeb, 0xad, 0x9c, 0xdc, 0x7f, 0xc8, 0xe9, 0x44, 0xe6, 0xf2, 0x78,
|
||||
0xaa, 0xe7, 0xcc, 0x15, 0x16, 0x45, 0x9f, 0x82, 0x47, 0xb2, 0xed, 0x54, 0xf6, 0xde, 0x24, 0xf9,
|
||||
0x79, 0x4f, 0xff, 0x2b, 0x7b, 0xf2, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb7, 0x59, 0x2e, 0xc0,
|
||||
0xa7, 0x09, 0x00, 0x00,
|
||||
// 1082 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xcf, 0x8e, 0xe3, 0xc4,
|
||||
0x13, 0x96, 0x63, 0x3b, 0x89, 0x2b, 0xb3, 0xf3, 0xfb, 0xa9, 0x59, 0xb1, 0x66, 0xb9, 0x04, 0x0b,
|
||||
0xa4, 0xf0, 0x67, 0x07, 0xb4, 0x2b, 0x24, 0xc4, 0x2d, 0x33, 0x41, 0xab, 0x61, 0x66, 0x97, 0x99,
|
||||
0xce, 0xcc, 0x70, 0x42, 0xab, 0x4e, 0x52, 0x99, 0x58, 0xeb, 0xd8, 0xa6, 0xdd, 0x9e, 0x89, 0xdf,
|
||||
0x82, 0x27, 0x40, 0x42, 0xe2, 0xc4, 0x81, 0x03, 0x2f, 0xc0, 0x9d, 0x17, 0xe2, 0x8a, 0xaa, 0xbb,
|
||||
0xed, 0x38, 0xec, 0x2c, 0xda, 0x0b, 0xdc, 0xfa, 0xab, 0xea, 0x54, 0x55, 0xd7, 0x57, 0xf5, 0xc5,
|
||||
0xb0, 0x1f, 0xa7, 0x0a, 0x65, 0x2a, 0x92, 0x83, 0x5c, 0x66, 0x2a, 0x63, 0xfd, 0x1a, 0x47, 0xbf,
|
||||
0x76, 0xa0, 0x3b, 0xcd, 0x4a, 0x39, 0x47, 0xb6, 0x0f, 0x9d, 0xe3, 0x49, 0xe8, 0x0c, 0x9d, 0x91,
|
||||
0xcb, 0x3b, 0xc7, 0x13, 0xc6, 0xc0, 0x7b, 0x2e, 0xd6, 0x18, 0x76, 0x86, 0xce, 0x28, 0xe0, 0xfa,
|
||||
0x4c, 0xb6, 0x8b, 0x2a, 0xc7, 0xd0, 0x35, 0x36, 0x3a, 0xb3, 0x87, 0xd0, 0xbf, 0x2c, 0x28, 0xda,
|
||||
0x1a, 0x43, 0x4f, 0xdb, 0x1b, 0x4c, 0xbe, 0x33, 0x51, 0x14, 0xb7, 0x99, 0x5c, 0x84, 0xbe, 0xf1,
|
||||
0xd5, 0x98, 0xfd, 0x1f, 0xdc, 0x4b, 0x7e, 0x1a, 0x76, 0xb5, 0x99, 0x8e, 0x2c, 0x84, 0xde, 0x04,
|
||||
0x97, 0xa2, 0x4c, 0x54, 0xd8, 0x1b, 0x3a, 0xa3, 0x3e, 0xaf, 0x21, 0xc5, 0xb9, 0xc0, 0x04, 0xaf,
|
||||
0xa5, 0x58, 0x86, 0x7d, 0x13, 0xa7, 0xc6, 0xec, 0x00, 0xd8, 0x71, 0x5a, 0xe0, 0xbc, 0x94, 0x38,
|
||||
0x7d, 0x19, 0xe7, 0x57, 0x28, 0xe3, 0x65, 0x15, 0x06, 0x3a, 0xc0, 0x1d, 0x1e, 0xca, 0xf2, 0x0c,
|
||||
0x95, 0xa0, 0xdc, 0xa0, 0x43, 0xd5, 0x90, 0x45, 0xb0, 0x37, 0x5d, 0x09, 0x89, 0x8b, 0x29, 0xce,
|
||||
0x25, 0xaa, 0x70, 0xa0, 0xdd, 0x3b, 0xb6, 0xe8, 0x07, 0x07, 0x82, 0x89, 0x28, 0x56, 0xb3, 0x4c,
|
||||
0xc8, 0xc5, 0x1b, 0xf5, 0xec, 0x11, 0xf8, 0x73, 0x4c, 0x92, 0x22, 0x74, 0x87, 0xee, 0x68, 0xf0,
|
||||
0xf8, 0xc1, 0x41, 0x43, 0x46, 0x13, 0xe7, 0x08, 0x93, 0x84, 0x9b, 0x5b, 0xec, 0x33, 0x08, 0x14,
|
||||
0xae, 0xf3, 0x44, 0x28, 0x2c, 0x42, 0x4f, 0xff, 0x84, 0x6d, 0x7f, 0x72, 0x61, 0x5d, 0x7c, 0x7b,
|
||||
0x29, 0xfa, 0xa5, 0x03, 0xf7, 0x76, 0x42, 0xb1, 0x3d, 0x70, 0x36, 0xba, 0x2a, 0x9f, 0x3b, 0x1b,
|
||||
0x42, 0x95, 0xae, 0xc8, 0xe7, 0x4e, 0x45, 0xe8, 0x56, 0xf3, 0xe7, 0x73, 0xe7, 0x96, 0xd0, 0x4a,
|
||||
0xb3, 0xe6, 0x73, 0x67, 0xc5, 0x3e, 0x84, 0xde, 0xf7, 0x25, 0xca, 0x18, 0x8b, 0xd0, 0xd7, 0x99,
|
||||
0xff, 0xb7, 0xcd, 0x7c, 0x5e, 0xa2, 0xac, 0x78, 0xed, 0xa7, 0x97, 0x6a, 0xc6, 0x0d, 0x7d, 0xfa,
|
||||
0x4c, 0x36, 0x45, 0xd3, 0xd1, 0x33, 0x36, 0x3a, 0xdb, 0x0e, 0x19, 0xce, 0xa8, 0x43, 0x9f, 0x83,
|
||||
0x27, 0x36, 0x58, 0x84, 0x81, 0x8e, 0xff, 0xde, 0x6b, 0x9a, 0x71, 0x30, 0xde, 0x60, 0xf1, 0x55,
|
||||
0xaa, 0x64, 0xc5, 0xf5, 0xf5, 0x87, 0x4f, 0x21, 0x68, 0x4c, 0x34, 0x39, 0x2f, 0xb1, 0xd2, 0x0f,
|
||||
0x0c, 0x38, 0x1d, 0xd9, 0xfb, 0xe0, 0xdf, 0x88, 0xa4, 0x34, 0x8d, 0x1f, 0x3c, 0xde, 0xdf, 0x86,
|
||||
0x1d, 0x6f, 0xe2, 0x82, 0x1b, 0xe7, 0x97, 0x9d, 0x2f, 0x9c, 0xe8, 0x37, 0x07, 0x3c, 0xb2, 0x11,
|
||||
0xd9, 0x09, 0x5e, 0x8b, 0x79, 0x75, 0x98, 0x95, 0xe9, 0xa2, 0x08, 0x9d, 0xa1, 0x3b, 0x72, 0xf9,
|
||||
0x8e, 0x8d, 0xbd, 0x0d, 0xdd, 0x99, 0xf1, 0x76, 0x86, 0xee, 0x28, 0xe0, 0x16, 0xb1, 0xfb, 0xe0,
|
||||
0x27, 0x62, 0x86, 0x89, 0xdd, 0x03, 0x03, 0xe8, 0x76, 0x2e, 0x71, 0x19, 0x6f, 0xec, 0x1a, 0x58,
|
||||
0x44, 0xf6, 0xa2, 0x5c, 0x92, 0xdd, 0xac, 0x80, 0x45, 0xd4, 0xae, 0x99, 0x28, 0x9a, 0x16, 0xd2,
|
||||
0x99, 0x22, 0x17, 0x73, 0x91, 0xd4, 0x3d, 0x34, 0x20, 0xfa, 0xdd, 0xa1, 0xf9, 0x37, 0x7c, 0xb7,
|
||||
0x66, 0xce, 0x74, 0xf4, 0x1d, 0xe8, 0xd3, 0x2c, 0xbc, 0xb8, 0x11, 0xd2, 0xce, 0x5d, 0x8f, 0xf0,
|
||||
0x95, 0x90, 0xec, 0x53, 0xe8, 0xea, 0x97, 0xdf, 0x31, 0x7b, 0x75, 0xb8, 0x2b, 0xf2, 0x73, 0x7b,
|
||||
0xad, 0x61, 0xd0, 0x6b, 0x31, 0xd8, 0x3c, 0xd6, 0x6f, 0x3f, 0xf6, 0x11, 0xf8, 0x34, 0x0a, 0x95,
|
||||
0xae, 0xfe, 0xce, 0xc8, 0x66, 0x60, 0xcc, 0xad, 0xe8, 0x12, 0xee, 0xed, 0x64, 0x6c, 0x32, 0x39,
|
||||
0xbb, 0x99, 0xb6, 0x2c, 0x06, 0x96, 0x35, 0xda, 0xfd, 0x02, 0x13, 0x9c, 0x2b, 0x5c, 0xe8, 0x7e,
|
||||
0xf7, 0x79, 0x83, 0xa3, 0x9f, 0x9c, 0x6d, 0x5c, 0x9d, 0x8f, 0xb6, 0x7b, 0x9e, 0xad, 0xd7, 0x22,
|
||||
0x5d, 0xd8, 0xd0, 0x35, 0xa4, 0xbe, 0x2d, 0x66, 0x36, 0x74, 0x67, 0x31, 0x23, 0x2c, 0x73, 0xcb,
|
||||
0x60, 0x47, 0xe6, 0x6c, 0x08, 0x83, 0x35, 0x8a, 0xa2, 0x94, 0xb8, 0xc6, 0x54, 0xd9, 0x16, 0xb4,
|
||||
0x4d, 0xec, 0x01, 0xf4, 0x94, 0xb8, 0x7e, 0x41, 0xb3, 0x67, 0x99, 0x54, 0xe2, 0xfa, 0x04, 0x2b,
|
||||
0xf6, 0x2e, 0x04, 0xcb, 0x18, 0x93, 0x85, 0x76, 0x19, 0x3a, 0xfb, 0xda, 0x70, 0x82, 0x55, 0xf4,
|
||||
0xb3, 0x03, 0xdd, 0x29, 0xca, 0x1b, 0x94, 0x6f, 0x24, 0x17, 0x6d, 0x39, 0x75, 0xff, 0x41, 0x4e,
|
||||
0xbd, 0xbb, 0xe5, 0xd4, 0xdf, 0xca, 0xe9, 0x7d, 0xf0, 0xa7, 0x72, 0x7e, 0x3c, 0xd1, 0x15, 0xb9,
|
||||
0xdc, 0x00, 0x9a, 0xc6, 0xf1, 0x5c, 0xc5, 0x37, 0x68, 0x35, 0xd6, 0xa2, 0xe8, 0x47, 0x07, 0xba,
|
||||
0xa7, 0xa2, 0xca, 0x4a, 0xf5, 0xca, 0x84, 0x0d, 0x61, 0x30, 0xce, 0xf3, 0x24, 0x9e, 0x0b, 0x15,
|
||||
0x67, 0xa9, 0xad, 0xb6, 0x6d, 0xa2, 0x1b, 0xcf, 0x5a, 0xbd, 0x33, 0x75, 0xb7, 0x4d, 0xb4, 0xa1,
|
||||
0x47, 0x5a, 0x05, 0x8d, 0xa4, 0xb5, 0x36, 0xd4, 0x88, 0x9f, 0x76, 0xd2, 0x03, 0xc7, 0xa5, 0xca,
|
||||
0x96, 0x49, 0x76, 0xab, 0x5f, 0xd2, 0xe7, 0x0d, 0x8e, 0xfe, 0xe8, 0x80, 0xf7, 0x5f, 0xa9, 0xdb,
|
||||
0x1e, 0x38, 0xb1, 0x25, 0xd2, 0x89, 0x1b, 0xad, 0xeb, 0xb5, 0xb4, 0x2e, 0x84, 0x5e, 0x25, 0x45,
|
||||
0x7a, 0x8d, 0x45, 0xd8, 0xd7, 0xca, 0x51, 0x43, 0xed, 0xd1, 0x3b, 0x62, 0x44, 0x2e, 0xe0, 0x35,
|
||||
0x6c, 0x66, 0x1e, 0x5a, 0x33, 0xff, 0x89, 0xd5, 0xc3, 0x81, 0xae, 0x28, 0xdc, 0x6d, 0xcb, 0xbf,
|
||||
0x27, 0x83, 0x7f, 0x3a, 0xe0, 0x37, 0x0b, 0x73, 0xb4, 0xbb, 0x30, 0x47, 0xdb, 0x85, 0x99, 0x1c,
|
||||
0xd6, 0x0b, 0x33, 0x39, 0x24, 0xcc, 0xcf, 0xea, 0x85, 0xe1, 0x67, 0x44, 0xd6, 0x53, 0x99, 0x95,
|
||||
0xf9, 0x61, 0x65, 0x58, 0x0d, 0x78, 0x83, 0x69, 0xca, 0xbe, 0x5d, 0xa1, 0xb4, 0xad, 0x0e, 0xb8,
|
||||
0x45, 0x34, 0x93, 0xa7, 0x5a, 0x4c, 0x4c, 0x73, 0x0d, 0x60, 0x1f, 0x80, 0xcf, 0xa9, 0x79, 0xba,
|
||||
0xc3, 0x3b, 0xbc, 0x68, 0x33, 0x37, 0x5e, 0x0a, 0x6a, 0xbe, 0x55, 0xec, 0xff, 0x49, 0xfd, 0xe5,
|
||||
0xf2, 0x31, 0x74, 0xa7, 0xab, 0x78, 0xa9, 0xea, 0x7f, 0x95, 0xb7, 0x5a, 0x62, 0x14, 0xaf, 0x51,
|
||||
0xfb, 0xb8, 0xbd, 0x12, 0x9d, 0x43, 0xd0, 0x18, 0xb7, 0xe5, 0x38, 0xed, 0x72, 0x18, 0x78, 0x97,
|
||||
0x69, 0xac, 0xea, 0xb5, 0xa4, 0x33, 0x3d, 0xf6, 0xbc, 0x14, 0xa9, 0x8a, 0x55, 0x55, 0xaf, 0x65,
|
||||
0x8d, 0xa3, 0x27, 0xb6, 0x7c, 0x0a, 0x77, 0x99, 0xe7, 0x28, 0xed, 0x8a, 0x1b, 0xa0, 0x93, 0x64,
|
||||
0xb7, 0x68, 0xd4, 0xd9, 0xe5, 0x06, 0x44, 0xdf, 0x41, 0x30, 0x4e, 0x50, 0x2a, 0x5e, 0x26, 0xaf,
|
||||
0x6a, 0x3a, 0x03, 0xef, 0xeb, 0xe9, 0x37, 0xcf, 0xeb, 0x0a, 0xe8, 0xbc, 0x5d, 0x67, 0xf7, 0x6f,
|
||||
0xeb, 0x7c, 0x22, 0x72, 0x71, 0x3c, 0xd1, 0x73, 0xee, 0x72, 0x8b, 0xa2, 0x8f, 0xc0, 0x23, 0xd9,
|
||||
0x68, 0x45, 0xf6, 0x5e, 0x27, 0x39, 0xb3, 0xae, 0xfe, 0x2a, 0x7c, 0xf2, 0x57, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xda, 0x20, 0xfc, 0x99, 0x27, 0x0a, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -23,15 +23,15 @@ message Dashboard {
|
|||
}
|
||||
|
||||
message DashboardCell {
|
||||
int32 x = 1; // X-coordinate of Cell in the Dashboard
|
||||
int32 y = 2; // Y-coordinate of Cell in the Dashboard
|
||||
int32 w = 3; // Width of Cell in the Dashboard
|
||||
int32 h = 4; // Height of Cell in the Dashboard
|
||||
repeated Query queries = 5; // Time-series data queries for Dashboard
|
||||
string name = 6; // User-facing name for this Dashboard
|
||||
string type = 7; // Dashboard visualization type
|
||||
string ID = 8; // id is the unique id of the dashboard. MIGRATED FIELD added in 1.2.0-beta6
|
||||
map<string, Axis> axes = 9; // Axes represent the graphical viewport for a cell's visualizations
|
||||
int32 x = 1; // X-coordinate of Cell in the Dashboard
|
||||
int32 y = 2; // Y-coordinate of Cell in the Dashboard
|
||||
int32 w = 3; // Width of Cell in the Dashboard
|
||||
int32 h = 4; // Height of Cell in the Dashboard
|
||||
repeated Query queries = 5; // Time-series data queries for Dashboard
|
||||
string name = 6; // User-facing name for this Dashboard
|
||||
string type = 7; // Dashboard visualization type
|
||||
string ID = 8; // id is the unique id of the dashboard. MIGRATED FIELD added in 1.2.0-beta6
|
||||
map<string, Axis> axes = 9; // Axes represent the graphical viewport for a cell's visualizations
|
||||
}
|
||||
|
||||
message Axis {
|
||||
|
@ -54,18 +54,18 @@ message Template {
|
|||
}
|
||||
|
||||
message TemplateValue {
|
||||
string type = 1; // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant
|
||||
string value = 2; // Value is the specific value used to replace a template in an InfluxQL query
|
||||
bool selected = 3; // Selected states that this variable has been picked to use for replacement
|
||||
string type = 1; // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant
|
||||
string value = 2; // Value is the specific value used to replace a template in an InfluxQL query
|
||||
bool selected = 3; // Selected states that this variable has been picked to use for replacement
|
||||
}
|
||||
|
||||
message TemplateQuery {
|
||||
string command = 1; // Command is the query itself
|
||||
string db = 2; // DB the database for the query (optional)
|
||||
string rp = 3; // RP is a retention policy and optional;
|
||||
string measurement = 4; // Measurement is the optinally selected measurement for the query
|
||||
string tag_key = 5; // TagKey is the optionally selected tag key for the query
|
||||
string field_key = 6; // FieldKey is the optionally selected field key for the query
|
||||
string command = 1; // Command is the query itself
|
||||
string db = 2; // DB the database for the query (optional)
|
||||
string rp = 3; // RP is a retention policy and optional;
|
||||
string measurement = 4; // Measurement is the optinally selected measurement for the query
|
||||
string tag_key = 5; // TagKey is the optionally selected tag key for the query
|
||||
string field_key = 6; // FieldKey is the optionally selected field key for the query
|
||||
}
|
||||
|
||||
message Server {
|
||||
|
@ -101,31 +101,38 @@ message Cell {
|
|||
}
|
||||
|
||||
message Query {
|
||||
string Command = 1; // Command is the query itself
|
||||
string DB = 2; // DB the database for the query (optional)
|
||||
string RP = 3; // RP is a retention policy and optional;
|
||||
repeated string GroupBys= 4; // GroupBys define the groups to combine in the query
|
||||
repeated string Wheres = 5; // Wheres define the restrictions on the query
|
||||
string Label = 6; // Label is the name of the Y-Axis
|
||||
Range Range = 7; // Range is the upper and lower bound of the Y-Axis
|
||||
string Source = 8; // Source is the optional URI to the data source
|
||||
string Command = 1; // Command is the query itself
|
||||
string DB = 2; // DB the database for the query (optional)
|
||||
string RP = 3; // RP is a retention policy and optional;
|
||||
repeated string GroupBys = 4; // GroupBys define the groups to combine in the query
|
||||
repeated string Wheres = 5; // Wheres define the restrictions on the query
|
||||
string Label = 6; // Label is the name of the Y-Axis
|
||||
Range Range = 7; // Range is the upper and lower bound of the Y-Axis
|
||||
string Source = 8; // Source is the optional URI to the data source
|
||||
repeated TimeShift Shifts = 9; // TimeShift represents a shift to apply to an influxql query's time range
|
||||
}
|
||||
|
||||
message TimeShift {
|
||||
string Label = 1; // Label user facing description
|
||||
string Unit = 2; // Unit influxql time unit representation i.e. ms, s, m, h, d
|
||||
string Quantity = 3; // Quantity number of units
|
||||
}
|
||||
|
||||
message Range {
|
||||
int64 Upper = 1; // Upper is the upper-bound of the range
|
||||
int64 Lower = 2; // Lower is the lower-bound of the range
|
||||
int64 Upper = 1; // Upper is the upper-bound of the range
|
||||
int64 Lower = 2; // Lower is the lower-bound of the range
|
||||
}
|
||||
|
||||
message AlertRule {
|
||||
string ID = 1; // ID is the unique ID of this alert rule
|
||||
string JSON = 2; // JSON byte representation of the alert
|
||||
int64 SrcID = 3; // SrcID is the id of the source this alert is associated with
|
||||
int64 KapaID = 4; // KapaID is the id of the kapacitor this alert is associated with
|
||||
string ID = 1; // ID is the unique ID of this alert rule
|
||||
string JSON = 2; // JSON byte representation of the alert
|
||||
int64 SrcID = 3; // SrcID is the id of the source this alert is associated with
|
||||
int64 KapaID = 4; // KapaID is the id of the kapacitor this alert is associated with
|
||||
}
|
||||
|
||||
message User {
|
||||
uint64 ID = 1; // ID is the unique ID of this user
|
||||
string Name = 2; // Name is the user's login name
|
||||
uint64 ID = 1; // ID is the unique ID of this user
|
||||
string Name = 2; // Name is the user's login name
|
||||
}
|
||||
|
||||
// The following is a vim modeline, it autoconfigures vim to have the
|
||||
|
|
|
@ -163,6 +163,7 @@ func Test_MarshalDashboard(t *testing.T) {
|
|||
Upper: int64(100),
|
||||
},
|
||||
Source: "/chronograf/v1/sources/1",
|
||||
Shifts: []chronograf.TimeShift{},
|
||||
},
|
||||
},
|
||||
Axes: map[string]chronograf.Axis{
|
||||
|
@ -210,6 +211,7 @@ func Test_MarshalDashboard_WithLegacyBounds(t *testing.T) {
|
|||
Range: &chronograf.Range{
|
||||
Upper: int64(100),
|
||||
},
|
||||
Shifts: []chronograf.TimeShift{},
|
||||
},
|
||||
},
|
||||
Axes: map[string]chronograf.Axis{
|
||||
|
@ -241,6 +243,7 @@ func Test_MarshalDashboard_WithLegacyBounds(t *testing.T) {
|
|||
Range: &chronograf.Range{
|
||||
Upper: int64(100),
|
||||
},
|
||||
Shifts: []chronograf.TimeShift{},
|
||||
},
|
||||
},
|
||||
Axes: map[string]chronograf.Axis{
|
||||
|
@ -285,6 +288,7 @@ func Test_MarshalDashboard_WithEmptyLegacyBounds(t *testing.T) {
|
|||
Range: &chronograf.Range{
|
||||
Upper: int64(100),
|
||||
},
|
||||
Shifts: []chronograf.TimeShift{},
|
||||
},
|
||||
},
|
||||
Axes: map[string]chronograf.Axis{
|
||||
|
@ -316,6 +320,7 @@ func Test_MarshalDashboard_WithEmptyLegacyBounds(t *testing.T) {
|
|||
Range: &chronograf.Range{
|
||||
Upper: int64(100),
|
||||
},
|
||||
Shifts: []chronograf.TimeShift{},
|
||||
},
|
||||
},
|
||||
Axes: map[string]chronograf.Axis{
|
||||
|
|
|
@ -171,6 +171,7 @@ type DashboardQuery struct {
|
|||
Range *Range `json:"range,omitempty"` // Range is the default Y-Axis range for the data
|
||||
QueryConfig QueryConfig `json:"queryConfig,omitempty"` // QueryConfig represents the query state that is understood by the data explorer
|
||||
Source string `json:"source"` // Source is the optional URI to the data source for this queryConfig
|
||||
Shifts []TimeShift `json:"-"` // Shifts represents shifts to apply to an influxql query's time range. Clients expect the shift to be in the generated QueryConfig
|
||||
}
|
||||
|
||||
// TemplateQuery is used to retrieve choices for template replacement
|
||||
|
@ -284,6 +285,13 @@ type DurationRange struct {
|
|||
Lower string `json:"lower"`
|
||||
}
|
||||
|
||||
// TimeShift represents a shift to apply to an influxql query's time range
|
||||
type TimeShift struct {
|
||||
Label string `json:"label"` // Label user facing description
|
||||
Unit string `json:"unit"` // Unit influxql time unit representation i.e. ms, s, m, h, d
|
||||
Quantity string `json:"quantity"` // Quantity number of units
|
||||
}
|
||||
|
||||
// QueryConfig represents UI query from the data explorer
|
||||
type QueryConfig struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
|
@ -297,6 +305,7 @@ type QueryConfig struct {
|
|||
Fill string `json:"fill,omitempty"`
|
||||
RawText *string `json:"rawText"`
|
||||
Range *DurationRange `json:"range"`
|
||||
Shifts []TimeShift `json:"shifts"`
|
||||
}
|
||||
|
||||
// KapacitorNode adds arguments and properties to an alert
|
||||
|
|
|
@ -31,7 +31,6 @@ func newCellResponses(dID chronograf.DashboardID, dcells []chronograf.DashboardC
|
|||
cells := make([]dashboardCellResponse, len(dcells))
|
||||
for i, cell := range dcells {
|
||||
newCell := chronograf.DashboardCell{}
|
||||
|
||||
newCell.Queries = make([]chronograf.DashboardQuery, len(cell.Queries))
|
||||
copy(newCell.Queries, cell.Queries)
|
||||
|
||||
|
@ -70,7 +69,17 @@ func newCellResponses(dID chronograf.DashboardID, dcells []chronograf.DashboardC
|
|||
// ValidDashboardCellRequest verifies that the dashboard cells have a query and
|
||||
// have the correct axes specified
|
||||
func ValidDashboardCellRequest(c *chronograf.DashboardCell) error {
|
||||
if c == nil {
|
||||
return fmt.Errorf("Chronograf dashboard cell was nil")
|
||||
}
|
||||
|
||||
CorrectWidthHeight(c)
|
||||
for _, q := range c.Queries {
|
||||
if err := ValidateQueryConfig(&q.QueryConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
MoveTimeShift(c)
|
||||
return HasCorrectAxes(c)
|
||||
}
|
||||
|
||||
|
@ -115,12 +124,22 @@ func CorrectWidthHeight(c *chronograf.DashboardCell) {
|
|||
}
|
||||
}
|
||||
|
||||
// MoveTimeShift moves TimeShift from the QueryConfig to the DashboardQuery
|
||||
func MoveTimeShift(c *chronograf.DashboardCell) {
|
||||
for i, query := range c.Queries {
|
||||
query.Shifts = query.QueryConfig.Shifts
|
||||
c.Queries[i] = query
|
||||
}
|
||||
}
|
||||
|
||||
// AddQueryConfig updates a cell by converting InfluxQL into queryconfigs
|
||||
// If influxql cannot be represented by a full query config, then, the
|
||||
// query config's raw text is set to the command.
|
||||
func AddQueryConfig(c *chronograf.DashboardCell) {
|
||||
for i, q := range c.Queries {
|
||||
qc := ToQueryConfig(q.Command)
|
||||
qc.Shifts = append([]chronograf.TimeShift(nil), q.Shifts...)
|
||||
q.Shifts = nil
|
||||
q.QueryConfig = qc
|
||||
c.Queries[i] = q
|
||||
}
|
||||
|
|
|
@ -162,14 +162,14 @@ func Test_Service_DashboardCells(t *testing.T) {
|
|||
http.StatusOK,
|
||||
},
|
||||
{
|
||||
"cell axes should always be \"x\", \"y\", and \"y2\"",
|
||||
&url.URL{
|
||||
name: "cell axes should always be \"x\", \"y\", and \"y2\"",
|
||||
reqURL: &url.URL{
|
||||
Path: "/chronograf/v1/dashboards/1/cells",
|
||||
},
|
||||
map[string]string{
|
||||
ctxParams: map[string]string{
|
||||
"id": "1",
|
||||
},
|
||||
[]chronograf.DashboardCell{
|
||||
mockResponse: []chronograf.DashboardCell{
|
||||
{
|
||||
ID: "3899be5a-f6eb-4347-b949-de2f4fbea859",
|
||||
X: 0,
|
||||
|
@ -182,7 +182,7 @@ func Test_Service_DashboardCells(t *testing.T) {
|
|||
Axes: map[string]chronograf.Axis{},
|
||||
},
|
||||
},
|
||||
[]chronograf.DashboardCell{
|
||||
expected: []chronograf.DashboardCell{
|
||||
{
|
||||
ID: "3899be5a-f6eb-4347-b949-de2f4fbea859",
|
||||
X: 0,
|
||||
|
@ -205,7 +205,7 @@ func Test_Service_DashboardCells(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
http.StatusOK,
|
||||
expectedCode: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -219,6 +219,13 @@ func Test_newDashboardResponse(t *testing.T) {
|
|||
{
|
||||
Source: "/chronograf/v1/sources/1",
|
||||
Command: "SELECT donors from hill_valley_preservation_society where time > '1985-10-25 08:00:00'",
|
||||
Shifts: []chronograf.TimeShift{
|
||||
{
|
||||
Label: "Best Week Evar",
|
||||
Unit: "d",
|
||||
Quantity: "7",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Axes: map[string]chronograf.Axis{
|
||||
|
@ -267,6 +274,13 @@ func Test_newDashboardResponse(t *testing.T) {
|
|||
},
|
||||
Tags: make(map[string][]string, 0),
|
||||
AreTagsAccepted: false,
|
||||
Shifts: []chronograf.TimeShift{
|
||||
{
|
||||
Label: "Best Week Evar",
|
||||
Unit: "d",
|
||||
Quantity: "7",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -84,6 +84,7 @@ func (s *Service) Queries(w http.ResponseWriter, r *http.Request) {
|
|||
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
||||
return
|
||||
}
|
||||
qc.Shifts = []chronograf.TimeShift{}
|
||||
qr.QueryConfig = qc
|
||||
|
||||
if stmt, err := queries.ParseSelect(query); err == nil {
|
||||
|
|
|
@ -60,7 +60,7 @@ func TestService_Queries(t *testing.T) {
|
|||
"id": "82b60d37-251e-4afe-ac93-ca20a3642b11"
|
||||
}
|
||||
]}`))),
|
||||
want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM db.\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"db","measurement":"httpd","retentionPolicy":"monitor","fields":[{"value":"pingReq","type":"field","alias":""}],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":null,"range":{"upper":"","lower":"now() - 1m"}},"queryAST":{"condition":{"expr":"binary","op":"\u003e","lhs":{"expr":"reference","val":"time"},"rhs":{"expr":"binary","op":"-","lhs":{"expr":"call","name":"now"},"rhs":{"expr":"literal","val":"1m","type":"duration"}}},"fields":[{"column":{"expr":"reference","val":"pingReq"}}],"sources":[{"database":"db","retentionPolicy":"monitor","name":"httpd","type":"measurement"}]}}]}
|
||||
want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM db.\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"db","measurement":"httpd","retentionPolicy":"monitor","fields":[{"value":"pingReq","type":"field","alias":""}],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":null,"range":{"upper":"","lower":"now() - 1m"},"shifts":[]},"queryAST":{"condition":{"expr":"binary","op":"\u003e","lhs":{"expr":"reference","val":"time"},"rhs":{"expr":"binary","op":"-","lhs":{"expr":"call","name":"now"},"rhs":{"expr":"literal","val":"1m","type":"duration"}}},"fields":[{"column":{"expr":"reference","val":"pingReq"}}],"sources":[{"database":"db","retentionPolicy":"monitor","name":"httpd","type":"measurement"}]}}]}
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
@ -81,7 +81,7 @@ func TestService_Queries(t *testing.T) {
|
|||
"id": "82b60d37-251e-4afe-ac93-ca20a3642b11"
|
||||
}
|
||||
]}`))),
|
||||
want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SHOW DATABASES","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SHOW DATABASES","range":null}}]}
|
||||
want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SHOW DATABASES","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SHOW DATABASES","range":null,"shifts":[]}}]}
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
@ -166,7 +166,7 @@ func TestService_Queries(t *testing.T) {
|
|||
}
|
||||
]
|
||||
}`))),
|
||||
want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","range":null},"queryTemplated":"SELECT \"pingReq\" FROM \"_internal\".\"monitor\".\"httpd\" WHERE time \u003e now() - 15m AND time \u003c now() GROUP BY time(2s)","tempVars":[{"tempVar":":upperDashboardTime:","values":[{"value":"now()","type":"constant","selected":true}]},{"tempVar":":dashboardTime:","values":[{"value":"now() - 15m","type":"constant","selected":true}]},{"tempVar":":dbs:","values":[{"value":"_internal","type":"database","selected":true}]},{"tempVar":":interval:","values":[{"value":"1000","type":"resolution","selected":false},{"value":"3","type":"pointsPerPixel","selected":false}]}]}]}
|
||||
want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e :dashboardTime: AND time \u003c :upperDashboardTime: GROUP BY :interval:","range":null,"shifts":[]},"queryTemplated":"SELECT \"pingReq\" FROM \"_internal\".\"monitor\".\"httpd\" WHERE time \u003e now() - 15m AND time \u003c now() GROUP BY time(2s)","tempVars":[{"tempVar":":upperDashboardTime:","values":[{"value":"now()","type":"constant","selected":true}]},{"tempVar":":dashboardTime:","values":[{"value":"now() - 15m","type":"constant","selected":true}]},{"tempVar":":dbs:","values":[{"value":"_internal","type":"database","selected":true}]},{"tempVar":":interval:","values":[{"value":"1000","type":"resolution","selected":false},{"value":"3","type":"pointsPerPixel","selected":false}]}]}]}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
"github.com/influxdata/chronograf/influx"
|
||||
)
|
||||
|
@ -22,3 +24,28 @@ func ToQueryConfig(query string) chronograf.QueryConfig {
|
|||
Tags: make(map[string][]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
var validFieldTypes = map[string]bool{
|
||||
"func": true,
|
||||
"field": true,
|
||||
"integer": true,
|
||||
"number": true,
|
||||
"regex": true,
|
||||
"wildcard": true,
|
||||
}
|
||||
|
||||
// ValidateQueryConfig checks any query config input
|
||||
func ValidateQueryConfig(q *chronograf.QueryConfig) error {
|
||||
for _, fld := range q.Fields {
|
||||
invalid := fmt.Errorf(`invalid field type "%s" ; expect func, field, integer, number, regex, wildcard`, fld.Type)
|
||||
if !validFieldTypes[fld.Type] {
|
||||
return invalid
|
||||
}
|
||||
for _, arg := range fld.Args {
|
||||
if !validFieldTypes[arg.Type] {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
)
|
||||
|
||||
func TestValidateQueryConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
q *chronograf.QueryConfig
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "invalid field type",
|
||||
q: &chronograf.QueryConfig{
|
||||
Fields: []chronograf.Field{
|
||||
{
|
||||
Type: "invalid",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid field args",
|
||||
q: &chronograf.QueryConfig{
|
||||
Fields: []chronograf.Field{
|
||||
{
|
||||
Type: "func",
|
||||
Args: []chronograf.Field{
|
||||
{
|
||||
Type: "invalid",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := ValidateQueryConfig(tt.q); (err != nil) != tt.wantErr {
|
||||
t.Errorf("ValidateQueryConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
"test": "karma start",
|
||||
"test:integration": "nightwatch tests --skip",
|
||||
"test:lint": "yarn run lint; yarn run test",
|
||||
"test:dev": "concurrently \"yarn run lint -- --watch\" \"yarn run test -- --no-single-run --reporters=verbose\"",
|
||||
"test:dev": "concurrently \"yarn run lint --watch\" \"yarn run test --no-single-run --reporters=verbose\"",
|
||||
"clean": "rm -rf build",
|
||||
"storybook": "node ./storybook.js",
|
||||
"prettier": "prettier --single-quote --trailing-comma es5 --bracket-spacing false --semi false --write \"{src,spec}/**/*.js\"; eslint src --fix"
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import reducer from 'src/data_explorer/reducers/queryConfigs'
|
||||
|
||||
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
||||
import {
|
||||
fill,
|
||||
timeShift,
|
||||
chooseTag,
|
||||
groupByTag,
|
||||
groupByTime,
|
||||
|
@ -26,63 +28,63 @@ const fakeAddQueryAction = (panelID, queryID) => {
|
|||
}
|
||||
}
|
||||
|
||||
function buildInitialState(queryId, params) {
|
||||
return Object.assign({}, defaultQueryConfig({id: queryId}), params)
|
||||
function buildInitialState(queryID, params) {
|
||||
return Object.assign({}, defaultQueryConfig({id: queryID}), params)
|
||||
}
|
||||
|
||||
describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
||||
const queryId = 123
|
||||
const queryID = 123
|
||||
|
||||
it('can add a query', () => {
|
||||
const state = reducer({}, fakeAddQueryAction('blah', queryId))
|
||||
const state = reducer({}, fakeAddQueryAction('blah', queryID))
|
||||
|
||||
const actual = state[queryId]
|
||||
const expected = defaultQueryConfig({id: queryId})
|
||||
const actual = state[queryID]
|
||||
const expected = defaultQueryConfig({id: queryID})
|
||||
expect(actual).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
describe('choosing db, rp, and measurement', () => {
|
||||
let state
|
||||
beforeEach(() => {
|
||||
state = reducer({}, fakeAddQueryAction('any', queryId))
|
||||
state = reducer({}, fakeAddQueryAction('any', queryID))
|
||||
})
|
||||
|
||||
it('sets the db and rp', () => {
|
||||
const newState = reducer(
|
||||
state,
|
||||
chooseNamespace(queryId, {
|
||||
chooseNamespace(queryID, {
|
||||
database: 'telegraf',
|
||||
retentionPolicy: 'monitor',
|
||||
})
|
||||
)
|
||||
|
||||
expect(newState[queryId].database).to.equal('telegraf')
|
||||
expect(newState[queryId].retentionPolicy).to.equal('monitor')
|
||||
expect(newState[queryID].database).to.equal('telegraf')
|
||||
expect(newState[queryID].retentionPolicy).to.equal('monitor')
|
||||
})
|
||||
|
||||
it('sets the measurement', () => {
|
||||
const newState = reducer(state, chooseMeasurement(queryId, 'mem'))
|
||||
const newState = reducer(state, chooseMeasurement(queryID, 'mem'))
|
||||
|
||||
expect(newState[queryId].measurement).to.equal('mem')
|
||||
expect(newState[queryID].measurement).to.equal('mem')
|
||||
})
|
||||
})
|
||||
|
||||
describe('a query has measurements and fields', () => {
|
||||
let state
|
||||
beforeEach(() => {
|
||||
const one = reducer({}, fakeAddQueryAction('any', queryId))
|
||||
const one = reducer({}, fakeAddQueryAction('any', queryID))
|
||||
const two = reducer(
|
||||
one,
|
||||
chooseNamespace(queryId, {
|
||||
chooseNamespace(queryID, {
|
||||
database: '_internal',
|
||||
retentionPolicy: 'daily',
|
||||
})
|
||||
)
|
||||
const three = reducer(two, chooseMeasurement(queryId, 'disk'))
|
||||
const three = reducer(two, chooseMeasurement(queryID, 'disk'))
|
||||
|
||||
state = reducer(
|
||||
three,
|
||||
addInitialField(queryId, {
|
||||
addInitialField(queryID, {
|
||||
value: 'a great field',
|
||||
type: 'field',
|
||||
})
|
||||
|
@ -92,91 +94,91 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
describe('choosing a new namespace', () => {
|
||||
it('clears out the old measurement and fields', () => {
|
||||
// what about tags?
|
||||
expect(state[queryId].measurement).to.equal('disk')
|
||||
expect(state[queryId].fields.length).to.equal(1)
|
||||
expect(state[queryID].measurement).to.equal('disk')
|
||||
expect(state[queryID].fields.length).to.equal(1)
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
chooseNamespace(queryId, {
|
||||
chooseNamespace(queryID, {
|
||||
database: 'newdb',
|
||||
retentionPolicy: 'newrp',
|
||||
})
|
||||
)
|
||||
|
||||
expect(newState[queryId].measurement).to.be.null
|
||||
expect(newState[queryId].fields.length).to.equal(0)
|
||||
expect(newState[queryID].measurement).to.be.null
|
||||
expect(newState[queryID].fields.length).to.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('choosing a new measurement', () => {
|
||||
it('leaves the namespace and clears out the old fields', () => {
|
||||
// what about tags?
|
||||
expect(state[queryId].fields.length).to.equal(1)
|
||||
expect(state[queryID].fields.length).to.equal(1)
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
chooseMeasurement(queryId, 'newmeasurement')
|
||||
chooseMeasurement(queryID, 'newmeasurement')
|
||||
)
|
||||
|
||||
expect(state[queryId].database).to.equal(newState[queryId].database)
|
||||
expect(state[queryId].retentionPolicy).to.equal(
|
||||
newState[queryId].retentionPolicy
|
||||
expect(state[queryID].database).to.equal(newState[queryID].database)
|
||||
expect(state[queryID].retentionPolicy).to.equal(
|
||||
newState[queryID].retentionPolicy
|
||||
)
|
||||
expect(newState[queryId].fields.length).to.equal(0)
|
||||
expect(newState[queryID].fields.length).to.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('DE_TOGGLE_FIELD', () => {
|
||||
it('can toggle multiple fields', () => {
|
||||
expect(state[queryId].fields.length).to.equal(1)
|
||||
expect(state[queryID].fields.length).to.equal(1)
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
toggleField(queryId, {
|
||||
toggleField(queryID, {
|
||||
value: 'f2',
|
||||
type: 'field',
|
||||
})
|
||||
)
|
||||
|
||||
expect(newState[queryId].fields.length).to.equal(2)
|
||||
expect(newState[queryId].fields[1].alias).to.deep.equal('mean_f2')
|
||||
expect(newState[queryId].fields[1].args).to.deep.equal([
|
||||
expect(newState[queryID].fields.length).to.equal(2)
|
||||
expect(newState[queryID].fields[1].alias).to.deep.equal('mean_f2')
|
||||
expect(newState[queryID].fields[1].args).to.deep.equal([
|
||||
{value: 'f2', type: 'field'},
|
||||
])
|
||||
expect(newState[queryId].fields[1].value).to.deep.equal('mean')
|
||||
expect(newState[queryID].fields[1].value).to.deep.equal('mean')
|
||||
})
|
||||
|
||||
it('applies a func to newly selected fields', () => {
|
||||
expect(state[queryId].fields.length).to.equal(1)
|
||||
expect(state[queryId].fields[0].type).to.equal('func')
|
||||
expect(state[queryId].fields[0].value).to.equal('mean')
|
||||
expect(state[queryID].fields.length).to.equal(1)
|
||||
expect(state[queryID].fields[0].type).to.equal('func')
|
||||
expect(state[queryID].fields[0].value).to.equal('mean')
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
toggleField(queryId, {
|
||||
toggleField(queryID, {
|
||||
value: 'f2',
|
||||
type: 'field',
|
||||
})
|
||||
)
|
||||
|
||||
expect(newState[queryId].fields[1].value).to.equal('mean')
|
||||
expect(newState[queryId].fields[1].alias).to.equal('mean_f2')
|
||||
expect(newState[queryId].fields[1].args).to.deep.equal([
|
||||
expect(newState[queryID].fields[1].value).to.equal('mean')
|
||||
expect(newState[queryID].fields[1].alias).to.equal('mean_f2')
|
||||
expect(newState[queryID].fields[1].args).to.deep.equal([
|
||||
{value: 'f2', type: 'field'},
|
||||
])
|
||||
expect(newState[queryId].fields[1].type).to.equal('func')
|
||||
expect(newState[queryID].fields[1].type).to.equal('func')
|
||||
})
|
||||
|
||||
it('adds the field property to query config if not found', () => {
|
||||
delete state[queryId].fields
|
||||
expect(state[queryId].fields).to.equal(undefined)
|
||||
delete state[queryID].fields
|
||||
expect(state[queryID].fields).to.equal(undefined)
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
toggleField(queryId, {value: 'fk1', type: 'field'})
|
||||
toggleField(queryID, {value: 'fk1', type: 'field'})
|
||||
)
|
||||
|
||||
expect(newState[queryId].fields.length).to.equal(1)
|
||||
expect(newState[queryID].fields.length).to.equal(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -189,7 +191,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
const f4 = {value: 'f4', type: 'field'}
|
||||
|
||||
const initialState = {
|
||||
[queryId]: {
|
||||
[queryID]: {
|
||||
id: 123,
|
||||
database: 'db1',
|
||||
measurement: 'm1',
|
||||
|
@ -201,7 +203,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
},
|
||||
}
|
||||
|
||||
const action = applyFuncsToField(queryId, {
|
||||
const action = applyFuncsToField(queryID, {
|
||||
field: {value: 'f1', type: 'field'},
|
||||
funcs: [
|
||||
{value: 'fn3', type: 'func', args: []},
|
||||
|
@ -211,7 +213,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].fields).to.deep.equal([
|
||||
expect(nextState[queryID].fields).to.deep.equal([
|
||||
{value: 'fn3', type: 'func', args: [f1], alias: `fn3_${f1.value}`},
|
||||
{value: 'fn4', type: 'func', args: [f1], alias: `fn4_${f1.value}`},
|
||||
{value: 'fn1', type: 'func', args: [f2], alias: `fn1_${f2.value}`},
|
||||
|
@ -230,7 +232,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
const groupBy = {time: '1m', tags: []}
|
||||
|
||||
const initialState = {
|
||||
[queryId]: {
|
||||
[queryID]: {
|
||||
id: 123,
|
||||
database: 'db1',
|
||||
measurement: 'm1',
|
||||
|
@ -239,35 +241,35 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
},
|
||||
}
|
||||
|
||||
const action = removeFuncs(queryId, fields, groupBy)
|
||||
const action = removeFuncs(queryID, fields, groupBy)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
const actual = nextState[queryId].fields
|
||||
const actual = nextState[queryID].fields
|
||||
const expected = [f1, f2]
|
||||
|
||||
expect(actual).to.eql(expected)
|
||||
expect(nextState[queryId].groupBy.time).to.equal(null)
|
||||
expect(nextState[queryID].groupBy.time).to.equal(null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('DE_CHOOSE_TAG', () => {
|
||||
it('adds a tag key/value to the query', () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId, {
|
||||
[queryID]: buildInitialState(queryID, {
|
||||
tags: {
|
||||
k1: ['v0'],
|
||||
k2: ['foo'],
|
||||
},
|
||||
}),
|
||||
}
|
||||
const action = chooseTag(queryId, {
|
||||
const action = chooseTag(queryID, {
|
||||
key: 'k1',
|
||||
value: 'v1',
|
||||
})
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].tags).to.eql({
|
||||
expect(nextState[queryID].tags).to.eql({
|
||||
k1: ['v0', 'v1'],
|
||||
k2: ['foo'],
|
||||
})
|
||||
|
@ -275,31 +277,31 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
|
||||
it("creates a new entry if it's the first key", () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId, {
|
||||
[queryID]: buildInitialState(queryID, {
|
||||
tags: {},
|
||||
}),
|
||||
}
|
||||
const action = chooseTag(queryId, {
|
||||
const action = chooseTag(queryID, {
|
||||
key: 'k1',
|
||||
value: 'v1',
|
||||
})
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].tags).to.eql({
|
||||
expect(nextState[queryID].tags).to.eql({
|
||||
k1: ['v1'],
|
||||
})
|
||||
})
|
||||
|
||||
it('removes a value that is already in the list', () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId, {
|
||||
[queryID]: buildInitialState(queryID, {
|
||||
tags: {
|
||||
k1: ['v1'],
|
||||
},
|
||||
}),
|
||||
}
|
||||
const action = chooseTag(queryId, {
|
||||
const action = chooseTag(queryID, {
|
||||
key: 'k1',
|
||||
value: 'v1',
|
||||
})
|
||||
|
@ -307,14 +309,14 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
const nextState = reducer(initialState, action)
|
||||
|
||||
// TODO: this should probably remove the `k1` property entirely from the tags object
|
||||
expect(nextState[queryId].tags).to.eql({})
|
||||
expect(nextState[queryID].tags).to.eql({})
|
||||
})
|
||||
})
|
||||
|
||||
describe('DE_GROUP_BY_TAG', () => {
|
||||
it('adds a tag key/value to the query', () => {
|
||||
const initialState = {
|
||||
[queryId]: {
|
||||
[queryID]: {
|
||||
id: 123,
|
||||
database: 'db1',
|
||||
measurement: 'm1',
|
||||
|
@ -323,11 +325,11 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
groupBy: {tags: [], time: null},
|
||||
},
|
||||
}
|
||||
const action = groupByTag(queryId, 'k1')
|
||||
const action = groupByTag(queryID, 'k1')
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].groupBy).to.eql({
|
||||
expect(nextState[queryID].groupBy).to.eql({
|
||||
time: null,
|
||||
tags: ['k1'],
|
||||
})
|
||||
|
@ -335,7 +337,7 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
|
||||
it('removes a tag if the given tag key is already in the GROUP BY list', () => {
|
||||
const initialState = {
|
||||
[queryId]: {
|
||||
[queryID]: {
|
||||
id: 123,
|
||||
database: 'db1',
|
||||
measurement: 'm1',
|
||||
|
@ -344,11 +346,11 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
groupBy: {tags: ['k1'], time: null},
|
||||
},
|
||||
}
|
||||
const action = groupByTag(queryId, 'k1')
|
||||
const action = groupByTag(queryID, 'k1')
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].groupBy).to.eql({
|
||||
expect(nextState[queryID].groupBy).to.eql({
|
||||
time: null,
|
||||
tags: [],
|
||||
})
|
||||
|
@ -358,14 +360,14 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
describe('DE_TOGGLE_TAG_ACCEPTANCE', () => {
|
||||
it('it toggles areTagsAccepted', () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId),
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
const action = toggleTagAcceptance(queryId)
|
||||
const action = toggleTagAcceptance(queryID)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].areTagsAccepted).to.equal(
|
||||
!initialState[queryId].areTagsAccepted
|
||||
expect(nextState[queryID].areTagsAccepted).to.equal(
|
||||
!initialState[queryID].areTagsAccepted
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -374,99 +376,113 @@ describe('Chronograf.Reducers.DataExplorer.queryConfigs', () => {
|
|||
it('applys the appropriate group by time', () => {
|
||||
const time = '100y'
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId),
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
|
||||
const action = groupByTime(queryId, time)
|
||||
const action = groupByTime(queryID, time)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].groupBy.time).to.equal(time)
|
||||
expect(nextState[queryID].groupBy.time).to.equal(time)
|
||||
})
|
||||
})
|
||||
|
||||
it('updates entire config', () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId),
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
const expected = defaultQueryConfig({id: queryId}, {rawText: 'hello'})
|
||||
const expected = defaultQueryConfig({id: queryID}, {rawText: 'hello'})
|
||||
const action = updateQueryConfig(expected)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId]).to.deep.equal(expected)
|
||||
expect(nextState[queryID]).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it("updates a query's raw text", () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId),
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
const text = 'foo'
|
||||
const action = updateRawQuery(queryId, text)
|
||||
const action = updateRawQuery(queryID, text)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].rawText).to.equal('foo')
|
||||
expect(nextState[queryID].rawText).to.equal('foo')
|
||||
})
|
||||
|
||||
it("updates a query's raw status", () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId),
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
const status = 'your query was sweet'
|
||||
const action = editQueryStatus(queryId, status)
|
||||
const action = editQueryStatus(queryID, status)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].status).to.equal(status)
|
||||
expect(nextState[queryID].status).to.equal(status)
|
||||
})
|
||||
|
||||
describe('DE_FILL', () => {
|
||||
it('applies an explicit fill when group by time is used', () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId),
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
const time = '10s'
|
||||
const action = groupByTime(queryId, time)
|
||||
const action = groupByTime(queryID, time)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].fill).to.equal(NULL_STRING)
|
||||
expect(nextState[queryID].fill).to.equal(NULL_STRING)
|
||||
})
|
||||
|
||||
it('updates fill to non-null-string non-number string value', () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId),
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
const action = fill(queryId, LINEAR)
|
||||
const action = fill(queryID, LINEAR)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].fill).to.equal(LINEAR)
|
||||
expect(nextState[queryID].fill).to.equal(LINEAR)
|
||||
})
|
||||
|
||||
it('updates fill to string integer value', () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId),
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
const INT_STRING = '1337'
|
||||
const action = fill(queryId, INT_STRING)
|
||||
const action = fill(queryID, INT_STRING)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].fill).to.equal(INT_STRING)
|
||||
expect(nextState[queryID].fill).to.equal(INT_STRING)
|
||||
})
|
||||
|
||||
it('updates fill to string float value', () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId),
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
const FLOAT_STRING = '1.337'
|
||||
const action = fill(queryId, FLOAT_STRING)
|
||||
const action = fill(queryID, FLOAT_STRING)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].fill).to.equal(FLOAT_STRING)
|
||||
expect(nextState[queryID].fill).to.equal(FLOAT_STRING)
|
||||
})
|
||||
})
|
||||
|
||||
describe('DE_TIME_SHIFT', () => {
|
||||
it('can shift the time', () => {
|
||||
const initialState = {
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
|
||||
const shift = {quantity: 1, unit: 'd', duration: '1d'}
|
||||
const action = timeShift(queryID, shift)
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryID].shifts).to.deep.equal([shift])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import reducer from 'src/kapacitor/reducers/queryConfigs'
|
||||
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
||||
import {
|
||||
chooseTag,
|
||||
timeShift,
|
||||
groupByTag,
|
||||
toggleField,
|
||||
groupByTime,
|
||||
chooseNamespace,
|
||||
chooseMeasurement,
|
||||
chooseTag,
|
||||
groupByTag,
|
||||
toggleTagAcceptance,
|
||||
toggleField,
|
||||
applyFuncsToField,
|
||||
groupByTime,
|
||||
toggleTagAcceptance,
|
||||
} from 'src/kapacitor/actions/queryConfigs'
|
||||
|
||||
const fakeAddQueryAction = (panelID, queryID) => {
|
||||
|
@ -18,142 +19,142 @@ const fakeAddQueryAction = (panelID, queryID) => {
|
|||
}
|
||||
}
|
||||
|
||||
function buildInitialState(queryId, params) {
|
||||
function buildInitialState(queryID, params) {
|
||||
return Object.assign(
|
||||
{},
|
||||
defaultQueryConfig({id: queryId, isKapacitorRule: true}),
|
||||
defaultQueryConfig({id: queryID, isKapacitorRule: true}),
|
||||
params
|
||||
)
|
||||
}
|
||||
|
||||
describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => {
|
||||
const queryId = 123
|
||||
const queryID = 123
|
||||
|
||||
it('can add a query', () => {
|
||||
const state = reducer({}, fakeAddQueryAction('blah', queryId))
|
||||
const state = reducer({}, fakeAddQueryAction('blah', queryID))
|
||||
|
||||
const actual = state[queryId]
|
||||
const expected = defaultQueryConfig({id: queryId, isKapacitorRule: true})
|
||||
const actual = state[queryID]
|
||||
const expected = defaultQueryConfig({id: queryID, isKapacitorRule: true})
|
||||
expect(actual).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
describe('choosing db, rp, and measurement', () => {
|
||||
let state
|
||||
beforeEach(() => {
|
||||
state = reducer({}, fakeAddQueryAction('any', queryId))
|
||||
state = reducer({}, fakeAddQueryAction('any', queryID))
|
||||
})
|
||||
|
||||
it('sets the db and rp', () => {
|
||||
const newState = reducer(
|
||||
state,
|
||||
chooseNamespace(queryId, {
|
||||
chooseNamespace(queryID, {
|
||||
database: 'telegraf',
|
||||
retentionPolicy: 'monitor',
|
||||
})
|
||||
)
|
||||
|
||||
expect(newState[queryId].database).to.equal('telegraf')
|
||||
expect(newState[queryId].retentionPolicy).to.equal('monitor')
|
||||
expect(newState[queryID].database).to.equal('telegraf')
|
||||
expect(newState[queryID].retentionPolicy).to.equal('monitor')
|
||||
})
|
||||
|
||||
it('sets the measurement', () => {
|
||||
const newState = reducer(state, chooseMeasurement(queryId, 'mem'))
|
||||
const newState = reducer(state, chooseMeasurement(queryID, 'mem'))
|
||||
|
||||
expect(newState[queryId].measurement).to.equal('mem')
|
||||
expect(newState[queryID].measurement).to.equal('mem')
|
||||
})
|
||||
})
|
||||
|
||||
describe('a query has measurements and fields', () => {
|
||||
let state
|
||||
beforeEach(() => {
|
||||
const one = reducer({}, fakeAddQueryAction('any', queryId))
|
||||
const one = reducer({}, fakeAddQueryAction('any', queryID))
|
||||
const two = reducer(
|
||||
one,
|
||||
chooseNamespace(queryId, {
|
||||
chooseNamespace(queryID, {
|
||||
database: '_internal',
|
||||
retentionPolicy: 'daily',
|
||||
})
|
||||
)
|
||||
const three = reducer(two, chooseMeasurement(queryId, 'disk'))
|
||||
const three = reducer(two, chooseMeasurement(queryID, 'disk'))
|
||||
state = reducer(
|
||||
three,
|
||||
toggleField(queryId, {value: 'a great field', funcs: []})
|
||||
toggleField(queryID, {value: 'a great field', funcs: []})
|
||||
)
|
||||
})
|
||||
|
||||
describe('choosing a new namespace', () => {
|
||||
it('clears out the old measurement and fields', () => {
|
||||
// what about tags?
|
||||
expect(state[queryId].measurement).to.exist
|
||||
expect(state[queryId].fields.length).to.equal(1)
|
||||
expect(state[queryID].measurement).to.exist
|
||||
expect(state[queryID].fields.length).to.equal(1)
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
chooseNamespace(queryId, {
|
||||
chooseNamespace(queryID, {
|
||||
database: 'newdb',
|
||||
retentionPolicy: 'newrp',
|
||||
})
|
||||
)
|
||||
|
||||
expect(newState[queryId].measurement).not.to.exist
|
||||
expect(newState[queryId].fields.length).to.equal(0)
|
||||
expect(newState[queryID].measurement).not.to.exist
|
||||
expect(newState[queryID].fields.length).to.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('choosing a new measurement', () => {
|
||||
it('leaves the namespace and clears out the old fields', () => {
|
||||
// what about tags?
|
||||
expect(state[queryId].fields.length).to.equal(1)
|
||||
expect(state[queryID].fields.length).to.equal(1)
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
chooseMeasurement(queryId, 'newmeasurement')
|
||||
chooseMeasurement(queryID, 'newmeasurement')
|
||||
)
|
||||
|
||||
expect(state[queryId].database).to.equal(newState[queryId].database)
|
||||
expect(state[queryId].retentionPolicy).to.equal(
|
||||
newState[queryId].retentionPolicy
|
||||
expect(state[queryID].database).to.equal(newState[queryID].database)
|
||||
expect(state[queryID].retentionPolicy).to.equal(
|
||||
newState[queryID].retentionPolicy
|
||||
)
|
||||
expect(newState[queryId].fields.length).to.equal(0)
|
||||
expect(newState[queryID].fields.length).to.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the query is part of a kapacitor rule', () => {
|
||||
it('only allows one field', () => {
|
||||
expect(state[queryId].fields.length).to.equal(1)
|
||||
expect(state[queryID].fields.length).to.equal(1)
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
toggleField(queryId, {value: 'a different field', type: 'field'})
|
||||
toggleField(queryID, {value: 'a different field', type: 'field'})
|
||||
)
|
||||
|
||||
expect(newState[queryId].fields.length).to.equal(1)
|
||||
expect(newState[queryId].fields[0].value).to.equal('a different field')
|
||||
expect(newState[queryID].fields.length).to.equal(1)
|
||||
expect(newState[queryID].fields[0].value).to.equal('a different field')
|
||||
})
|
||||
})
|
||||
|
||||
describe('KAPA_TOGGLE_FIELD', () => {
|
||||
it('cannot toggle multiple fields', () => {
|
||||
expect(state[queryId].fields.length).to.equal(1)
|
||||
expect(state[queryID].fields.length).to.equal(1)
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
toggleField(queryId, {value: 'a different field', type: 'field'})
|
||||
toggleField(queryID, {value: 'a different field', type: 'field'})
|
||||
)
|
||||
|
||||
expect(newState[queryId].fields.length).to.equal(1)
|
||||
expect(newState[queryId].fields[0].value).to.equal('a different field')
|
||||
expect(newState[queryID].fields.length).to.equal(1)
|
||||
expect(newState[queryID].fields[0].value).to.equal('a different field')
|
||||
})
|
||||
|
||||
it('applies no funcs to newly selected fields', () => {
|
||||
expect(state[queryId].fields.length).to.equal(1)
|
||||
expect(state[queryID].fields.length).to.equal(1)
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
toggleField(queryId, {value: 'a different field', type: 'field'})
|
||||
toggleField(queryID, {value: 'a different field', type: 'field'})
|
||||
)
|
||||
|
||||
expect(newState[queryId].fields[0].type).to.equal('field')
|
||||
expect(newState[queryID].fields[0].type).to.equal('field')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -162,7 +163,7 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => {
|
|||
it('applies functions to a field without any existing functions', () => {
|
||||
const f1 = {value: 'f1', type: 'field'}
|
||||
const initialState = {
|
||||
[queryId]: {
|
||||
[queryID]: {
|
||||
id: 123,
|
||||
database: 'db1',
|
||||
measurement: 'm1',
|
||||
|
@ -174,13 +175,13 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => {
|
|||
},
|
||||
}
|
||||
|
||||
const action = applyFuncsToField(queryId, {
|
||||
const action = applyFuncsToField(queryID, {
|
||||
field: {value: 'f1', type: 'field'},
|
||||
funcs: [{value: 'fn3', type: 'func'}, {value: 'fn4', type: 'func'}],
|
||||
})
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
const actual = nextState[queryId].fields
|
||||
const actual = nextState[queryID].fields
|
||||
const expected = [
|
||||
{value: 'fn3', type: 'func', args: [f1], alias: `fn3_${f1.value}`},
|
||||
{value: 'fn4', type: 'func', args: [f1], alias: `fn4_${f1.value}`},
|
||||
|
@ -193,21 +194,21 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => {
|
|||
describe('KAPA_CHOOSE_TAG', () => {
|
||||
it('adds a tag key/value to the query', () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId, {
|
||||
[queryID]: buildInitialState(queryID, {
|
||||
tags: {
|
||||
k1: ['v0'],
|
||||
k2: ['foo'],
|
||||
},
|
||||
}),
|
||||
}
|
||||
const action = chooseTag(queryId, {
|
||||
const action = chooseTag(queryID, {
|
||||
key: 'k1',
|
||||
value: 'v1',
|
||||
})
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].tags).to.eql({
|
||||
expect(nextState[queryID].tags).to.eql({
|
||||
k1: ['v0', 'v1'],
|
||||
k2: ['foo'],
|
||||
})
|
||||
|
@ -215,31 +216,31 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => {
|
|||
|
||||
it("creates a new entry if it's the first key", () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId, {
|
||||
[queryID]: buildInitialState(queryID, {
|
||||
tags: {},
|
||||
}),
|
||||
}
|
||||
const action = chooseTag(queryId, {
|
||||
const action = chooseTag(queryID, {
|
||||
key: 'k1',
|
||||
value: 'v1',
|
||||
})
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].tags).to.eql({
|
||||
expect(nextState[queryID].tags).to.eql({
|
||||
k1: ['v1'],
|
||||
})
|
||||
})
|
||||
|
||||
it('removes a value that is already in the list', () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId, {
|
||||
[queryID]: buildInitialState(queryID, {
|
||||
tags: {
|
||||
k1: ['v1'],
|
||||
},
|
||||
}),
|
||||
}
|
||||
const action = chooseTag(queryId, {
|
||||
const action = chooseTag(queryID, {
|
||||
key: 'k1',
|
||||
value: 'v1',
|
||||
})
|
||||
|
@ -247,14 +248,14 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => {
|
|||
const nextState = reducer(initialState, action)
|
||||
|
||||
// TODO: this should probably remove the `k1` property entirely from the tags object
|
||||
expect(nextState[queryId].tags).to.eql({})
|
||||
expect(nextState[queryID].tags).to.eql({})
|
||||
})
|
||||
})
|
||||
|
||||
describe('KAPA_GROUP_BY_TAG', () => {
|
||||
it('adds a tag key/value to the query', () => {
|
||||
const initialState = {
|
||||
[queryId]: {
|
||||
[queryID]: {
|
||||
id: 123,
|
||||
database: 'db1',
|
||||
measurement: 'm1',
|
||||
|
@ -263,11 +264,11 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => {
|
|||
groupBy: {tags: [], time: null},
|
||||
},
|
||||
}
|
||||
const action = groupByTag(queryId, 'k1')
|
||||
const action = groupByTag(queryID, 'k1')
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].groupBy).to.eql({
|
||||
expect(nextState[queryID].groupBy).to.eql({
|
||||
time: null,
|
||||
tags: ['k1'],
|
||||
})
|
||||
|
@ -275,7 +276,7 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => {
|
|||
|
||||
it('removes a tag if the given tag key is already in the GROUP BY list', () => {
|
||||
const initialState = {
|
||||
[queryId]: {
|
||||
[queryID]: {
|
||||
id: 123,
|
||||
database: 'db1',
|
||||
measurement: 'm1',
|
||||
|
@ -284,11 +285,11 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => {
|
|||
groupBy: {tags: ['k1'], time: null},
|
||||
},
|
||||
}
|
||||
const action = groupByTag(queryId, 'k1')
|
||||
const action = groupByTag(queryID, 'k1')
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].groupBy).to.eql({
|
||||
expect(nextState[queryID].groupBy).to.eql({
|
||||
time: null,
|
||||
tags: [],
|
||||
})
|
||||
|
@ -298,14 +299,14 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => {
|
|||
describe('KAPA_TOGGLE_TAG_ACCEPTANCE', () => {
|
||||
it('it toggles areTagsAccepted', () => {
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId),
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
const action = toggleTagAcceptance(queryId)
|
||||
const action = toggleTagAcceptance(queryID)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].areTagsAccepted).to.equal(
|
||||
!initialState[queryId].areTagsAccepted
|
||||
expect(nextState[queryID].areTagsAccepted).to.equal(
|
||||
!initialState[queryID].areTagsAccepted
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -314,14 +315,28 @@ describe('Chronograf.Reducers.Kapacitor.queryConfigs', () => {
|
|||
it('applys the appropriate group by time', () => {
|
||||
const time = '100y'
|
||||
const initialState = {
|
||||
[queryId]: buildInitialState(queryId),
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
|
||||
const action = groupByTime(queryId, time)
|
||||
const action = groupByTime(queryID, time)
|
||||
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryId].groupBy.time).to.equal(time)
|
||||
expect(nextState[queryID].groupBy.time).to.equal(time)
|
||||
})
|
||||
})
|
||||
|
||||
describe('KAPA_TIME_SHIFT', () => {
|
||||
it('can shift the time', () => {
|
||||
const initialState = {
|
||||
[queryID]: buildInitialState(queryID),
|
||||
}
|
||||
|
||||
const shift = {quantity: 1, unit: 'd', duration: '1d'}
|
||||
const action = timeShift(queryID, shift)
|
||||
const nextState = reducer(initialState, action)
|
||||
|
||||
expect(nextState[queryID].shifts).to.deep.equal([shift])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
import {timeRangeType, shiftTimeRange} from 'shared/query/helpers'
|
||||
import moment from 'moment'
|
||||
import {
|
||||
INVALID,
|
||||
ABSOLUTE,
|
||||
INFLUXQL,
|
||||
RELATIVE_LOWER,
|
||||
RELATIVE_UPPER,
|
||||
} from 'shared/constants/timeRange'
|
||||
const format = INFLUXQL
|
||||
|
||||
describe('Shared.Query.Helpers', () => {
|
||||
describe('timeRangeTypes', () => {
|
||||
it('returns invalid if no upper and lower', () => {
|
||||
const upper = null
|
||||
const lower = null
|
||||
|
||||
const timeRange = {lower, upper}
|
||||
|
||||
expect(timeRangeType(timeRange)).to.equal(INVALID)
|
||||
})
|
||||
|
||||
it('can detect absolute type', () => {
|
||||
const tenMinutes = 600000
|
||||
const upper = Date.now()
|
||||
const lower = upper - tenMinutes
|
||||
|
||||
const timeRange = {lower, upper, format}
|
||||
|
||||
expect(timeRangeType(timeRange)).to.equal(ABSOLUTE)
|
||||
})
|
||||
|
||||
it('can detect exclusive relative lower', () => {
|
||||
const lower = 'now() - 15m'
|
||||
const upper = null
|
||||
|
||||
const timeRange = {lower, upper, format}
|
||||
|
||||
expect(timeRangeType(timeRange)).to.equal(RELATIVE_LOWER)
|
||||
})
|
||||
|
||||
it('can detect relative upper', () => {
|
||||
const upper = 'now()'
|
||||
const oneMinute = 60000
|
||||
const lower = Date.now() - oneMinute
|
||||
|
||||
const timeRange = {lower, upper, format}
|
||||
|
||||
expect(timeRangeType(timeRange)).to.equal(RELATIVE_UPPER)
|
||||
})
|
||||
})
|
||||
|
||||
describe('timeRangeShift', () => {
|
||||
it('can calculate the shift for absolute timeRanges', () => {
|
||||
const upper = Date.now()
|
||||
const oneMinute = 60000
|
||||
const lower = Date.now() - oneMinute
|
||||
const shift = {quantity: 7, unit: 'd'}
|
||||
const timeRange = {upper, lower}
|
||||
|
||||
const type = timeRangeType(timeRange)
|
||||
const actual = shiftTimeRange(timeRange, shift)
|
||||
const expected = {
|
||||
lower: `${lower} - 7d`,
|
||||
upper: `${upper} - 7d`,
|
||||
type: 'shifted',
|
||||
}
|
||||
|
||||
expect(type).to.equal(ABSOLUTE)
|
||||
expect(actual).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('can calculate the shift for relative lower timeRanges', () => {
|
||||
const shift = {quantity: 7, unit: 'd'}
|
||||
const lower = 'now() - 15m'
|
||||
const timeRange = {lower, upper: null}
|
||||
|
||||
const type = timeRangeType(timeRange)
|
||||
const actual = shiftTimeRange(timeRange, shift)
|
||||
const expected = {
|
||||
lower: `${lower} - 7d`,
|
||||
upper: `now() - 7d`,
|
||||
type: 'shifted',
|
||||
}
|
||||
|
||||
expect(type).to.equal(RELATIVE_LOWER)
|
||||
expect(actual).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('can calculate the shift for relative upper timeRanges', () => {
|
||||
const upper = Date.now()
|
||||
const oneMinute = 60000
|
||||
const lower = Date.now() - oneMinute
|
||||
const shift = {quantity: 7, unit: 'd'}
|
||||
const timeRange = {upper, lower}
|
||||
|
||||
const type = timeRangeType(timeRange)
|
||||
const actual = shiftTimeRange(timeRange, shift)
|
||||
const expected = {
|
||||
lower: `${lower} - 7d`,
|
||||
upper: `${upper} - 7d`,
|
||||
type: 'shifted',
|
||||
}
|
||||
|
||||
expect(type).to.equal(ABSOLUTE)
|
||||
expect(actual).to.deep.equal(expected)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -228,11 +228,7 @@ describe('timeSeriesToDygraph', () => {
|
|||
]
|
||||
|
||||
const isInDataExplorer = true
|
||||
const actual = timeSeriesToDygraph(
|
||||
influxResponse,
|
||||
undefined,
|
||||
isInDataExplorer
|
||||
)
|
||||
const actual = timeSeriesToDygraph(influxResponse, isInDataExplorer)
|
||||
|
||||
const expected = {}
|
||||
|
||||
|
|
|
@ -1,54 +1,81 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import React, {PropTypes, Component} from 'react'
|
||||
|
||||
import DashboardsTable from 'src/dashboards/components/DashboardsTable'
|
||||
import SearchBar from 'src/hosts/components/SearchBar'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
|
||||
const DashboardsPageContents = ({
|
||||
dashboards,
|
||||
onDeleteDashboard,
|
||||
onCreateDashboard,
|
||||
dashboardLink,
|
||||
}) => {
|
||||
let tableHeader
|
||||
if (dashboards === null) {
|
||||
tableHeader = 'Loading Dashboards...'
|
||||
} else if (dashboards.length === 1) {
|
||||
tableHeader = '1 Dashboard'
|
||||
} else {
|
||||
tableHeader = `${dashboards.length} Dashboards`
|
||||
class DashboardsPageContents extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
searchTerm: '',
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<FancyScrollbar className="page-contents">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
||||
<h2 className="panel-title">
|
||||
{tableHeader}
|
||||
</h2>
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={onCreateDashboard}
|
||||
>
|
||||
<span className="icon plus" /> Create Dashboard
|
||||
</button>
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
<DashboardsTable
|
||||
dashboards={dashboards}
|
||||
onDeleteDashboard={onDeleteDashboard}
|
||||
onCreateDashboard={onCreateDashboard}
|
||||
dashboardLink={dashboardLink}
|
||||
/>
|
||||
filterDashboards = searchTerm => {
|
||||
this.setState({searchTerm})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
dashboards,
|
||||
onDeleteDashboard,
|
||||
onCreateDashboard,
|
||||
dashboardLink,
|
||||
} = this.props
|
||||
|
||||
let tableHeader
|
||||
if (dashboards === null) {
|
||||
tableHeader = 'Loading Dashboards...'
|
||||
} else if (dashboards.length === 1) {
|
||||
tableHeader = '1 Dashboard'
|
||||
} else {
|
||||
tableHeader = `${dashboards.length} Dashboards`
|
||||
}
|
||||
|
||||
const filteredDashboards = dashboards.filter(d =>
|
||||
d.name.includes(this.state.searchTerm)
|
||||
)
|
||||
|
||||
return (
|
||||
<FancyScrollbar className="page-contents">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
||||
<h2 className="panel-title">
|
||||
{tableHeader}
|
||||
</h2>
|
||||
<div className="u-flex u-ai-center dashboards-page--actions">
|
||||
<SearchBar
|
||||
placeholder="Filter by Name..."
|
||||
onSearch={this.filterDashboards}
|
||||
/>
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={onCreateDashboard}
|
||||
>
|
||||
<span className="icon plus" /> Create Dashboard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
<DashboardsTable
|
||||
dashboards={filteredDashboards}
|
||||
onDeleteDashboard={onDeleteDashboard}
|
||||
onCreateDashboard={onCreateDashboard}
|
||||
dashboardLink={dashboardLink}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
)
|
||||
</FancyScrollbar>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
|
|
|
@ -79,35 +79,29 @@ export const applyMasks = query => {
|
|||
const maskForWholeTemplates = '😸$1😸'
|
||||
return query.replace(matchWholeTemplates, maskForWholeTemplates)
|
||||
}
|
||||
|
||||
export const insertTempVar = (query, tempVar) => {
|
||||
return query.replace(MATCH_INCOMPLETE_TEMPLATES, tempVar)
|
||||
}
|
||||
|
||||
export const unMask = query => {
|
||||
return query.replace(/😸/g, ':')
|
||||
}
|
||||
|
||||
export const removeUnselectedTemplateValues = templates => {
|
||||
return templates.map(template => {
|
||||
const selectedValues = template.values.filter(value => value.selected)
|
||||
return {...template, values: selectedValues}
|
||||
})
|
||||
}
|
||||
|
||||
export const DISPLAY_OPTIONS = {
|
||||
LINEAR: 'linear',
|
||||
LOG: 'log',
|
||||
BASE_2: '2',
|
||||
BASE_10: '10',
|
||||
}
|
||||
|
||||
export const TOOLTIP_CONTENT = {
|
||||
FORMAT:
|
||||
'<p><strong>K/M/B</strong> = Thousand / Million / Billion<br/><strong>K/M/G</strong> = Kilo / Mega / Giga </p>',
|
||||
}
|
||||
|
||||
export const TYPE_QUERY_CONFIG = 'queryConfig'
|
||||
export const TYPE_SHIFTED = 'shifted queryConfig'
|
||||
export const TYPE_IFQL = 'ifql'
|
||||
|
||||
export const DASHBOARD_NAME_MAX_LENGTH = 50
|
||||
|
|
|
@ -39,13 +39,12 @@ class DashboardPage extends Component {
|
|||
selectedCell: null,
|
||||
isTemplating: false,
|
||||
zoomedTimeRange: {zoomedLower: null, zoomedUpper: null},
|
||||
names: [],
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const {
|
||||
params: {dashboardID, sourceID},
|
||||
params: {dashboardID},
|
||||
dashboardActions: {
|
||||
getDashboardsAsync,
|
||||
updateTempVarValues,
|
||||
|
@ -62,13 +61,6 @@ class DashboardPage extends Component {
|
|||
// Refresh and persists influxql generated template variable values
|
||||
await updateTempVarValues(source, dashboard)
|
||||
await putDashboardByID(dashboardID)
|
||||
|
||||
const names = dashboards.map(d => ({
|
||||
name: d.name,
|
||||
link: `/sources/${sourceID}/dashboards/${d.id}`,
|
||||
}))
|
||||
|
||||
this.setState({names})
|
||||
}
|
||||
|
||||
handleOpenTemplateManager = () => {
|
||||
|
@ -294,7 +286,11 @@ class DashboardPage extends Component {
|
|||
templatesIncludingDashTime = []
|
||||
}
|
||||
|
||||
const {selectedCell, isEditMode, isTemplating, names} = this.state
|
||||
const {selectedCell, isEditMode, isTemplating} = this.state
|
||||
const names = dashboards.map(d => ({
|
||||
name: d.name,
|
||||
link: `/sources/${sourceID}/dashboards/${d.id}`,
|
||||
}))
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import React, {PropTypes, Component} from 'react'
|
||||
import {withRouter} from 'react-router'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
@ -11,40 +11,20 @@ import {getDashboardsAsync, deleteDashboardAsync} from 'src/dashboards/actions'
|
|||
|
||||
import {NEW_DASHBOARD} from 'src/dashboards/constants'
|
||||
|
||||
const {arrayOf, func, string, shape} = PropTypes
|
||||
|
||||
const DashboardsPage = React.createClass({
|
||||
propTypes: {
|
||||
source: shape({
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
type: string,
|
||||
links: shape({
|
||||
proxy: string.isRequired,
|
||||
}).isRequired,
|
||||
telegraf: string.isRequired,
|
||||
}),
|
||||
router: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
handleGetDashboards: func.isRequired,
|
||||
handleDeleteDashboard: func.isRequired,
|
||||
dashboards: arrayOf(shape()),
|
||||
},
|
||||
|
||||
class DashboardsPage extends Component {
|
||||
componentDidMount() {
|
||||
this.props.handleGetDashboards()
|
||||
},
|
||||
}
|
||||
|
||||
async handleCreateDashbord() {
|
||||
const {source: {id}, router: {push}} = this.props
|
||||
const {data} = await createDashboard(NEW_DASHBOARD)
|
||||
push(`/sources/${id}/dashboards/${data.id}`)
|
||||
},
|
||||
}
|
||||
|
||||
handleDeleteDashboard(dashboard) {
|
||||
this.props.handleDeleteDashboard(dashboard)
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
const {dashboards} = this.props
|
||||
|
@ -61,8 +41,28 @@ const DashboardsPage = React.createClass({
|
|||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, string, shape} = PropTypes
|
||||
|
||||
DashboardsPage.propTypes = {
|
||||
source: shape({
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
type: string,
|
||||
links: shape({
|
||||
proxy: string.isRequired,
|
||||
}).isRequired,
|
||||
telegraf: string.isRequired,
|
||||
}),
|
||||
router: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
handleGetDashboards: func.isRequired,
|
||||
handleDeleteDashboard: func.isRequired,
|
||||
dashboards: arrayOf(shape()),
|
||||
}
|
||||
|
||||
const mapStateToProps = ({dashboardUI: {dashboards, dashboard}}) => ({
|
||||
dashboards,
|
||||
|
|
|
@ -18,26 +18,26 @@ export const deleteQuery = queryID => ({
|
|||
},
|
||||
})
|
||||
|
||||
export const toggleField = (queryId, fieldFunc) => ({
|
||||
export const toggleField = (queryID, fieldFunc) => ({
|
||||
type: 'DE_TOGGLE_FIELD',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
fieldFunc,
|
||||
},
|
||||
})
|
||||
|
||||
export const groupByTime = (queryId, time) => ({
|
||||
export const groupByTime = (queryID, time) => ({
|
||||
type: 'DE_GROUP_BY_TIME',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
time,
|
||||
},
|
||||
})
|
||||
|
||||
export const fill = (queryId, value) => ({
|
||||
export const fill = (queryID, value) => ({
|
||||
type: 'DE_FILL',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
value,
|
||||
},
|
||||
})
|
||||
|
@ -51,44 +51,44 @@ export const removeFuncs = (queryID, fields, groupBy) => ({
|
|||
},
|
||||
})
|
||||
|
||||
export const applyFuncsToField = (queryId, fieldFunc, groupBy) => ({
|
||||
export const applyFuncsToField = (queryID, fieldFunc, groupBy) => ({
|
||||
type: 'DE_APPLY_FUNCS_TO_FIELD',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
fieldFunc,
|
||||
groupBy,
|
||||
},
|
||||
})
|
||||
|
||||
export const chooseTag = (queryId, tag) => ({
|
||||
export const chooseTag = (queryID, tag) => ({
|
||||
type: 'DE_CHOOSE_TAG',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
tag,
|
||||
},
|
||||
})
|
||||
|
||||
export const chooseNamespace = (queryId, {database, retentionPolicy}) => ({
|
||||
export const chooseNamespace = (queryID, {database, retentionPolicy}) => ({
|
||||
type: 'DE_CHOOSE_NAMESPACE',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
database,
|
||||
retentionPolicy,
|
||||
},
|
||||
})
|
||||
|
||||
export const chooseMeasurement = (queryId, measurement) => ({
|
||||
export const chooseMeasurement = (queryID, measurement) => ({
|
||||
type: 'DE_CHOOSE_MEASUREMENT',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
measurement,
|
||||
},
|
||||
})
|
||||
|
||||
export const editRawText = (queryId, rawText) => ({
|
||||
export const editRawText = (queryID, rawText) => ({
|
||||
type: 'DE_EDIT_RAW_TEXT',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
rawText,
|
||||
},
|
||||
})
|
||||
|
@ -100,18 +100,18 @@ export const setTimeRange = bounds => ({
|
|||
},
|
||||
})
|
||||
|
||||
export const groupByTag = (queryId, tagKey) => ({
|
||||
export const groupByTag = (queryID, tagKey) => ({
|
||||
type: 'DE_GROUP_BY_TAG',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
tagKey,
|
||||
},
|
||||
})
|
||||
|
||||
export const toggleTagAcceptance = queryId => ({
|
||||
export const toggleTagAcceptance = queryID => ({
|
||||
type: 'DE_TOGGLE_TAG_ACCEPTANCE',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -147,6 +147,14 @@ export const editQueryStatus = (queryID, status) => ({
|
|||
},
|
||||
})
|
||||
|
||||
export const timeShift = (queryID, shift) => ({
|
||||
type: 'DE_TIME_SHIFT',
|
||||
payload: {
|
||||
queryID,
|
||||
shift,
|
||||
},
|
||||
})
|
||||
|
||||
// Async actions
|
||||
export const editRawTextAsync = (url, id, text) => async dispatch => {
|
||||
try {
|
||||
|
|
|
@ -7,8 +7,6 @@ import Dropdown from 'shared/components/Dropdown'
|
|||
|
||||
import {AUTO_GROUP_BY} from 'shared/constants'
|
||||
|
||||
const {func, string, shape} = PropTypes
|
||||
|
||||
const isInRuleBuilder = pathname => pathname.includes('alert-rules')
|
||||
const isInDataExplorer = pathname => pathname.includes('data-explorer')
|
||||
|
||||
|
@ -37,6 +35,8 @@ const GroupByTimeDropdown = ({
|
|||
/>
|
||||
</div>
|
||||
|
||||
const {func, string, shape} = PropTypes
|
||||
|
||||
GroupByTimeDropdown.propTypes = {
|
||||
location: shape({
|
||||
pathname: string.isRequired,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import buildInfluxQLQuery from 'utils/influxql'
|
||||
import classnames from 'classnames'
|
||||
import VisHeader from 'src/data_explorer/components/VisHeader'
|
||||
import VisView from 'src/data_explorer/components/VisView'
|
||||
import {GRAPH, TABLE} from 'shared/constants'
|
||||
import buildQueries from 'utils/buildQueriesForGraphs'
|
||||
import _ from 'lodash'
|
||||
|
||||
const META_QUERY_REGEX = /^show/i
|
||||
const META_QUERY_REGEX = /^(show|create|drop)/i
|
||||
|
||||
class Visualization extends Component {
|
||||
constructor(props) {
|
||||
|
@ -61,19 +61,11 @@ class Visualization extends Component {
|
|||
resizerBottomHeight,
|
||||
errorThrown,
|
||||
} = this.props
|
||||
|
||||
const {source: {links: {proxy}}} = this.context
|
||||
const {view} = this.state
|
||||
|
||||
const statements = queryConfigs.map(query => {
|
||||
const text =
|
||||
query.rawText || buildInfluxQLQuery(query.range || timeRange, query)
|
||||
return {text, id: query.id, queryConfig: query}
|
||||
})
|
||||
|
||||
const queries = statements.filter(s => s.text !== null).map(s => {
|
||||
return {host: [proxy], text: s.text, id: s.id, queryConfig: s.queryConfig}
|
||||
})
|
||||
|
||||
const queries = buildQueries(proxy, queryConfigs, timeRange)
|
||||
const activeQuery = queries[activeQueryIndex]
|
||||
const defaultQuery = queries[0]
|
||||
const query = activeQuery || defaultQuery
|
||||
|
@ -81,12 +73,12 @@ class Visualization extends Component {
|
|||
return (
|
||||
<div className="graph" style={{height}}>
|
||||
<VisHeader
|
||||
views={views}
|
||||
view={view}
|
||||
onToggleView={this.handleToggleView}
|
||||
name={cellName}
|
||||
views={views}
|
||||
query={query}
|
||||
name={cellName}
|
||||
errorThrown={errorThrown}
|
||||
onToggleView={this.handleToggleView}
|
||||
/>
|
||||
<div
|
||||
className={classnames({
|
||||
|
|
|
@ -3,6 +3,7 @@ import _ from 'lodash'
|
|||
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
||||
import {
|
||||
fill,
|
||||
timeShift,
|
||||
chooseTag,
|
||||
groupByTag,
|
||||
removeFuncs,
|
||||
|
@ -20,24 +21,24 @@ import {
|
|||
const queryConfigs = (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
case 'DE_CHOOSE_NAMESPACE': {
|
||||
const {queryId, database, retentionPolicy} = action.payload
|
||||
const nextQueryConfig = chooseNamespace(state[queryId], {
|
||||
const {queryID, database, retentionPolicy} = action.payload
|
||||
const nextQueryConfig = chooseNamespace(state[queryID], {
|
||||
database,
|
||||
retentionPolicy,
|
||||
})
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: Object.assign(nextQueryConfig, {rawText: null}),
|
||||
[queryID]: Object.assign(nextQueryConfig, {rawText: null}),
|
||||
})
|
||||
}
|
||||
|
||||
case 'DE_CHOOSE_MEASUREMENT': {
|
||||
const {queryId, measurement} = action.payload
|
||||
const nextQueryConfig = chooseMeasurement(state[queryId], measurement)
|
||||
const {queryID, measurement} = action.payload
|
||||
const nextQueryConfig = chooseMeasurement(state[queryID], measurement)
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: Object.assign(nextQueryConfig, {
|
||||
rawText: state[queryId].rawText,
|
||||
[queryID]: Object.assign(nextQueryConfig, {
|
||||
rawText: state[queryID].rawText,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
@ -64,78 +65,78 @@ const queryConfigs = (state = {}, action) => {
|
|||
}
|
||||
|
||||
case 'DE_EDIT_RAW_TEXT': {
|
||||
const {queryId, rawText} = action.payload
|
||||
const nextQueryConfig = editRawText(state[queryId], rawText)
|
||||
const {queryID, rawText} = action.payload
|
||||
const nextQueryConfig = editRawText(state[queryID], rawText)
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: nextQueryConfig,
|
||||
[queryID]: nextQueryConfig,
|
||||
})
|
||||
}
|
||||
|
||||
case 'DE_GROUP_BY_TIME': {
|
||||
const {queryId, time} = action.payload
|
||||
const nextQueryConfig = groupByTime(state[queryId], time)
|
||||
const {queryID, time} = action.payload
|
||||
const nextQueryConfig = groupByTime(state[queryID], time)
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: nextQueryConfig,
|
||||
[queryID]: nextQueryConfig,
|
||||
})
|
||||
}
|
||||
|
||||
case 'DE_TOGGLE_TAG_ACCEPTANCE': {
|
||||
const {queryId} = action.payload
|
||||
const nextQueryConfig = toggleTagAcceptance(state[queryId])
|
||||
const {queryID} = action.payload
|
||||
const nextQueryConfig = toggleTagAcceptance(state[queryID])
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: nextQueryConfig,
|
||||
[queryID]: nextQueryConfig,
|
||||
})
|
||||
}
|
||||
|
||||
case 'DE_TOGGLE_FIELD': {
|
||||
const {queryId, fieldFunc} = action.payload
|
||||
const nextQueryConfig = toggleField(state[queryId], fieldFunc)
|
||||
const {queryID, fieldFunc} = action.payload
|
||||
const nextQueryConfig = toggleField(state[queryID], fieldFunc)
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: {...nextQueryConfig, rawText: null},
|
||||
[queryID]: {...nextQueryConfig, rawText: null},
|
||||
})
|
||||
}
|
||||
|
||||
case 'DE_APPLY_FUNCS_TO_FIELD': {
|
||||
const {queryId, fieldFunc, groupBy} = action.payload
|
||||
const {queryID, fieldFunc, groupBy} = action.payload
|
||||
const nextQueryConfig = applyFuncsToField(
|
||||
state[queryId],
|
||||
state[queryID],
|
||||
fieldFunc,
|
||||
groupBy
|
||||
)
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: nextQueryConfig,
|
||||
[queryID]: nextQueryConfig,
|
||||
})
|
||||
}
|
||||
|
||||
case 'DE_CHOOSE_TAG': {
|
||||
const {queryId, tag} = action.payload
|
||||
const nextQueryConfig = chooseTag(state[queryId], tag)
|
||||
const {queryID, tag} = action.payload
|
||||
const nextQueryConfig = chooseTag(state[queryID], tag)
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: nextQueryConfig,
|
||||
[queryID]: nextQueryConfig,
|
||||
})
|
||||
}
|
||||
|
||||
case 'DE_GROUP_BY_TAG': {
|
||||
const {queryId, tagKey} = action.payload
|
||||
const nextQueryConfig = groupByTag(state[queryId], tagKey)
|
||||
const {queryID, tagKey} = action.payload
|
||||
const nextQueryConfig = groupByTag(state[queryID], tagKey)
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: nextQueryConfig,
|
||||
[queryID]: nextQueryConfig,
|
||||
})
|
||||
}
|
||||
|
||||
case 'DE_FILL': {
|
||||
const {queryId, value} = action.payload
|
||||
const nextQueryConfig = fill(state[queryId], value)
|
||||
const {queryID, value} = action.payload
|
||||
const nextQueryConfig = fill(state[queryID], value)
|
||||
|
||||
return {
|
||||
...state,
|
||||
[queryId]: nextQueryConfig,
|
||||
[queryID]: nextQueryConfig,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,6 +172,13 @@ const queryConfigs = (state = {}, action) => {
|
|||
|
||||
return {...state, [queryID]: nextQuery}
|
||||
}
|
||||
|
||||
case 'DE_TIME_SHIFT': {
|
||||
const {queryID, shift} = action.payload
|
||||
const nextQuery = timeShift(state[queryID], shift)
|
||||
|
||||
return {...state, [queryID]: nextQuery}
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
|
|
@ -103,7 +103,10 @@ class HostsTable extends Component {
|
|||
<h2 className="panel-title">
|
||||
{hostsTitle}
|
||||
</h2>
|
||||
<SearchBar onSearch={this.updateSearchTerm} />
|
||||
<SearchBar
|
||||
placeholder="Filter by Host..."
|
||||
onSearch={this.updateSearchTerm}
|
||||
/>
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
{hostCount > 0 && !hostsError.length
|
||||
|
|
|
@ -10,8 +10,7 @@ class SearchBar extends Component {
|
|||
}
|
||||
|
||||
componentWillMount() {
|
||||
const waitPeriod = 300
|
||||
this.handleSearch = _.debounce(this.handleSearch, waitPeriod)
|
||||
this.handleSearch = _.debounce(this.handleSearch, 50)
|
||||
}
|
||||
|
||||
handleSearch = () => {
|
||||
|
@ -23,12 +22,13 @@ class SearchBar extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {placeholder} = this.props
|
||||
return (
|
||||
<div className="users__search-widget input-group">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Filter by Host..."
|
||||
placeholder={placeholder}
|
||||
ref="searchInput"
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
@ -40,10 +40,11 @@ class SearchBar extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const {func} = PropTypes
|
||||
const {func, string} = PropTypes
|
||||
|
||||
SearchBar.propTypes = {
|
||||
onSearch: func.isRequired,
|
||||
placeholder: string.isRequired,
|
||||
}
|
||||
|
||||
export default SearchBar
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import React, {PropTypes, Component} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import HostsTable from 'src/hosts/components/HostsTable'
|
||||
|
@ -7,27 +7,16 @@ import SourceIndicator from 'shared/components/SourceIndicator'
|
|||
|
||||
import {getCpuAndLoadForHosts, getMappings, getAppsForHosts} from '../apis'
|
||||
|
||||
export const HostsPage = React.createClass({
|
||||
propTypes: {
|
||||
source: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
type: PropTypes.string, // 'influx-enterprise'
|
||||
links: PropTypes.shape({
|
||||
proxy: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
telegraf: PropTypes.string.isRequired,
|
||||
}),
|
||||
addFlashMessage: PropTypes.func,
|
||||
},
|
||||
class HostsPage extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
this.state = {
|
||||
hosts: {},
|
||||
hostsLoading: true,
|
||||
hostsError: '',
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {source, addFlashMessage} = this.props
|
||||
|
@ -71,7 +60,7 @@ export const HostsPage = React.createClass({
|
|||
// (like with a bogus proxy link). We should provide better messaging to the user in this catch after that's fixed.
|
||||
console.error(reason) // eslint-disable-line no-console
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
const {source} = this.props
|
||||
|
@ -104,7 +93,22 @@ export const HostsPage = React.createClass({
|
|||
</FancyScrollbar>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const {func, shape, string} = PropTypes
|
||||
|
||||
HostsPage.propTypes = {
|
||||
source: shape({
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
type: string, // 'influx-enterprise'
|
||||
links: shape({
|
||||
proxy: string.isRequired,
|
||||
}).isRequired,
|
||||
telegraf: string.isRequired,
|
||||
}),
|
||||
addFlashMessage: func,
|
||||
}
|
||||
|
||||
export default HostsPage
|
||||
|
|
|
@ -1,63 +1,63 @@
|
|||
export const chooseNamespace = (queryId, {database, retentionPolicy}) => ({
|
||||
export const chooseNamespace = (queryID, {database, retentionPolicy}) => ({
|
||||
type: 'KAPA_CHOOSE_NAMESPACE',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
database,
|
||||
retentionPolicy,
|
||||
},
|
||||
})
|
||||
|
||||
export const chooseMeasurement = (queryId, measurement) => ({
|
||||
export const chooseMeasurement = (queryID, measurement) => ({
|
||||
type: 'KAPA_CHOOSE_MEASUREMENT',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
measurement,
|
||||
},
|
||||
})
|
||||
|
||||
export const chooseTag = (queryId, tag) => ({
|
||||
export const chooseTag = (queryID, tag) => ({
|
||||
type: 'KAPA_CHOOSE_TAG',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
tag,
|
||||
},
|
||||
})
|
||||
|
||||
export const groupByTag = (queryId, tagKey) => ({
|
||||
export const groupByTag = (queryID, tagKey) => ({
|
||||
type: 'KAPA_GROUP_BY_TAG',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
tagKey,
|
||||
},
|
||||
})
|
||||
|
||||
export const toggleTagAcceptance = queryId => ({
|
||||
export const toggleTagAcceptance = queryID => ({
|
||||
type: 'KAPA_TOGGLE_TAG_ACCEPTANCE',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
},
|
||||
})
|
||||
|
||||
export const toggleField = (queryId, fieldFunc) => ({
|
||||
export const toggleField = (queryID, fieldFunc) => ({
|
||||
type: 'KAPA_TOGGLE_FIELD',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
fieldFunc,
|
||||
},
|
||||
})
|
||||
|
||||
export const applyFuncsToField = (queryId, fieldFunc) => ({
|
||||
export const applyFuncsToField = (queryID, fieldFunc) => ({
|
||||
type: 'KAPA_APPLY_FUNCS_TO_FIELD',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
fieldFunc,
|
||||
},
|
||||
})
|
||||
|
||||
export const groupByTime = (queryId, time) => ({
|
||||
export const groupByTime = (queryID, time) => ({
|
||||
type: 'KAPA_GROUP_BY_TIME',
|
||||
payload: {
|
||||
queryId,
|
||||
queryID,
|
||||
time,
|
||||
},
|
||||
})
|
||||
|
@ -69,3 +69,11 @@ export const removeFuncs = (queryID, fields) => ({
|
|||
fields,
|
||||
},
|
||||
})
|
||||
|
||||
export const timeShift = (queryID, shift) => ({
|
||||
type: 'KAPA_TIME_SHIFT',
|
||||
payload: {
|
||||
queryID,
|
||||
shift,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -66,7 +66,7 @@ export const getRule = (kapacitor, ruleID) => async dispatch => {
|
|||
}
|
||||
}
|
||||
|
||||
export function loadDefaultRule() {
|
||||
export const loadDefaultRule = () => {
|
||||
return dispatch => {
|
||||
const queryID = uuid.v4()
|
||||
dispatch({
|
||||
|
@ -88,15 +88,13 @@ export const fetchRules = kapacitor => async dispatch => {
|
|||
}
|
||||
}
|
||||
|
||||
export function chooseTrigger(ruleID, trigger) {
|
||||
return {
|
||||
type: 'CHOOSE_TRIGGER',
|
||||
payload: {
|
||||
ruleID,
|
||||
trigger,
|
||||
},
|
||||
}
|
||||
}
|
||||
export const chooseTrigger = (ruleID, trigger) => ({
|
||||
type: 'CHOOSE_TRIGGER',
|
||||
payload: {
|
||||
ruleID,
|
||||
trigger,
|
||||
},
|
||||
})
|
||||
|
||||
export const addEvery = (ruleID, frequency) => ({
|
||||
type: 'ADD_EVERY',
|
||||
|
@ -113,36 +111,30 @@ export const removeEvery = ruleID => ({
|
|||
},
|
||||
})
|
||||
|
||||
export function updateRuleValues(ruleID, trigger, values) {
|
||||
return {
|
||||
type: 'UPDATE_RULE_VALUES',
|
||||
payload: {
|
||||
ruleID,
|
||||
trigger,
|
||||
values,
|
||||
},
|
||||
}
|
||||
}
|
||||
export const updateRuleValues = (ruleID, trigger, values) => ({
|
||||
type: 'UPDATE_RULE_VALUES',
|
||||
payload: {
|
||||
ruleID,
|
||||
trigger,
|
||||
values,
|
||||
},
|
||||
})
|
||||
|
||||
export function updateMessage(ruleID, message) {
|
||||
return {
|
||||
type: 'UPDATE_RULE_MESSAGE',
|
||||
payload: {
|
||||
ruleID,
|
||||
message,
|
||||
},
|
||||
}
|
||||
}
|
||||
export const updateMessage = (ruleID, message) => ({
|
||||
type: 'UPDATE_RULE_MESSAGE',
|
||||
payload: {
|
||||
ruleID,
|
||||
message,
|
||||
},
|
||||
})
|
||||
|
||||
export function updateDetails(ruleID, details) {
|
||||
return {
|
||||
type: 'UPDATE_RULE_DETAILS',
|
||||
payload: {
|
||||
ruleID,
|
||||
details,
|
||||
},
|
||||
}
|
||||
}
|
||||
export const updateDetails = (ruleID, details) => ({
|
||||
type: 'UPDATE_RULE_DETAILS',
|
||||
payload: {
|
||||
ruleID,
|
||||
details,
|
||||
},
|
||||
})
|
||||
|
||||
export const updateAlertProperty = (ruleID, alertNodeName, alertProperty) => ({
|
||||
type: 'UPDATE_RULE_ALERT_PROPERTY',
|
||||
|
@ -153,87 +145,73 @@ export const updateAlertProperty = (ruleID, alertNodeName, alertProperty) => ({
|
|||
},
|
||||
})
|
||||
|
||||
export function updateAlerts(ruleID, alerts) {
|
||||
return {
|
||||
type: 'UPDATE_RULE_ALERTS',
|
||||
payload: {
|
||||
ruleID,
|
||||
alerts,
|
||||
},
|
||||
}
|
||||
export const updateAlerts = (ruleID, alerts) => ({
|
||||
type: 'UPDATE_RULE_ALERTS',
|
||||
payload: {
|
||||
ruleID,
|
||||
alerts,
|
||||
},
|
||||
})
|
||||
|
||||
export const updateAlertNodes = (ruleID, alertNodeName, alertNodesText) => ({
|
||||
type: 'UPDATE_RULE_ALERT_NODES',
|
||||
payload: {
|
||||
ruleID,
|
||||
alertNodeName,
|
||||
alertNodesText,
|
||||
},
|
||||
})
|
||||
|
||||
export const updateRuleName = (ruleID, name) => ({
|
||||
type: 'UPDATE_RULE_NAME',
|
||||
payload: {
|
||||
ruleID,
|
||||
name,
|
||||
},
|
||||
})
|
||||
|
||||
export const deleteRuleSuccess = ruleID => ({
|
||||
type: 'DELETE_RULE_SUCCESS',
|
||||
payload: {
|
||||
ruleID,
|
||||
},
|
||||
})
|
||||
|
||||
export const updateRuleStatusSuccess = (ruleID, status) => ({
|
||||
type: 'UPDATE_RULE_STATUS_SUCCESS',
|
||||
payload: {
|
||||
ruleID,
|
||||
status,
|
||||
},
|
||||
})
|
||||
|
||||
export const deleteRule = rule => dispatch => {
|
||||
deleteRuleAPI(rule)
|
||||
.then(() => {
|
||||
dispatch(deleteRuleSuccess(rule.id))
|
||||
dispatch(
|
||||
publishNotification('success', `${rule.name} deleted successfully`)
|
||||
)
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch(
|
||||
publishNotification('error', `${rule.name} could not be deleted`)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export function updateAlertNodes(ruleID, alertNodeName, alertNodesText) {
|
||||
return {
|
||||
type: 'UPDATE_RULE_ALERT_NODES',
|
||||
payload: {
|
||||
ruleID,
|
||||
alertNodeName,
|
||||
alertNodesText,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function updateRuleName(ruleID, name) {
|
||||
return {
|
||||
type: 'UPDATE_RULE_NAME',
|
||||
payload: {
|
||||
ruleID,
|
||||
name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteRuleSuccess(ruleID) {
|
||||
return {
|
||||
type: 'DELETE_RULE_SUCCESS',
|
||||
payload: {
|
||||
ruleID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function updateRuleStatusSuccess(ruleID, status) {
|
||||
return {
|
||||
type: 'UPDATE_RULE_STATUS_SUCCESS',
|
||||
payload: {
|
||||
ruleID,
|
||||
status,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteRule(rule) {
|
||||
return dispatch => {
|
||||
deleteRuleAPI(rule)
|
||||
.then(() => {
|
||||
dispatch(deleteRuleSuccess(rule.id))
|
||||
dispatch(
|
||||
publishNotification('success', `${rule.name} deleted successfully`)
|
||||
)
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch(
|
||||
publishNotification('error', `${rule.name} could not be deleted`)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function updateRuleStatus(rule, status) {
|
||||
return dispatch => {
|
||||
updateRuleStatusAPI(rule, status)
|
||||
.then(() => {
|
||||
dispatch(
|
||||
publishNotification('success', `${rule.name} ${status} successfully`)
|
||||
)
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch(
|
||||
publishNotification('error', `${rule.name} could not be ${status}`)
|
||||
)
|
||||
})
|
||||
}
|
||||
export const updateRuleStatus = (rule, status) => dispatch => {
|
||||
updateRuleStatusAPI(rule, status)
|
||||
.then(() => {
|
||||
dispatch(
|
||||
publishNotification('success', `${rule.name} ${status} successfully`)
|
||||
)
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch(
|
||||
publishNotification('error', `${rule.name} could not be ${status}`)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const createTask = (
|
||||
|
|
|
@ -61,13 +61,13 @@ class KapacitorRulePage extends Component {
|
|||
render() {
|
||||
const {
|
||||
rules,
|
||||
queryConfigs,
|
||||
params,
|
||||
ruleActions,
|
||||
source,
|
||||
queryConfigActions,
|
||||
addFlashMessage,
|
||||
router,
|
||||
ruleActions,
|
||||
queryConfigs,
|
||||
addFlashMessage,
|
||||
queryConfigActions,
|
||||
} = this.props
|
||||
const {enabledAlerts, kapacitor} = this.state
|
||||
const rule = this.isEditing()
|
||||
|
@ -80,17 +80,17 @@ class KapacitorRulePage extends Component {
|
|||
}
|
||||
return (
|
||||
<KapacitorRule
|
||||
source={source}
|
||||
rule={rule}
|
||||
query={query}
|
||||
queryConfigs={queryConfigs}
|
||||
queryConfigActions={queryConfigActions}
|
||||
ruleActions={ruleActions}
|
||||
addFlashMessage={addFlashMessage}
|
||||
enabledAlerts={enabledAlerts}
|
||||
isEditing={this.isEditing()}
|
||||
router={router}
|
||||
source={source}
|
||||
kapacitor={kapacitor}
|
||||
ruleActions={ruleActions}
|
||||
queryConfigs={queryConfigs}
|
||||
isEditing={this.isEditing()}
|
||||
enabledAlerts={enabledAlerts}
|
||||
addFlashMessage={addFlashMessage}
|
||||
queryConfigActions={queryConfigActions}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
||||
import {
|
||||
applyFuncsToField,
|
||||
chooseMeasurement,
|
||||
chooseNamespace,
|
||||
timeShift,
|
||||
chooseTag,
|
||||
groupByTag,
|
||||
groupByTime,
|
||||
removeFuncs,
|
||||
chooseNamespace,
|
||||
toggleKapaField,
|
||||
applyFuncsToField,
|
||||
chooseMeasurement,
|
||||
toggleTagAcceptance,
|
||||
} from 'src/utils/queryTransitions'
|
||||
|
||||
|
@ -34,9 +35,9 @@ const queryConfigs = (state = {}, action) => {
|
|||
}
|
||||
|
||||
case 'KAPA_CHOOSE_NAMESPACE': {
|
||||
const {queryId, database, retentionPolicy} = action.payload
|
||||
const {queryID, database, retentionPolicy} = action.payload
|
||||
const nextQueryConfig = chooseNamespace(
|
||||
state[queryId],
|
||||
state[queryID],
|
||||
{
|
||||
database,
|
||||
retentionPolicy,
|
||||
|
@ -45,75 +46,75 @@ const queryConfigs = (state = {}, action) => {
|
|||
)
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: Object.assign(nextQueryConfig, {rawText: null}),
|
||||
[queryID]: Object.assign(nextQueryConfig, {rawText: null}),
|
||||
})
|
||||
}
|
||||
|
||||
case 'KAPA_CHOOSE_MEASUREMENT': {
|
||||
const {queryId, measurement} = action.payload
|
||||
const {queryID, measurement} = action.payload
|
||||
const nextQueryConfig = chooseMeasurement(
|
||||
state[queryId],
|
||||
state[queryID],
|
||||
measurement,
|
||||
IS_KAPACITOR_RULE
|
||||
)
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: Object.assign(nextQueryConfig, {
|
||||
rawText: state[queryId].rawText,
|
||||
[queryID]: Object.assign(nextQueryConfig, {
|
||||
rawText: state[queryID].rawText,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
case 'KAPA_CHOOSE_TAG': {
|
||||
const {queryId, tag} = action.payload
|
||||
const nextQueryConfig = chooseTag(state[queryId], tag)
|
||||
const {queryID, tag} = action.payload
|
||||
const nextQueryConfig = chooseTag(state[queryID], tag)
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: nextQueryConfig,
|
||||
[queryID]: nextQueryConfig,
|
||||
})
|
||||
}
|
||||
|
||||
case 'KAPA_GROUP_BY_TAG': {
|
||||
const {queryId, tagKey} = action.payload
|
||||
const nextQueryConfig = groupByTag(state[queryId], tagKey)
|
||||
const {queryID, tagKey} = action.payload
|
||||
const nextQueryConfig = groupByTag(state[queryID], tagKey)
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: nextQueryConfig,
|
||||
[queryID]: nextQueryConfig,
|
||||
})
|
||||
}
|
||||
|
||||
case 'KAPA_TOGGLE_TAG_ACCEPTANCE': {
|
||||
const {queryId} = action.payload
|
||||
const nextQueryConfig = toggleTagAcceptance(state[queryId])
|
||||
const {queryID} = action.payload
|
||||
const nextQueryConfig = toggleTagAcceptance(state[queryID])
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: nextQueryConfig,
|
||||
[queryID]: nextQueryConfig,
|
||||
})
|
||||
}
|
||||
|
||||
case 'KAPA_TOGGLE_FIELD': {
|
||||
const {queryId, fieldFunc} = action.payload
|
||||
const nextQueryConfig = toggleKapaField(state[queryId], fieldFunc)
|
||||
const {queryID, fieldFunc} = action.payload
|
||||
const nextQueryConfig = toggleKapaField(state[queryID], fieldFunc)
|
||||
|
||||
return {...state, [queryId]: {...nextQueryConfig, rawText: null}}
|
||||
return {...state, [queryID]: {...nextQueryConfig, rawText: null}}
|
||||
}
|
||||
|
||||
case 'KAPA_APPLY_FUNCS_TO_FIELD': {
|
||||
const {queryId, fieldFunc} = action.payload
|
||||
const {groupBy} = state[queryId]
|
||||
const nextQueryConfig = applyFuncsToField(state[queryId], fieldFunc, {
|
||||
const {queryID, fieldFunc} = action.payload
|
||||
const {groupBy} = state[queryID]
|
||||
const nextQueryConfig = applyFuncsToField(state[queryID], fieldFunc, {
|
||||
...groupBy,
|
||||
time: groupBy.time ? groupBy.time : '10s',
|
||||
})
|
||||
|
||||
return {...state, [queryId]: nextQueryConfig}
|
||||
return {...state, [queryID]: nextQueryConfig}
|
||||
}
|
||||
|
||||
case 'KAPA_GROUP_BY_TIME': {
|
||||
const {queryId, time} = action.payload
|
||||
const nextQueryConfig = groupByTime(state[queryId], time)
|
||||
const {queryID, time} = action.payload
|
||||
const nextQueryConfig = groupByTime(state[queryID], time)
|
||||
|
||||
return Object.assign({}, state, {
|
||||
[queryId]: nextQueryConfig,
|
||||
[queryID]: nextQueryConfig,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -124,6 +125,13 @@ const queryConfigs = (state = {}, action) => {
|
|||
// fields with no functions cannot have a group by time
|
||||
return {...state, [queryID]: nextQuery}
|
||||
}
|
||||
|
||||
case 'KAPA_TIME_SHIFT': {
|
||||
const {queryID, shift} = action.payload
|
||||
const nextQuery = timeShift(state[queryID], shift)
|
||||
|
||||
return {...state, [queryID]: nextQuery}
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@ export function showQueries(source, db) {
|
|||
return proxy({source, query, db})
|
||||
}
|
||||
|
||||
export function killQuery(source, queryId) {
|
||||
const query = `KILL QUERY ${queryId}`
|
||||
export function killQuery(source, queryID) {
|
||||
const query = `KILL QUERY ${queryID}`
|
||||
|
||||
return proxy({source, query})
|
||||
}
|
||||
|
|
|
@ -45,12 +45,17 @@ const DatabaseList = React.createClass({
|
|||
this.getDbRp()
|
||||
},
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (_.isEqual(prevProps.querySource, this.props.querySource)) {
|
||||
return
|
||||
}
|
||||
componentDidUpdate({querySource: prevSource, query: prevQuery}) {
|
||||
const {querySource: nextSource, query: nextQuery} = this.props
|
||||
const differentSource = !_.isEqual(prevSource, nextSource)
|
||||
|
||||
this.getDbRp()
|
||||
const newMetaQuery =
|
||||
prevQuery.rawText !== nextQuery.rawText &&
|
||||
nextQuery.rawText.match(/^(create|drop)/i)
|
||||
|
||||
if (differentSource || newMetaQuery) {
|
||||
setTimeout(this.getDbRp, 100)
|
||||
}
|
||||
},
|
||||
|
||||
getDbRp() {
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import QueryOptions from 'shared/components/QueryOptions'
|
||||
import FieldListItem from 'src/data_explorer/components/FieldListItem'
|
||||
import GroupByTimeDropdown from 'src/data_explorer/components/GroupByTimeDropdown'
|
||||
import FillQuery from 'shared/components/FillQuery'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
|
||||
import {showFieldKeys} from 'shared/apis/metaQuery'
|
||||
|
@ -107,6 +106,10 @@ class FieldList extends Component {
|
|||
applyFuncsToField(fieldFunc, groupBy)
|
||||
}
|
||||
|
||||
handleTimeShift = shift => {
|
||||
this.props.onTimeShift(shift)
|
||||
}
|
||||
|
||||
_getFields = () => {
|
||||
const {database, measurement, retentionPolicy} = this.props.query
|
||||
const {source} = this.context
|
||||
|
@ -129,12 +132,11 @@ class FieldList extends Component {
|
|||
|
||||
render() {
|
||||
const {
|
||||
query: {database, measurement, fields = [], groupBy, fill},
|
||||
query: {database, measurement, fields = [], groupBy, fill, shifts},
|
||||
isKapacitorRule,
|
||||
} = this.props
|
||||
|
||||
const hasAggregates = numFunctions(fields) > 0
|
||||
const hasGroupByTime = groupBy.time
|
||||
const noDBorMeas = !database || !measurement
|
||||
|
||||
return (
|
||||
|
@ -142,16 +144,15 @@ class FieldList extends Component {
|
|||
<div className="query-builder--heading">
|
||||
<span>Fields</span>
|
||||
{hasAggregates
|
||||
? <div className="query-builder--groupby-fill-container">
|
||||
<GroupByTimeDropdown
|
||||
isOpen={!hasGroupByTime}
|
||||
selected={groupBy.time}
|
||||
onChooseGroupByTime={this.handleGroupByTime}
|
||||
/>
|
||||
{isKapacitorRule
|
||||
? null
|
||||
: <FillQuery value={fill} onChooseFill={this.handleFill} />}
|
||||
</div>
|
||||
? <QueryOptions
|
||||
fill={fill}
|
||||
shift={_.first(shifts)}
|
||||
groupBy={groupBy}
|
||||
onFill={this.handleFill}
|
||||
isKapacitorRule={isKapacitorRule}
|
||||
onTimeShift={this.handleTimeShift}
|
||||
onGroupByTime={this.handleGroupByTime}
|
||||
/>
|
||||
: null}
|
||||
</div>
|
||||
{noDBorMeas
|
||||
|
@ -192,7 +193,7 @@ class FieldList extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const {bool, func, shape, string} = PropTypes
|
||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||
|
||||
FieldList.defaultProps = {
|
||||
isKapacitorRule: false,
|
||||
|
@ -212,7 +213,15 @@ FieldList.propTypes = {
|
|||
database: string,
|
||||
retentionPolicy: string,
|
||||
measurement: string,
|
||||
shifts: arrayOf(
|
||||
shape({
|
||||
label: string,
|
||||
unit: string,
|
||||
quantity: string,
|
||||
})
|
||||
),
|
||||
}).isRequired,
|
||||
onTimeShift: func,
|
||||
onToggleField: func.isRequired,
|
||||
onGroupByTime: func.isRequired,
|
||||
onFill: func,
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, {Component, PropTypes} from 'react'
|
|||
import WidgetCell from 'shared/components/WidgetCell'
|
||||
import LayoutCell from 'shared/components/LayoutCell'
|
||||
import RefreshingGraph from 'shared/components/RefreshingGraph'
|
||||
import {buildQueriesForLayouts} from 'utils/influxql'
|
||||
import {buildQueriesForLayouts} from 'utils/buildQueriesForLayouts'
|
||||
|
||||
import _ from 'lodash'
|
||||
|
||||
|
|
|
@ -17,12 +17,8 @@ class LineGraph extends Component {
|
|||
}
|
||||
|
||||
componentWillMount() {
|
||||
const {data, activeQueryIndex, isInDataExplorer} = this.props
|
||||
this._timeSeries = timeSeriesToDygraph(
|
||||
data,
|
||||
activeQueryIndex,
|
||||
isInDataExplorer
|
||||
)
|
||||
const {data, isInDataExplorer} = this.props
|
||||
this._timeSeries = timeSeriesToDygraph(data, isInDataExplorer)
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
|
@ -33,7 +29,6 @@ class LineGraph extends Component {
|
|||
) {
|
||||
this._timeSeries = timeSeriesToDygraph(
|
||||
nextProps.data,
|
||||
nextProps.activeQueryIndex,
|
||||
nextProps.isInDataExplorer
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import GroupByTimeDropdown from 'src/data_explorer/components/GroupByTimeDropdown'
|
||||
import TimeShiftDropdown from 'src/shared/components/TimeShiftDropdown'
|
||||
import FillQuery from 'shared/components/FillQuery'
|
||||
|
||||
const QueryOptions = ({
|
||||
fill,
|
||||
shift,
|
||||
onFill,
|
||||
groupBy,
|
||||
onTimeShift,
|
||||
onGroupByTime,
|
||||
isKapacitorRule,
|
||||
}) =>
|
||||
<div className="query-builder--groupby-fill-container">
|
||||
<GroupByTimeDropdown
|
||||
selected={groupBy.time}
|
||||
onChooseGroupByTime={onGroupByTime}
|
||||
/>
|
||||
{isKapacitorRule
|
||||
? null
|
||||
: <TimeShiftDropdown
|
||||
selected={shift && shift.label}
|
||||
onChooseTimeShift={onTimeShift}
|
||||
/>}
|
||||
{isKapacitorRule ? null : <FillQuery value={fill} onChooseFill={onFill} />}
|
||||
</div>
|
||||
|
||||
const {bool, func, shape, string} = PropTypes
|
||||
|
||||
QueryOptions.propTypes = {
|
||||
fill: string,
|
||||
onFill: func.isRequired,
|
||||
groupBy: shape({
|
||||
time: string,
|
||||
}).isRequired,
|
||||
shift: shape({
|
||||
label: string,
|
||||
}),
|
||||
onGroupByTime: func.isRequired,
|
||||
isKapacitorRule: bool.isRequired,
|
||||
onTimeShift: func.isRequired,
|
||||
}
|
||||
|
||||
export default QueryOptions
|
|
@ -13,6 +13,7 @@ const SchemaExplorer = ({
|
|||
initialGroupByTime,
|
||||
actions: {
|
||||
fill,
|
||||
timeShift,
|
||||
chooseTag,
|
||||
groupByTag,
|
||||
groupByTime,
|
||||
|
@ -44,13 +45,14 @@ const SchemaExplorer = ({
|
|||
source={source}
|
||||
query={query}
|
||||
querySource={source}
|
||||
initialGroupByTime={initialGroupByTime}
|
||||
onToggleField={actionBinder(id, toggleField)}
|
||||
onFill={actionBinder(id, fill)}
|
||||
onGroupByTime={actionBinder(id, groupByTime)}
|
||||
applyFuncsToField={actionBinder(id, applyFuncsToField)}
|
||||
initialGroupByTime={initialGroupByTime}
|
||||
onTimeShift={actionBinder(id, timeShift)}
|
||||
removeFuncs={actionBinder(id, removeFuncs)}
|
||||
onToggleField={actionBinder(id, toggleField)}
|
||||
onGroupByTime={actionBinder(id, groupByTime)}
|
||||
addInitialField={actionBinder(id, addInitialField)}
|
||||
applyFuncsToField={actionBinder(id, applyFuncsToField)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
import {TIME_SHIFTS} from 'shared/constants/timeShift'
|
||||
|
||||
const TimeShiftDropdown = ({selected, onChooseTimeShift}) =>
|
||||
<div className="group-by-time">
|
||||
<label className="group-by-time--label">Compare:</label>
|
||||
<Dropdown
|
||||
className="group-by-time--dropdown"
|
||||
buttonColor="btn-info"
|
||||
items={TIME_SHIFTS}
|
||||
onChoose={onChooseTimeShift}
|
||||
selected={selected || 'none'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
const {func, string} = PropTypes
|
||||
|
||||
TimeShiftDropdown.propTypes = {
|
||||
selected: string,
|
||||
onChooseTimeShift: func.isRequired,
|
||||
}
|
||||
|
||||
export default TimeShiftDropdown
|
|
@ -0,0 +1,4 @@
|
|||
export const ABSOLUTE = 'absolute'
|
||||
export const INVALID = 'invalid'
|
||||
export const RELATIVE_LOWER = 'relative lower'
|
||||
export const RELATIVE_UPPER = 'relative upper'
|
|
@ -0,0 +1,10 @@
|
|||
export const TIME_SHIFTS = [
|
||||
{label: 'none', text: 'none', quantity: null, unit: null},
|
||||
{label: '1m', text: '1m', quantity: '1', unit: 'm'},
|
||||
{label: '1h', text: '1h', quantity: '1', unit: 'h'},
|
||||
{label: '12h', text: '12h', quantity: '12', unit: 'h'},
|
||||
{label: '1d', text: '1d', quantity: '1', unit: 'd'},
|
||||
{label: '7d', text: '7d', quantity: '7', unit: 'd'},
|
||||
{label: '30d', text: '30d', quantity: '30', unit: 'd'},
|
||||
{label: '365d', text: '365d', quantity: '365', unit: 'd'},
|
||||
]
|
|
@ -0,0 +1,110 @@
|
|||
import moment from 'moment'
|
||||
import {
|
||||
INFLUXQL,
|
||||
ABSOLUTE,
|
||||
INVALID,
|
||||
RELATIVE_LOWER,
|
||||
RELATIVE_UPPER,
|
||||
} from 'shared/constants/timeRange'
|
||||
const now = /^now/
|
||||
|
||||
export const timeRangeType = ({upper, lower, type}) => {
|
||||
if (!upper && !lower) {
|
||||
return INVALID
|
||||
}
|
||||
|
||||
if (type && type !== INFLUXQL) {
|
||||
return INVALID
|
||||
}
|
||||
|
||||
const isUpperValid = moment(upper).isValid()
|
||||
const isLowerValid = moment(lower).isValid()
|
||||
|
||||
// {lower: <Date>, upper: <Date>}
|
||||
if (isLowerValid && isUpperValid) {
|
||||
return ABSOLUTE
|
||||
}
|
||||
|
||||
// {lower: now - <Duration>, upper: <empty>}
|
||||
if (now.test(lower) && !upper) {
|
||||
return RELATIVE_LOWER
|
||||
}
|
||||
|
||||
// {lower: <Date>, upper: now() - <Duration>}
|
||||
if (isLowerValid && now.test(upper)) {
|
||||
return RELATIVE_UPPER
|
||||
}
|
||||
|
||||
return INVALID
|
||||
}
|
||||
|
||||
export const shiftTimeRange = (timeRange, shift) => {
|
||||
const {upper, lower} = timeRange
|
||||
const {quantity, unit} = shift
|
||||
const trType = timeRangeType(timeRange)
|
||||
const duration = `${quantity}${unit}`
|
||||
const type = 'shifted'
|
||||
|
||||
switch (trType) {
|
||||
case RELATIVE_UPPER:
|
||||
case ABSOLUTE: {
|
||||
return {
|
||||
lower: `${lower} - ${duration}`,
|
||||
upper: `${upper} - ${duration}`,
|
||||
type,
|
||||
}
|
||||
}
|
||||
|
||||
case RELATIVE_LOWER: {
|
||||
return {
|
||||
lower: `${lower} - ${duration}`,
|
||||
upper: `now() - ${duration}`,
|
||||
type,
|
||||
}
|
||||
}
|
||||
|
||||
default: {
|
||||
return {lower, upper, type: 'unshifted'}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getMomentUnit = unit => {
|
||||
switch (unit) {
|
||||
case 'ms': {
|
||||
return 'milliseconds' // (1 thousandth of a second)
|
||||
}
|
||||
|
||||
case 's': {
|
||||
return 'seconds'
|
||||
}
|
||||
|
||||
case 'm': {
|
||||
return 'minute'
|
||||
}
|
||||
|
||||
case 'h': {
|
||||
return 'hour'
|
||||
}
|
||||
|
||||
case 'd': {
|
||||
return 'day'
|
||||
}
|
||||
|
||||
case 'w': {
|
||||
return 'week'
|
||||
}
|
||||
|
||||
default: {
|
||||
return unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const shiftDate = (date, quantity, unit) => {
|
||||
if (!date && !quantity && !unit) {
|
||||
return moment(date)
|
||||
}
|
||||
|
||||
return moment(date).add(quantity, getMomentUnit(unit))
|
||||
}
|
|
@ -56,6 +56,14 @@ $dash-graph-options-arrow: 8px;
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Dashboard Index Page
|
||||
------------------------------------------------------
|
||||
*/
|
||||
.dashboards-page--actions .users__search-widget {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/*
|
||||
Default Dashboard Mode
|
||||
------------------------------------------------------
|
||||
|
|
|
@ -1,28 +1,46 @@
|
|||
import {buildQuery} from 'utils/influxql'
|
||||
import {TYPE_QUERY_CONFIG} from 'src/dashboards/constants'
|
||||
import {TYPE_QUERY_CONFIG, TYPE_SHIFTED} from 'src/dashboards/constants'
|
||||
|
||||
const buildQueries = (proxy, queryConfigs, timeRange) => {
|
||||
const buildQueries = (proxy, queryConfigs, tR) => {
|
||||
const statements = queryConfigs.map(query => {
|
||||
const text =
|
||||
query.rawText ||
|
||||
buildQuery(TYPE_QUERY_CONFIG, query.range || timeRange, query)
|
||||
return {text, id: query.id, queryConfig: query}
|
||||
})
|
||||
const {rawText, range, id, shifts, database, measurement, fields} = query
|
||||
const timeRange = range || tR
|
||||
const text = rawText || buildQuery(TYPE_QUERY_CONFIG, timeRange, query)
|
||||
const isParsable = database && measurement && fields.length
|
||||
|
||||
const queries = statements.filter(s => s.text !== null).map(s => {
|
||||
let queryProxy = ''
|
||||
if (s.queryConfig.source) {
|
||||
queryProxy = `${s.queryConfig.source.links.proxy}`
|
||||
if (shifts && shifts.length && isParsable) {
|
||||
const shiftedQueries = shifts
|
||||
.filter(s => s.unit)
|
||||
.map(s => buildQuery(TYPE_SHIFTED, timeRange, query, s))
|
||||
|
||||
return {
|
||||
text: `${text};${shiftedQueries.join(';')}`,
|
||||
id,
|
||||
queryConfig: query,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
host: [queryProxy || proxy],
|
||||
text: s.text,
|
||||
id: s.id,
|
||||
queryConfig: s.queryConfig,
|
||||
}
|
||||
return {text, id, queryConfig: query}
|
||||
})
|
||||
|
||||
const queries = statements
|
||||
.filter(s => s.text !== null)
|
||||
.map(({queryConfig, text, id}) => {
|
||||
let queryProxy = ''
|
||||
if (queryConfig.source) {
|
||||
queryProxy = `${queryConfig.source.links.proxy}`
|
||||
}
|
||||
|
||||
const host = [queryProxy || proxy]
|
||||
|
||||
return {
|
||||
host,
|
||||
text,
|
||||
id,
|
||||
queryConfig,
|
||||
}
|
||||
})
|
||||
|
||||
return queries
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import {buildQuery} from 'utils/influxql'
|
||||
import {TYPE_SHIFTED, TYPE_QUERY_CONFIG} from 'src/dashboards/constants'
|
||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
||||
|
||||
const buildCannedDashboardQuery = (query, {lower, upper}, host) => {
|
||||
const {defaultGroupBy} = timeRanges.find(range => range.lower === lower) || {
|
||||
defaultGroupBy: '5m',
|
||||
}
|
||||
const {wheres, groupbys} = query
|
||||
|
||||
let text = query.text
|
||||
|
||||
if (upper) {
|
||||
text += ` where time > '${lower}' AND time < '${upper}'`
|
||||
} else {
|
||||
text += ` where time > ${lower}`
|
||||
}
|
||||
|
||||
if (host) {
|
||||
text += ` and \"host\" = '${host}'`
|
||||
}
|
||||
|
||||
if (wheres && wheres.length > 0) {
|
||||
text += ` and ${wheres.join(' and ')}`
|
||||
}
|
||||
|
||||
if (groupbys) {
|
||||
if (groupbys.find(g => g.includes('time'))) {
|
||||
text += ` group by ${groupbys.join(',')}`
|
||||
} else if (groupbys.length > 0) {
|
||||
text += ` group by time(${defaultGroupBy}),${groupbys.join(',')}`
|
||||
} else {
|
||||
text += ` group by time(${defaultGroupBy})`
|
||||
}
|
||||
} else {
|
||||
text += ` group by time(${defaultGroupBy})`
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
export const buildQueriesForLayouts = (cell, source, timeRange, host) => {
|
||||
return cell.queries.map(query => {
|
||||
let queryText
|
||||
// Canned dashboards use an different a schema different from queryConfig.
|
||||
if (query.queryConfig) {
|
||||
const {
|
||||
queryConfig: {database, measurement, fields, shifts, rawText, range},
|
||||
} = query
|
||||
const tR = range || {
|
||||
upper: ':upperDashboardTime:',
|
||||
lower: ':dashboardTime:',
|
||||
}
|
||||
|
||||
queryText =
|
||||
rawText || buildQuery(TYPE_QUERY_CONFIG, tR, query.queryConfig)
|
||||
const isParsable = database && measurement && fields.length
|
||||
|
||||
if (shifts && shifts.length && isParsable) {
|
||||
const shiftedQueries = shifts
|
||||
.filter(s => s.unit)
|
||||
.map(s => buildQuery(TYPE_SHIFTED, timeRange, query.queryConfig, s))
|
||||
|
||||
queryText = `${queryText};${shiftedQueries.join(';')}`
|
||||
}
|
||||
} else {
|
||||
queryText = buildCannedDashboardQuery(query, timeRange, host)
|
||||
}
|
||||
|
||||
return {...query, host: source.links.proxy, text: queryText}
|
||||
})
|
||||
}
|
|
@ -15,6 +15,7 @@ const defaultQueryConfig = ({id, isKapacitorRule = false}) => {
|
|||
areTagsAccepted: true,
|
||||
rawText: null,
|
||||
status: null,
|
||||
shifts: [],
|
||||
}
|
||||
|
||||
return isKapacitorRule ? queryConfig : {...queryConfig, fill: NULL_STRING}
|
||||
|
|
|
@ -2,8 +2,12 @@ import _ from 'lodash'
|
|||
|
||||
import {TEMP_VAR_INTERVAL, AUTO_GROUP_BY} from 'shared/constants'
|
||||
import {NULL_STRING} from 'shared/constants/queryFillOptions'
|
||||
import {TYPE_QUERY_CONFIG, TYPE_IFQL} from 'src/dashboards/constants'
|
||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
||||
import {
|
||||
TYPE_QUERY_CONFIG,
|
||||
TYPE_SHIFTED,
|
||||
TYPE_IFQL,
|
||||
} from 'src/dashboards/constants'
|
||||
import {shiftTimeRange} from 'shared/query/helpers'
|
||||
|
||||
/* eslint-disable quotes */
|
||||
export const quoteIfTimestamp = ({lower, upper}) => {
|
||||
|
@ -19,11 +23,11 @@ export const quoteIfTimestamp = ({lower, upper}) => {
|
|||
}
|
||||
/* eslint-enable quotes */
|
||||
|
||||
export default function buildInfluxQLQuery(timeRange, config) {
|
||||
export default function buildInfluxQLQuery(timeRange, config, shift) {
|
||||
const {groupBy, fill = NULL_STRING, tags, areTagsAccepted} = config
|
||||
const {upper, lower} = quoteIfTimestamp(timeRange)
|
||||
|
||||
const select = _buildSelect(config)
|
||||
const select = _buildSelect(config, shift)
|
||||
if (select === null) {
|
||||
return null
|
||||
}
|
||||
|
@ -35,26 +39,35 @@ export default function buildInfluxQLQuery(timeRange, config) {
|
|||
return `${select}${condition}${dimensions}${fillClause}`
|
||||
}
|
||||
|
||||
function _buildSelect({fields, database, retentionPolicy, measurement}) {
|
||||
function _buildSelect({fields, database, retentionPolicy, measurement}, shift) {
|
||||
if (!database || !measurement || !fields || !fields.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
const rpSegment = retentionPolicy ? `"${retentionPolicy}"` : ''
|
||||
const fieldsClause = _buildFields(fields)
|
||||
const fieldsClause = _buildFields(fields, shift)
|
||||
const fullyQualifiedMeasurement = `"${database}".${rpSegment}."${measurement}"`
|
||||
const statement = `SELECT ${fieldsClause} FROM ${fullyQualifiedMeasurement}`
|
||||
return statement
|
||||
}
|
||||
|
||||
// type arg will reason about new query types i.e. IFQL, GraphQL, or queryConfig
|
||||
export const buildQuery = (type, timeRange, config) => {
|
||||
export const buildQuery = (type, timeRange, config, shift) => {
|
||||
switch (type) {
|
||||
case `${TYPE_QUERY_CONFIG}`: {
|
||||
case TYPE_QUERY_CONFIG: {
|
||||
return buildInfluxQLQuery(timeRange, config)
|
||||
}
|
||||
|
||||
case `${TYPE_IFQL}`: {
|
||||
case TYPE_SHIFTED: {
|
||||
const {quantity, unit} = shift
|
||||
return buildInfluxQLQuery(
|
||||
shiftTimeRange(timeRange, shift),
|
||||
config,
|
||||
`_shifted__${quantity}__${unit}`
|
||||
)
|
||||
}
|
||||
|
||||
case TYPE_IFQL: {
|
||||
// build query usining IFQL here
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +79,7 @@ export function buildSelectStatement(config) {
|
|||
return _buildSelect(config)
|
||||
}
|
||||
|
||||
function _buildFields(fieldFuncs) {
|
||||
function _buildFields(fieldFuncs, shift = '') {
|
||||
if (!fieldFuncs) {
|
||||
return ''
|
||||
}
|
||||
|
@ -91,7 +104,7 @@ function _buildFields(fieldFuncs) {
|
|||
}
|
||||
case 'func': {
|
||||
const args = _buildFields(f.args)
|
||||
const alias = f.alias ? ` AS "${f.alias}"` : ''
|
||||
const alias = f.alias ? ` AS "${f.alias}${shift}"` : ''
|
||||
return `${f.value}(${args})${alias}`
|
||||
}
|
||||
}
|
||||
|
@ -167,61 +180,5 @@ function _buildFill(fill) {
|
|||
return ` FILL(${fill})`
|
||||
}
|
||||
|
||||
const buildCannedDashboardQuery = (query, {lower, upper}, host) => {
|
||||
const {defaultGroupBy} = timeRanges.find(range => range.lower === lower) || {
|
||||
defaultGroupBy: '5m',
|
||||
}
|
||||
const {wheres, groupbys} = query
|
||||
|
||||
let text = query.text
|
||||
|
||||
if (upper) {
|
||||
text += ` where time > '${lower}' AND time < '${upper}'`
|
||||
} else {
|
||||
text += ` where time > ${lower}`
|
||||
}
|
||||
|
||||
if (host) {
|
||||
text += ` and \"host\" = '${host}'`
|
||||
}
|
||||
|
||||
if (wheres && wheres.length > 0) {
|
||||
text += ` and ${wheres.join(' and ')}`
|
||||
}
|
||||
|
||||
if (groupbys) {
|
||||
if (groupbys.find(g => g.includes('time'))) {
|
||||
text += ` group by ${groupbys.join(',')}`
|
||||
} else if (groupbys.length > 0) {
|
||||
text += ` group by time(${defaultGroupBy}),${groupbys.join(',')}`
|
||||
} else {
|
||||
text += ` group by time(${defaultGroupBy})`
|
||||
}
|
||||
} else {
|
||||
text += ` group by time(${defaultGroupBy})`
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
export const buildQueriesForLayouts = (cell, source, timeRange, host) => {
|
||||
return cell.queries.map(query => {
|
||||
let queryText
|
||||
// Canned dashboards use an different a schema different from queryConfig.
|
||||
if (query.queryConfig) {
|
||||
const {queryConfig: {rawText, range}} = query
|
||||
const tR = range || {
|
||||
upper: ':upperDashboardTime:',
|
||||
lower: ':dashboardTime:',
|
||||
}
|
||||
queryText = rawText || buildInfluxQLQuery(tR, query.queryConfig)
|
||||
} else {
|
||||
queryText = buildCannedDashboardQuery(query, timeRange, host)
|
||||
}
|
||||
|
||||
return {...query, host: source.links.proxy, text: queryText}
|
||||
})
|
||||
}
|
||||
|
||||
export const buildRawText = (q, timeRange) =>
|
||||
q.rawText || buildInfluxQLQuery(timeRange, q) || ''
|
||||
|
|
|
@ -108,7 +108,7 @@ export const toggleField = (query, {value}) => {
|
|||
}
|
||||
}
|
||||
|
||||
export function groupByTime(query, time) {
|
||||
export const groupByTime = (query, time) => {
|
||||
return Object.assign({}, query, {
|
||||
groupBy: Object.assign({}, query.groupBy, {
|
||||
time,
|
||||
|
@ -118,7 +118,7 @@ export function groupByTime(query, time) {
|
|||
|
||||
export const fill = (query, value) => ({...query, fill: value})
|
||||
|
||||
export function toggleTagAcceptance(query) {
|
||||
export const toggleTagAcceptance = query => {
|
||||
return Object.assign({}, query, {
|
||||
areTagsAccepted: !query.areTagsAccepted,
|
||||
})
|
||||
|
@ -185,13 +185,13 @@ export const applyFuncsToField = (query, {field, funcs = []}, groupBy) => {
|
|||
}
|
||||
}
|
||||
|
||||
export function updateRawQuery(query, rawText) {
|
||||
export const updateRawQuery = (query, rawText) => {
|
||||
return Object.assign({}, query, {
|
||||
rawText,
|
||||
})
|
||||
}
|
||||
|
||||
export function groupByTag(query, tagKey) {
|
||||
export const groupByTag = (query, tagKey) => {
|
||||
const oldTags = query.groupBy.tags
|
||||
let newTags
|
||||
|
||||
|
@ -209,7 +209,7 @@ export function groupByTag(query, tagKey) {
|
|||
})
|
||||
}
|
||||
|
||||
export function chooseTag(query, tag) {
|
||||
export const chooseTag = (query, tag) => {
|
||||
const tagValues = query.tags[tag.key]
|
||||
const shouldRemoveTag =
|
||||
tagValues && tagValues.length === 1 && tagValues[0] === tag.value
|
||||
|
@ -219,6 +219,14 @@ export function chooseTag(query, tag) {
|
|||
return Object.assign({}, query, {tags: newTags})
|
||||
}
|
||||
|
||||
const updateTagValues = newTagValues => {
|
||||
return Object.assign({}, query, {
|
||||
tags: Object.assign({}, query.tags, {
|
||||
[tag.key]: newTagValues,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
const oldTagValues = query.tags[tag.key]
|
||||
if (!oldTagValues) {
|
||||
return updateTagValues([tag.value])
|
||||
|
@ -233,12 +241,6 @@ export function chooseTag(query, tag) {
|
|||
}
|
||||
|
||||
return updateTagValues(query.tags[tag.key].concat(tag.value))
|
||||
|
||||
function updateTagValues(newTagValues) {
|
||||
return Object.assign({}, query, {
|
||||
tags: Object.assign({}, query.tags, {
|
||||
[tag.key]: newTagValues,
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const timeShift = (query, shift) => ({...query, shifts: [shift]})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import _ from 'lodash'
|
||||
import {shiftDate} from 'shared/query/helpers'
|
||||
import {map, reduce, forEach, concat, clone} from 'fast.js'
|
||||
|
||||
/**
|
||||
|
@ -15,12 +16,7 @@ const cells = {
|
|||
responseIndex: new Array(DEFAULT_SIZE),
|
||||
}
|
||||
|
||||
// activeQueryIndex is an optional argument that indicated which query's series we want highlighted.
|
||||
export default function timeSeriesToDygraph(
|
||||
raw = [],
|
||||
activeQueryIndex,
|
||||
isInDataExplorer
|
||||
) {
|
||||
export default function timeSeriesToDygraph(raw = [], isInDataExplorer) {
|
||||
// collect results from each influx response
|
||||
const results = reduce(
|
||||
raw,
|
||||
|
@ -115,11 +111,16 @@ export default function timeSeriesToDygraph(
|
|||
|
||||
const timeSeries = []
|
||||
for (let i = 0; i < size; i++) {
|
||||
const time = cells.time[i]
|
||||
let time = cells.time[i]
|
||||
const value = cells.value[i]
|
||||
const label = cells.label[i]
|
||||
const seriesIndex = cells.seriesIndex[i]
|
||||
|
||||
if (label.includes('_shifted__')) {
|
||||
const [, quantity, duration] = label.split('__')
|
||||
time = +shiftDate(time, quantity, duration).format('x')
|
||||
}
|
||||
|
||||
let existingRowIndex = tsMemo[time]
|
||||
|
||||
if (existingRowIndex === undefined) {
|
||||
|
|
Loading…
Reference in New Issue