Merge pull request #632 from influxdata/layout-y-ranges-labels
Layout y ranges labelspull/635/head^2
commit
e72d8a64b8
|
@ -107,14 +107,21 @@ func MarshalLayout(l chronograf.Layout) ([]byte, error) {
|
|||
for i, c := range l.Cells {
|
||||
queries := make([]*Query, len(c.Queries))
|
||||
for j, q := range c.Queries {
|
||||
r := new(Range)
|
||||
if q.Range != nil {
|
||||
r.Upper, r.Lower = q.Range.Upper, q.Range.Lower
|
||||
}
|
||||
queries[j] = &Query{
|
||||
Command: q.Command,
|
||||
DB: q.DB,
|
||||
RP: q.RP,
|
||||
GroupBys: q.GroupBys,
|
||||
Wheres: q.Wheres,
|
||||
Label: q.Label,
|
||||
Range: r,
|
||||
}
|
||||
}
|
||||
|
||||
cells[i] = &Cell{
|
||||
X: c.X,
|
||||
Y: c.Y,
|
||||
|
@ -155,8 +162,16 @@ func UnmarshalLayout(data []byte, l *chronograf.Layout) error {
|
|||
RP: q.RP,
|
||||
GroupBys: q.GroupBys,
|
||||
Wheres: q.Wheres,
|
||||
Label: q.Label,
|
||||
}
|
||||
if q.Range.Upper != q.Range.Lower {
|
||||
queries[j].Range = &chronograf.Range{
|
||||
Upper: q.Range.Upper,
|
||||
Lower: q.Range.Lower,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cells[i] = chronograf.Cell{
|
||||
X: c.X,
|
||||
Y: c.Y,
|
||||
|
|
|
@ -15,6 +15,7 @@ It has these top-level messages:
|
|||
Layout
|
||||
Cell
|
||||
Query
|
||||
Range
|
||||
AlertRule
|
||||
User
|
||||
*/
|
||||
|
@ -108,6 +109,8 @@ 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"`
|
||||
Ylabels []string `protobuf:"bytes,9,rep,name=ylabels" json:"ylabels,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Cell) Reset() { *m = Cell{} }
|
||||
|
@ -128,6 +131,8 @@ type Query struct {
|
|||
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"`
|
||||
}
|
||||
|
||||
func (m *Query) Reset() { *m = Query{} }
|
||||
|
@ -135,6 +140,23 @@ func (m *Query) String() string { return proto.CompactTextString(m) }
|
|||
func (*Query) ProtoMessage() {}
|
||||
func (*Query) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{5} }
|
||||
|
||||
func (m *Query) GetRange() *Range {
|
||||
if m != nil {
|
||||
return m.Range
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
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{6} }
|
||||
|
||||
type AlertRule struct {
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
JSON string `protobuf:"bytes,2,opt,name=JSON,proto3" json:"JSON,omitempty"`
|
||||
|
@ -145,7 +167,7 @@ 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{6} }
|
||||
func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{7} }
|
||||
|
||||
type User struct {
|
||||
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
|
@ -155,7 +177,7 @@ 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{7} }
|
||||
func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{8} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Exploration)(nil), "internal.Exploration")
|
||||
|
@ -164,6 +186,7 @@ func init() {
|
|||
proto.RegisterType((*Layout)(nil), "internal.Layout")
|
||||
proto.RegisterType((*Cell)(nil), "internal.Cell")
|
||||
proto.RegisterType((*Query)(nil), "internal.Query")
|
||||
proto.RegisterType((*Range)(nil), "internal.Range")
|
||||
proto.RegisterType((*AlertRule)(nil), "internal.AlertRule")
|
||||
proto.RegisterType((*User)(nil), "internal.User")
|
||||
}
|
||||
|
@ -171,40 +194,45 @@ func init() {
|
|||
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
|
||||
|
||||
var fileDescriptorInternal = []byte{
|
||||
// 555 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x94, 0x4f, 0x8e, 0xd3, 0x4a,
|
||||
0x10, 0xc6, 0xd5, 0xb1, 0x9d, 0xc4, 0x35, 0x4f, 0x79, 0xa8, 0x35, 0x42, 0x16, 0x62, 0x11, 0x59,
|
||||
0x2c, 0x82, 0x84, 0x66, 0x01, 0x27, 0x48, 0xe2, 0x11, 0x0a, 0x0c, 0x43, 0xe8, 0x4c, 0xc4, 0x8a,
|
||||
0x45, 0x93, 0x54, 0x88, 0x25, 0xc7, 0x36, 0xed, 0x36, 0x89, 0xcf, 0x00, 0x67, 0xe0, 0x12, 0x5c,
|
||||
0x80, 0xa3, 0xa1, 0x6a, 0xb7, 0x1d, 0x4b, 0xfc, 0xd1, 0xec, 0xea, 0xab, 0xaa, 0xae, 0xfc, 0xfa,
|
||||
0xab, 0x8e, 0x61, 0x14, 0xa7, 0x1a, 0x55, 0x2a, 0x93, 0xab, 0x5c, 0x65, 0x3a, 0xe3, 0xc3, 0x46,
|
||||
0x87, 0x3f, 0x18, 0x5c, 0x5c, 0x9f, 0xf2, 0x24, 0x53, 0x52, 0xc7, 0x59, 0xca, 0x47, 0xd0, 0x5b,
|
||||
0x44, 0x01, 0x1b, 0xb3, 0x89, 0x23, 0x7a, 0x8b, 0x88, 0x73, 0x70, 0x6f, 0xe5, 0x01, 0x83, 0xde,
|
||||
0x98, 0x4d, 0x7c, 0x61, 0x62, 0xfe, 0x10, 0xfa, 0xeb, 0x02, 0xd5, 0x22, 0x0a, 0x1c, 0xd3, 0x67,
|
||||
0x15, 0xf5, 0x46, 0x52, 0xcb, 0xc0, 0xad, 0x7b, 0x29, 0xe6, 0x8f, 0xc1, 0x9f, 0x2b, 0x94, 0x1a,
|
||||
0xb7, 0x53, 0x1d, 0x78, 0xa6, 0xfd, 0x9c, 0xa0, 0xea, 0x3a, 0xdf, 0xda, 0x6a, 0xbf, 0xae, 0xb6,
|
||||
0x09, 0x1e, 0xc0, 0x20, 0xc2, 0x9d, 0x2c, 0x13, 0x1d, 0x0c, 0xc6, 0x6c, 0x32, 0x14, 0x8d, 0x0c,
|
||||
0x7f, 0x32, 0xe8, 0xaf, 0xb2, 0x52, 0x6d, 0xf0, 0x5e, 0xc0, 0x1c, 0xdc, 0xbb, 0x2a, 0x47, 0x83,
|
||||
0xeb, 0x0b, 0x13, 0xf3, 0x47, 0x30, 0x24, 0xec, 0x94, 0x7a, 0x6b, 0xe0, 0x56, 0x53, 0x6d, 0x29,
|
||||
0x8b, 0xe2, 0x98, 0xa9, 0xad, 0x61, 0xf6, 0x45, 0xab, 0xf9, 0x03, 0x70, 0xd6, 0xe2, 0xc6, 0xc0,
|
||||
0xfa, 0x82, 0xc2, 0xbf, 0x63, 0xd2, 0x9c, 0x3b, 0x4c, 0xf0, 0x93, 0x92, 0xbb, 0x60, 0x58, 0xcf,
|
||||
0x69, 0x74, 0xf8, 0x8d, 0xae, 0x80, 0xea, 0x0b, 0xaa, 0x7b, 0x5d, 0xa1, 0x8b, 0xeb, 0xfc, 0x03,
|
||||
0xd7, 0xfd, 0x33, 0xae, 0x77, 0xc6, 0xbd, 0x04, 0x6f, 0xa5, 0x36, 0x8b, 0xc8, 0xfa, 0x5d, 0x8b,
|
||||
0xf0, 0x3b, 0x83, 0xfe, 0x8d, 0xac, 0xb2, 0x52, 0x77, 0x70, 0x7c, 0x83, 0x33, 0x86, 0x8b, 0x69,
|
||||
0x9e, 0x27, 0xf1, 0xc6, 0xbc, 0x10, 0x4b, 0xd5, 0x4d, 0x51, 0xc7, 0x1b, 0x94, 0x45, 0xa9, 0xf0,
|
||||
0x80, 0xa9, 0xb6, 0x7c, 0xdd, 0x14, 0x7f, 0x02, 0xde, 0x1c, 0x93, 0xa4, 0x08, 0xdc, 0xb1, 0x33,
|
||||
0xb9, 0x78, 0x3e, 0xba, 0x6a, 0x1f, 0x24, 0xa5, 0x45, 0x5d, 0xa4, 0x8b, 0x4c, 0x4b, 0x9d, 0xed,
|
||||
0x92, 0xec, 0x68, 0x88, 0x87, 0xa2, 0xd5, 0xe1, 0x57, 0x06, 0x2e, 0x75, 0xf1, 0xff, 0x80, 0x9d,
|
||||
0x0c, 0x9d, 0x27, 0xd8, 0x89, 0x54, 0x65, 0x90, 0x3c, 0xc1, 0x2a, 0x52, 0x47, 0xf3, 0xf3, 0x9e,
|
||||
0x60, 0x47, 0x52, 0x7b, 0x63, 0x88, 0x27, 0xd8, 0x9e, 0x3f, 0x85, 0xc1, 0xe7, 0x12, 0x55, 0x8c,
|
||||
0x45, 0xe0, 0x19, 0x88, 0xff, 0xcf, 0x10, 0xef, 0x4a, 0x54, 0x95, 0x68, 0xea, 0x74, 0x30, 0xb6,
|
||||
0x1b, 0x66, 0x31, 0xad, 0xc3, 0xd8, 0x3e, 0xa8, 0xd7, 0x41, 0x71, 0x58, 0x82, 0x67, 0xce, 0xd0,
|
||||
0xf2, 0xe7, 0xd9, 0xe1, 0x20, 0xd3, 0xad, 0x75, 0xac, 0x91, 0x64, 0x63, 0x34, 0xb3, 0x6e, 0xf5,
|
||||
0xa2, 0x19, 0x69, 0xb1, 0xb4, 0xde, 0xf4, 0xc4, 0x92, 0x2e, 0xfb, 0x52, 0x65, 0x65, 0x3e, 0xab,
|
||||
0x6a, 0x57, 0x7c, 0xd1, 0x6a, 0xfa, 0x87, 0xbd, 0xdf, 0xa3, 0xb2, 0xa8, 0xbe, 0xb0, 0x2a, 0xfc,
|
||||
0x00, 0xfe, 0x34, 0x41, 0xa5, 0x45, 0x99, 0xe0, 0x6f, 0x7b, 0xe2, 0xe0, 0xbe, 0x5a, 0xbd, 0xbd,
|
||||
0x6d, 0x9e, 0x0d, 0xc5, 0xe7, 0x65, 0x3b, 0x9d, 0x65, 0xd3, 0xf8, 0xd7, 0x32, 0x97, 0x8b, 0xc8,
|
||||
0xb8, 0xe3, 0x08, 0xab, 0xc2, 0x67, 0xe0, 0xd2, 0xa3, 0xea, 0x4c, 0x76, 0xcd, 0xe4, 0x4b, 0xf0,
|
||||
0xae, 0x0f, 0x32, 0x4e, 0xec, 0xe8, 0x5a, 0x7c, 0xec, 0x9b, 0x6f, 0xc9, 0x8b, 0x5f, 0x01, 0x00,
|
||||
0x00, 0xff, 0xff, 0x92, 0xf3, 0x5a, 0x42, 0x5d, 0x04, 0x00, 0x00,
|
||||
// 626 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x94, 0x5f, 0x6e, 0xd3, 0x4c,
|
||||
0x10, 0xc0, 0xb5, 0xb1, 0x9d, 0xc4, 0xdb, 0x4f, 0xfd, 0xd0, 0xaa, 0x42, 0x2b, 0xc4, 0x83, 0x65,
|
||||
0x81, 0x14, 0x24, 0xd4, 0x07, 0x7a, 0x82, 0xb4, 0xae, 0x50, 0xa0, 0x94, 0xb2, 0x6d, 0xc4, 0x13,
|
||||
0x0f, 0xdb, 0x66, 0xda, 0x5a, 0xda, 0xd8, 0x66, 0x6d, 0x93, 0xfa, 0x0e, 0x9c, 0x81, 0x43, 0xc0,
|
||||
0x01, 0xe0, 0x68, 0x68, 0x66, 0xd7, 0x49, 0x10, 0x7f, 0xd4, 0xb7, 0xfd, 0xed, 0x4c, 0xc7, 0xbf,
|
||||
0x99, 0x9d, 0x86, 0xef, 0xe6, 0x45, 0x03, 0xb6, 0xd0, 0x66, 0xbf, 0xb2, 0x65, 0x53, 0x8a, 0x71,
|
||||
0xcf, 0xe9, 0x37, 0xc6, 0x77, 0x8e, 0xef, 0x2a, 0x53, 0x5a, 0xdd, 0xe4, 0x65, 0x21, 0x76, 0xf9,
|
||||
0x60, 0x96, 0x49, 0x96, 0xb0, 0x49, 0xa0, 0x06, 0xb3, 0x4c, 0x08, 0x1e, 0x9e, 0xea, 0x25, 0xc8,
|
||||
0x41, 0xc2, 0x26, 0xb1, 0xa2, 0xb3, 0x78, 0xc8, 0x87, 0xf3, 0x1a, 0xec, 0x2c, 0x93, 0x01, 0xe5,
|
||||
0x79, 0xc2, 0xdc, 0x4c, 0x37, 0x5a, 0x86, 0x2e, 0x17, 0xcf, 0xe2, 0x31, 0x8f, 0x8f, 0x2c, 0xe8,
|
||||
0x06, 0x16, 0xd3, 0x46, 0x46, 0x94, 0xbe, 0xb9, 0xc0, 0xe8, 0xbc, 0x5a, 0xf8, 0xe8, 0xd0, 0x45,
|
||||
0xd7, 0x17, 0x42, 0xf2, 0x51, 0x06, 0xd7, 0xba, 0x35, 0x8d, 0x1c, 0x25, 0x6c, 0x32, 0x56, 0x3d,
|
||||
0xa6, 0x3f, 0x18, 0x1f, 0x9e, 0x97, 0xad, 0xbd, 0x82, 0x7b, 0x09, 0x0b, 0x1e, 0x5e, 0x74, 0x15,
|
||||
0x90, 0x6e, 0xac, 0xe8, 0x2c, 0x1e, 0xf1, 0x31, 0x6a, 0x17, 0x98, 0xeb, 0x84, 0xd7, 0x8c, 0xb1,
|
||||
0x33, 0x5d, 0xd7, 0xab, 0xd2, 0x2e, 0xc8, 0x39, 0x56, 0x6b, 0x16, 0x0f, 0x78, 0x30, 0x57, 0x27,
|
||||
0x24, 0x1b, 0x2b, 0x3c, 0xfe, 0x5d, 0x13, 0xeb, 0x5c, 0x80, 0x81, 0x1b, 0xab, 0xaf, 0xe5, 0xd8,
|
||||
0xd5, 0xe9, 0x39, 0xfd, 0x8c, 0x2d, 0x80, 0xfd, 0x04, 0xf6, 0x5e, 0x2d, 0x6c, 0xeb, 0x06, 0xff,
|
||||
0xd0, 0x0d, 0xff, 0xac, 0x1b, 0x6d, 0x74, 0xf7, 0x78, 0x74, 0x6e, 0xaf, 0x66, 0x99, 0x9f, 0xb7,
|
||||
0x83, 0xf4, 0x0b, 0xe3, 0xc3, 0x13, 0xdd, 0x95, 0x6d, 0xb3, 0xa5, 0x13, 0x93, 0x4e, 0xc2, 0x77,
|
||||
0xa6, 0x55, 0x65, 0xf2, 0x2b, 0xda, 0x10, 0x6f, 0xb5, 0x7d, 0x85, 0x19, 0x6f, 0x40, 0xd7, 0xad,
|
||||
0x85, 0x25, 0x14, 0x8d, 0xf7, 0xdb, 0xbe, 0x12, 0x4f, 0x78, 0x74, 0x04, 0xc6, 0xd4, 0x32, 0x4c,
|
||||
0x82, 0xc9, 0xce, 0x8b, 0xdd, 0xfd, 0xf5, 0x42, 0xe2, 0xb5, 0x72, 0x41, 0x6c, 0x64, 0xda, 0x36,
|
||||
0xe5, 0xb5, 0x29, 0x57, 0x64, 0x3c, 0x56, 0x6b, 0x4e, 0xbf, 0x33, 0x1e, 0x62, 0x96, 0xf8, 0x8f,
|
||||
0xb3, 0x3b, 0xb2, 0x8b, 0x14, 0xbb, 0x43, 0xea, 0x48, 0x29, 0x52, 0xac, 0x43, 0x5a, 0xd1, 0xe7,
|
||||
0x23, 0xc5, 0x56, 0x48, 0xb7, 0x34, 0x90, 0x48, 0xb1, 0x5b, 0xf1, 0x8c, 0x8f, 0x3e, 0xb6, 0x60,
|
||||
0x73, 0xa8, 0x65, 0x44, 0x12, 0xff, 0x6f, 0x24, 0xde, 0xb5, 0x60, 0x3b, 0xd5, 0xc7, 0xf1, 0x0f,
|
||||
0x73, 0xff, 0xc2, 0x2c, 0xc7, 0xe7, 0xa0, 0xb1, 0x8f, 0xdc, 0x73, 0xd0, 0xc8, 0x25, 0x1f, 0x75,
|
||||
0x56, 0x17, 0x37, 0x50, 0xcb, 0x71, 0x12, 0x4c, 0x02, 0xd5, 0x23, 0x45, 0x8c, 0xbe, 0x04, 0x53,
|
||||
0xcb, 0x38, 0x09, 0x26, 0xb1, 0xea, 0x31, 0xfd, 0xca, 0x78, 0x44, 0x1f, 0xc2, 0x9c, 0xa3, 0x72,
|
||||
0xb9, 0xd4, 0xc5, 0xc2, 0x8f, 0xb9, 0x47, 0x9c, 0x7d, 0x76, 0xe8, 0x47, 0x3c, 0xc8, 0x0e, 0x91,
|
||||
0xd5, 0x99, 0x1f, 0xe8, 0x40, 0x9d, 0xe1, 0x84, 0x5e, 0xda, 0xb2, 0xad, 0x0e, 0x3b, 0x37, 0xca,
|
||||
0x58, 0xad, 0x19, 0xff, 0x2d, 0xdf, 0xdf, 0x82, 0xf5, 0xfd, 0xc5, 0xca, 0x13, 0x3e, 0xf8, 0x09,
|
||||
0x1a, 0xf8, 0x8e, 0x1c, 0x88, 0xa7, 0x3c, 0x52, 0x68, 0x4c, 0x6d, 0xfd, 0x32, 0x0c, 0xba, 0x56,
|
||||
0x2e, 0x9a, 0x1e, 0xf8, 0x34, 0xac, 0x32, 0xaf, 0x2a, 0xb0, 0x7e, 0x4f, 0x1d, 0x50, 0xed, 0x72,
|
||||
0x05, 0x96, 0x94, 0x03, 0xe5, 0x20, 0xfd, 0xc0, 0xe3, 0xa9, 0x01, 0xdb, 0xa8, 0xd6, 0xc0, 0x6f,
|
||||
0xeb, 0x24, 0x78, 0xf8, 0xea, 0xfc, 0xed, 0x69, 0xbf, 0xdd, 0x78, 0xde, 0xec, 0x64, 0xb0, 0xb5,
|
||||
0x93, 0xd8, 0xd0, 0x6b, 0x5d, 0xe9, 0x59, 0x46, 0x8f, 0x18, 0x28, 0x4f, 0xe9, 0x73, 0x1e, 0xe2,
|
||||
0xee, 0x6f, 0x55, 0x0e, 0xa9, 0xf2, 0x1e, 0x8f, 0x8e, 0x97, 0x3a, 0x37, 0xbe, 0xb4, 0x83, 0xcb,
|
||||
0x21, 0xfd, 0xe4, 0x1d, 0xfc, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x4e, 0xdb, 0x24, 0x8b, 0x04, 0x05,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ message Cell {
|
|||
repeated Query queries = 5; // Time-series data queries for Cell.
|
||||
string i = 6; // Unique identifier for the cell
|
||||
string name = 7; // User-facing name for this cell
|
||||
repeated int64 yranges = 8; // Limits of the y-axes
|
||||
repeated string ylabels = 9; // Labels of the y-axes
|
||||
}
|
||||
|
||||
message Query {
|
||||
|
@ -55,6 +57,13 @@ message Query {
|
|||
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
|
||||
}
|
||||
|
||||
message 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 {
|
||||
|
|
|
@ -71,3 +71,46 @@ func TestMarshalServer(t *testing.T) {
|
|||
t.Fatalf("source protobuf copy error: got %#v, expected %#v", vv, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalLayout(t *testing.T) {
|
||||
layout := chronograf.Layout{
|
||||
ID: "id",
|
||||
Measurement: "measurement",
|
||||
Application: "app",
|
||||
Cells: []chronograf.Cell{
|
||||
{
|
||||
X: 1,
|
||||
Y: 1,
|
||||
W: 4,
|
||||
H: 4,
|
||||
I: "anotherid",
|
||||
Name: "cell1",
|
||||
Queries: []chronograf.Query{
|
||||
{
|
||||
Range: &chronograf.Range{
|
||||
Lower: 1,
|
||||
Upper: 2,
|
||||
},
|
||||
Label: "y1",
|
||||
Command: "select mean(usage_user) as usage_user from cpu",
|
||||
Wheres: []string{
|
||||
`"host"="myhost"`,
|
||||
},
|
||||
GroupBys: []string{
|
||||
`"cpu"`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var vv chronograf.Layout
|
||||
if buf, err := internal.MarshalLayout(layout); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if err := internal.UnmarshalLayout(buf, &vv); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !reflect.DeepEqual(layout, vv) {
|
||||
t.Fatalf("source protobuf copy error: got %#v, expected %#v", vv, layout)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,13 +53,21 @@ type TimeSeries interface {
|
|||
Connect(context.Context, *Source) error
|
||||
}
|
||||
|
||||
// Range represents an upper and lower bound for data
|
||||
type Range struct {
|
||||
Upper int64 `json:"upper"` // Upper is the upper bound
|
||||
Lower int64 `json:"lower"` // Lower is the lower bound
|
||||
}
|
||||
|
||||
// Query retrieves a Response from a TimeSeries.
|
||||
type Query struct {
|
||||
Command string `json:"query"` // Command is the query itself
|
||||
DB string `json:"db,omitempty"` // DB is optional and if empty will not be used.
|
||||
RP string `json:"rp,omitempty"` // RP is a retention policy and optional; if empty will not be used.
|
||||
Wheres []string `json:"wheres"` // Wheres restricts the query to certain attributes
|
||||
GroupBys []string `json:"groupbys"` // GroupBys collate the query by these tags
|
||||
Command string `json:"query"` // Command is the query itself
|
||||
DB string `json:"db,omitempty"` // DB is optional and if empty will not be used.
|
||||
RP string `json:"rp,omitempty"` // RP is a retention policy and optional; if empty will not be used.
|
||||
Wheres []string `json:"wheres,omitempty"` // Wheres restricts the query to certain attributes
|
||||
GroupBys []string `json:"groupbys,omitempty"` // GroupBys collate the query by these tags
|
||||
Label string `json:"label,omitempty"` // Label is the Y-Axis label for the data
|
||||
Range *Range `json:"range,omitempty"` // Range is the default Y-Axis range for the data
|
||||
}
|
||||
|
||||
// Response is the result of a query against a TimeSeries
|
||||
|
|
|
@ -5,41 +5,52 @@ Chronograf's applications are built by layout configurations that can be found i
|
|||
To create a new layout use the `new_apps.sh` script in the `canned` directory. This script takes an argument, which is the name of the layout you want, often this will map to the InfluxDB measurement, but it does not have to. For this example I will be creating a layout for the zombocom daemon zombocomd. So first step is the run `new_app.sh zombocomd` this will create a file called `zombocomd.json`. The file will look something like:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "d77917f4-9305-4c9c-beba-c29bf17a0fd2",
|
||||
"measurement": "zombocomd",
|
||||
"app": "zombocomd",
|
||||
"cells": [{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 4,
|
||||
"h": 4,
|
||||
"i": "b75694f1-5764-4d1d-9d67-564455985894",
|
||||
"name": "Running Zombo Average",
|
||||
"queries": [{
|
||||
"query": "SELECT mean(\"zombo\") FROM zombocomd",
|
||||
"db": "telegraf",
|
||||
"rp": "",
|
||||
"groupbys": ["\"pod\""],
|
||||
"wheres": []
|
||||
}]
|
||||
{
|
||||
"id": "d77917f4-9305-4c9c-beba-c29bf17a0fd2",
|
||||
"measurement": "zombocomd",
|
||||
"app": "zombocomd",
|
||||
"cells": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 4,
|
||||
"h": 4,
|
||||
"i": "b75694f1-5764-4d1d-9d67-564455985894",
|
||||
"name": "Running Zombo Average",
|
||||
"queries": [
|
||||
{
|
||||
"query": "SELECT mean(\"zombo\") FROM zombocomd",
|
||||
"db": "telegraf",
|
||||
"rp": "",
|
||||
"groupbys": [
|
||||
"\"pod\""
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 4,
|
||||
"h": 4,
|
||||
"i": "b75694f1-5764-4d1d-9d67-564455985894",
|
||||
"name": "Anythings Per Second",
|
||||
"queries": [{
|
||||
"query": "SELECT non_negative_derivative(max(\"anything\"), 1s) FROM zombocomd",
|
||||
"db": "telegraf",
|
||||
"rp": "",
|
||||
"groupbys": [],
|
||||
"wheres": []
|
||||
}]
|
||||
}]
|
||||
}
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 4,
|
||||
"h": 4,
|
||||
"i": "7a6bc880-054e-455c-becd-c0ebbbe109ce",
|
||||
"name": "Anythings Per Second",
|
||||
"queries": [
|
||||
{
|
||||
"query": "SELECT non_negative_derivative(max(\"anything\"), 1s) FROM zombocomd",
|
||||
"db": "telegraf",
|
||||
"rp": "",
|
||||
"label": "anything/s",
|
||||
"range": {
|
||||
"upper": 100,
|
||||
"lower": 10
|
||||
},
|
||||
"wheres": ["\"status\" = 'critical'"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The meaning of the fields are as follows:
|
||||
|
@ -54,10 +65,14 @@ The meaning of the fields are as follows:
|
|||
* i - A unique ID for the graph
|
||||
* name - The displayed name of the graph
|
||||
* queries - An array of InfluxQL queries. Note: queries must use an aggregate function since chronograf adds a group by time function to keep the data points manageable.
|
||||
* db - The name of the database for the query
|
||||
* rp - The [retention policy](https://docs.influxdata.com/influxdb/v1.1/concepts/glossary/#retention-policy-rp) for the database
|
||||
* groupbys - An array of GROUP BY clauses to use on the query
|
||||
* wheres - An array of WHERE clauses to add to the query
|
||||
* queries.[].db - The name of the database for the query
|
||||
* queries.[].rp - The [retention policy](https://docs.influxdata.com/influxdb/v1.1/concepts/glossary/#retention-policy-rp) for the database
|
||||
* queries.[].groupbys - An optional array of GROUP BY clauses to use on the query
|
||||
* queries.[].wheres - An optional array of WHERE clauses to add to the query
|
||||
* queries.[].label - Optional key specifying the user-facing name of the y-axes
|
||||
* queries.[].range.upper - Optional key specifying the default upper bound of the y-axis
|
||||
* queries.[].range.lower - Optional key specifying the default lower bound of the y-axis
|
||||
|
||||
|
||||
The above example will create two graphs. The first graph will show the mean number of zombos created then group the zombos by pod and by time. It is important to note that all queries have a `GROUP BY` time(x) appended to them. In this case `x` is determined by the duration of the query. The second graph will show the number of the "anything" [field](https://docs.influxdata.com/influxdb/v1.1/concepts/glossary/#field) per second average.
|
||||
For real world examples of when to use the `groupbys` see the docker examples and for `wheres` examples see the kubernetes examples in the `canned` directory.
|
||||
|
|
|
@ -18,7 +18,6 @@ func newLayoutResponse(layout chronograf.Layout) layoutResponse {
|
|||
httpAPILayouts := "/chronograf/v1/layouts"
|
||||
href := fmt.Sprintf("%s/%s", httpAPILayouts, layout.ID)
|
||||
rel := "self"
|
||||
|
||||
return layoutResponse{
|
||||
Layout: layout,
|
||||
Link: link{
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"routes"
|
||||
],
|
||||
"summary": "Lists all the endpoints",
|
||||
"description": "List of the endpoints.",
|
||||
"responses": {
|
||||
|
@ -38,6 +41,9 @@
|
|||
},
|
||||
"/sources": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources"
|
||||
],
|
||||
"summary": "Configured data sources",
|
||||
"description": "These data sources store time series data.",
|
||||
"responses": {
|
||||
|
@ -56,6 +62,9 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"sources"
|
||||
],
|
||||
"summary": "Create new data source",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -92,6 +101,9 @@
|
|||
},
|
||||
"/sources/{id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -125,6 +137,9 @@
|
|||
}
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"sources"
|
||||
],
|
||||
"summary": "Update data source configuration",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -166,6 +181,9 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"sources"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -197,6 +215,10 @@
|
|||
},
|
||||
"/sources/{id}/proxy": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"proxy"
|
||||
],
|
||||
"description": "Query the backend time series data source and return the response according to `format`",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -252,6 +274,9 @@
|
|||
},
|
||||
"/users": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "List of all users on this data source",
|
||||
"responses": {
|
||||
"200": {
|
||||
|
@ -269,6 +294,9 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Create new user for this data source",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -305,6 +333,9 @@
|
|||
},
|
||||
"/users/{user_id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
|
@ -338,6 +369,9 @@
|
|||
}
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Update user configuration",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -379,6 +413,9 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
|
@ -410,6 +447,11 @@
|
|||
},
|
||||
"/users/{user_id}/explorations": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"users",
|
||||
"explorations"
|
||||
],
|
||||
"summary": "Returns all explorations for specified user",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
|
@ -441,6 +483,10 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"users",
|
||||
"explorations"
|
||||
],
|
||||
"summary": "Create new named exploration for this user",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -490,6 +536,10 @@
|
|||
},
|
||||
"/users/{user_id}/explorations/{exploration_id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"users",
|
||||
"explorations"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
|
@ -530,6 +580,10 @@
|
|||
}
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"users",
|
||||
"explorations"
|
||||
],
|
||||
"summary": "Update exploration configuration",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -578,6 +632,10 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"users",
|
||||
"explorations"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
|
@ -616,6 +674,10 @@
|
|||
},
|
||||
"/sources/{id}/kapacitors": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -642,6 +704,10 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors"
|
||||
],
|
||||
"summary": "Create new kapacitor backend",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -685,6 +751,10 @@
|
|||
},
|
||||
"/sources/{id}/kapacitors/{kapa_id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -725,6 +795,10 @@
|
|||
}
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors"
|
||||
],
|
||||
"summary": "Update kapacitor configuration",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -773,6 +847,10 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -809,9 +887,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/sources/{id}/kapacitors/{kapa_id}/tasks": {
|
||||
"/sources/{id}/kapacitors/{kapa_id}/rules": {
|
||||
"get": {
|
||||
"description": "Get all defined alert tasks.",
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"rules"
|
||||
],
|
||||
"description": "Get all defined alert rules.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -830,9 +913,9 @@
|
|||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "All alert tasks for this specific kapacitor are returned",
|
||||
"description": "All alert rules for this specific kapacitor are returned",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Tasks"
|
||||
"$ref": "#/definitions/Rules"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
|
@ -850,7 +933,12 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create kapacitor alert task",
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"rules"
|
||||
],
|
||||
"description": "Create kapacitor alert rule",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -867,37 +955,43 @@
|
|||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "task",
|
||||
"name": "rule",
|
||||
"in": "body",
|
||||
"description": "Rule to generate alert task",
|
||||
"description": "Rule to generate alert rule",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Task"
|
||||
"$ref": "#/definitions/Rule"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Successfully created new kapacitor alert task",
|
||||
"description": "Successfully created new kapacitor alert rule",
|
||||
"headers": {
|
||||
"Location": {
|
||||
"type": "string",
|
||||
"format": "url",
|
||||
"description": "Location of the newly created kapacitor task resource."
|
||||
"description": "Location of the newly created kapacitor rule resource."
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Task"
|
||||
"$ref": "#/definitions/Rule"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Kapacitor ID does not exist.",
|
||||
"description": "Source ID or Kapacitor ID does not exist.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Source ID , Kapacitor ID or alert are unprocessable",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Internal server error",
|
||||
"description": "Internal server error; generally a problem creating alert in kapacitor",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
|
@ -905,8 +999,13 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/sources/{id}/kapacitors/{kapa_id}/tasks/{task_id}": {
|
||||
"/sources/{id}/kapacitors/{kapa_id}/rules/{rule_id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"rules"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -923,24 +1022,24 @@
|
|||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "task_id",
|
||||
"name": "rule_id",
|
||||
"in": "path",
|
||||
"type": "string",
|
||||
"description": "ID of the task",
|
||||
"description": "ID of the rule",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"summary": "Specific kapacitor alert task",
|
||||
"description": "Alerting task for kapacitor",
|
||||
"summary": "Specific kapacitor alert rule",
|
||||
"description": "Alerting rule for kapacitor",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Alert exists and has a specific TICKscript",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Task"
|
||||
"$ref": "#/definitions/Rule"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown data source, kapacitor id, or task id",
|
||||
"description": "Unknown data source, kapacitor id, or rule id",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
|
@ -954,7 +1053,12 @@
|
|||
}
|
||||
},
|
||||
"put": {
|
||||
"summary": "Update rule alert task configuration",
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"rules"
|
||||
],
|
||||
"summary": "Update rule alert rule configuration",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -971,18 +1075,18 @@
|
|||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "task_id",
|
||||
"name": "rule_id",
|
||||
"in": "path",
|
||||
"type": "string",
|
||||
"description": "ID of a task",
|
||||
"description": "ID of a rule",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "task",
|
||||
"name": "rule",
|
||||
"in": "body",
|
||||
"description": "Task update",
|
||||
"description": "Rule update",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Task"
|
||||
"$ref": "#/definitions/Rule"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
|
@ -991,11 +1095,11 @@
|
|||
"200": {
|
||||
"description": "Alert configuration was changed",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Task"
|
||||
"$ref": "#/definitions/Rule"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Happens when trying to access a non-existent data source, kapacitor, or task.",
|
||||
"description": "Happens when trying to access a non-existent data source, kapacitor, or rule.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
|
@ -1009,6 +1113,11 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"rules"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -1025,20 +1134,20 @@
|
|||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "task_id",
|
||||
"name": "rule_id",
|
||||
"in": "path",
|
||||
"type": "string",
|
||||
"description": "ID of the task",
|
||||
"description": "ID of the rule",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"summary": "This specific alert task will be removed.",
|
||||
"summary": "This specific alert rule will be removed.",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Alert task has been removed."
|
||||
"description": "Alert rule has been removed."
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown Data source, Kapacitor id, or alert task",
|
||||
"description": "Unknown Data source, Kapacitor id, or alert rule",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
|
@ -1054,6 +1163,11 @@
|
|||
},
|
||||
"/sources/{id}/kapacitors/{kapa_id}/proxy": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"proxy"
|
||||
],
|
||||
"description": "GET to `path` of kapacitor. The response and status code from kapacitor is directly returned.",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1097,6 +1211,11 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"proxy"
|
||||
],
|
||||
"description": "DELETE to `path` of kapacitor. The response and status code from kapacitor is directly returned.",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1140,6 +1259,11 @@
|
|||
}
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"proxy"
|
||||
],
|
||||
"description": "PATCH body directly to configured kapacitor. The response and status code from kapacitor is directly returned.",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1192,6 +1316,11 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"proxy"
|
||||
],
|
||||
"description": "POST body directly to configured kapacitor. The response and status code from kapacitor is directly returned.",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1246,6 +1375,10 @@
|
|||
},
|
||||
"/mappings": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"layouts",
|
||||
"mappings"
|
||||
],
|
||||
"summary": "Mappings between app names and measurements",
|
||||
"description": "Mappings provide a means to alias measurement names found within a telegraf database and application layouts found within Chronograf\n",
|
||||
"responses": {
|
||||
|
@ -1266,6 +1399,9 @@
|
|||
},
|
||||
"/layouts": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"layouts"
|
||||
],
|
||||
"summary": "Pre-configured layouts",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1308,6 +1444,9 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"layouts"
|
||||
],
|
||||
"summary": "Create new layout",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1344,6 +1483,9 @@
|
|||
},
|
||||
"/layouts/{id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"layouts"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -1377,6 +1519,9 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"layouts"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -1406,6 +1551,9 @@
|
|||
}
|
||||
},
|
||||
"put": {
|
||||
"tags": [
|
||||
"layouts"
|
||||
],
|
||||
"summary": "Replace layout configuration.",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1505,9 +1653,9 @@
|
|||
"description": "URL location of proxy endpoint for this kapacitor",
|
||||
"format": "url"
|
||||
},
|
||||
"tasks": {
|
||||
"rules": {
|
||||
"type": "string",
|
||||
"description": "URL location of tasks endpoint for this kapacitor",
|
||||
"description": "URL location of rules endpoint for this kapacitor",
|
||||
"format": "url"
|
||||
}
|
||||
}
|
||||
|
@ -1522,6 +1670,101 @@
|
|||
"description": "Entire response from the kapacitor backend.",
|
||||
"type": "object"
|
||||
},
|
||||
"Rules": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"rules"
|
||||
],
|
||||
"properties": {
|
||||
"rules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Rule"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rule": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"every",
|
||||
"trigger"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "ID for this rule; the ID is shared with kapacitor"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "User facing name of the alerting rule"
|
||||
},
|
||||
"every": {
|
||||
"type": "string",
|
||||
"description": "Golang duration string specifying how often the alert condition is checked"
|
||||
},
|
||||
"alerts": {
|
||||
"type": "array",
|
||||
"description": "Array of alerting services to warn if the alert is triggered",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"hipchat",
|
||||
"opsgenie",
|
||||
"pagerduty",
|
||||
"victorops",
|
||||
"smtp",
|
||||
"email",
|
||||
"sensu",
|
||||
"slack",
|
||||
"talk",
|
||||
"telegram"
|
||||
]
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Message to send when alert occurs."
|
||||
},
|
||||
"trigger": {
|
||||
"type": "string",
|
||||
"description": "Trigger defines the alerting structure; deadman alert if no data are received for the specified time range; relative alert if the data change relative to the data in a different time range; threshold alert if the data cross a boundary",
|
||||
"enum": [
|
||||
"deadman",
|
||||
"relative",
|
||||
"threshold"
|
||||
]
|
||||
},
|
||||
"tickscript": {
|
||||
"type": "string",
|
||||
"description": "TICKscript representing this rule"
|
||||
},
|
||||
"links": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"self",
|
||||
"kapacitor"
|
||||
],
|
||||
"properties": {
|
||||
"self": {
|
||||
"description": "Self link pointing to this rule resource",
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"kapacitor": {
|
||||
"description": "Link pointing to the kapacitor proxy for this rule including the path query parameter.",
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"output": {
|
||||
"description": "Link pointing to the kapacitor httpOut node of the tickscript; includes the path query argument",
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Sources": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
@ -1690,12 +1933,12 @@
|
|||
"User": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"username"
|
||||
"email"
|
||||
],
|
||||
"properties": {
|
||||
"username": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"maxLength": 64
|
||||
"maxLength": 254
|
||||
},
|
||||
"link": {
|
||||
"$ref": "#/definitions/Link"
|
||||
|
@ -1782,12 +2025,18 @@
|
|||
"Cell": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"i",
|
||||
"x",
|
||||
"y",
|
||||
"w",
|
||||
"h"
|
||||
],
|
||||
"properties": {
|
||||
"i": {
|
||||
"description": "Unique ID of Cell",
|
||||
"type": "string",
|
||||
"format": "uuid4"
|
||||
},
|
||||
"x": {
|
||||
"description": "X-coordinate of Cell in the Layout",
|
||||
"type": "integer",
|
||||
|
@ -1823,6 +2072,30 @@
|
|||
"query"
|
||||
],
|
||||
"properties": {
|
||||
"label": {
|
||||
"description": "Optional Y-axis user-facing label for this query",
|
||||
"type": "string"
|
||||
},
|
||||
"range": {
|
||||
"description": "Optional default range of the Y-axis",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"upper",
|
||||
"lower"
|
||||
],
|
||||
"properties": {
|
||||
"upper": {
|
||||
"description": "Upper bound of the display range of the Y-axis",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"lower": {
|
||||
"description": "Lower bound of the display range of the Y-axis",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -1850,6 +2123,11 @@
|
|||
"type": "string",
|
||||
"format": "url"
|
||||
},
|
||||
"me": {
|
||||
"description": "Location of the me endpoint.",
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
},
|
||||
"layouts": {
|
||||
"description": "Location of the layouts endpoint",
|
||||
"type": "string",
|
||||
|
|
|
@ -33,7 +33,7 @@ describe('timeSeriesToDygraph', () => {
|
|||
const actual = timeSeriesToDygraph(influxResponse);
|
||||
|
||||
const expected = {
|
||||
fields: [
|
||||
labels: [
|
||||
'time',
|
||||
`m1.f1`,
|
||||
`m1.f2`,
|
||||
|
@ -43,6 +43,14 @@ describe('timeSeriesToDygraph', () => {
|
|||
[new Date(2000), 2, 3],
|
||||
[new Date(4000), null, 4],
|
||||
],
|
||||
dygraphSeries: {
|
||||
'm1.f1': {
|
||||
axis: 'y',
|
||||
},
|
||||
'm1.f2': {
|
||||
axis: 'y',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(actual).to.deep.equal(expected);
|
||||
|
@ -72,7 +80,7 @@ describe('timeSeriesToDygraph', () => {
|
|||
const actual = timeSeriesToDygraph(influxResponse);
|
||||
|
||||
const expected = {
|
||||
fields: [
|
||||
labels: [
|
||||
'time',
|
||||
'm1.f1',
|
||||
],
|
||||
|
@ -83,6 +91,81 @@ describe('timeSeriesToDygraph', () => {
|
|||
],
|
||||
};
|
||||
|
||||
expect(actual).to.deep.equal(expected);
|
||||
expect(actual.timeSeries).to.deep.equal(expected.timeSeries);
|
||||
});
|
||||
|
||||
it('can parse multiple responses into two axes', () => {
|
||||
const influxResponse = [
|
||||
{
|
||||
"response":
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"series": [
|
||||
{
|
||||
"name":"m1",
|
||||
"columns": ["time","f1"],
|
||||
"values": [[1000, 1],[2000, 2]],
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"series": [
|
||||
{
|
||||
"name":"m1",
|
||||
"columns": ["time","f2"],
|
||||
"values": [[2000, 3],[4000, 4]],
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"response":
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"series": [
|
||||
{
|
||||
"name":"m3",
|
||||
"columns": ["time","f3"],
|
||||
"values": [[1000, 1],[2000, 2]],
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const actual = timeSeriesToDygraph(influxResponse);
|
||||
|
||||
const expected = {
|
||||
labels: [
|
||||
'time',
|
||||
`m1.f1`,
|
||||
`m1.f2`,
|
||||
`m3.f3`,
|
||||
],
|
||||
timeSeries: [
|
||||
[new Date(1000), 1, null, 1],
|
||||
[new Date(2000), 2, 3, 2],
|
||||
[new Date(4000), null, 4, null],
|
||||
],
|
||||
dygraphSeries: {
|
||||
'm1.f1': {
|
||||
axis: 'y',
|
||||
},
|
||||
'm1.f2': {
|
||||
axis: 'y',
|
||||
},
|
||||
'm3.f3': {
|
||||
axis: 'y2',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(actual.dygraphSeries).to.deep.equal(expected.dygraphSeries);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,6 @@ import TimeRangeDropdown from '../../shared/components/TimeRangeDropdown';
|
|||
import timeRanges from 'hson!../../shared/data/timeRanges.hson';
|
||||
import {getMappings, getAppsForHosts, getMeasurementsForHost} from 'src/hosts/apis';
|
||||
import {fetchLayouts} from 'shared/apis';
|
||||
import _ from 'lodash';
|
||||
|
||||
export const HostPage = React.createClass({
|
||||
propTypes: {
|
||||
|
@ -34,7 +33,7 @@ export const HostPage = React.createClass({
|
|||
},
|
||||
|
||||
componentDidMount() {
|
||||
const {source, params} = this.props;
|
||||
const {source, params, location} = this.props;
|
||||
const hosts = {[params.hostID]: {name: params.hostID}};
|
||||
|
||||
// fetching layouts and mappings can be done at the same time
|
||||
|
@ -44,7 +43,7 @@ export const HostPage = React.createClass({
|
|||
getMeasurementsForHost(source, params.hostID).then((measurements) => {
|
||||
const host = newHosts[this.props.params.hostID];
|
||||
const filteredLayouts = layouts.filter((layout) => {
|
||||
const focusedApp = this.props.location.query.app;
|
||||
const focusedApp = location.query.app;
|
||||
if (focusedApp) {
|
||||
return layout.app === focusedApp;
|
||||
}
|
||||
|
@ -68,38 +67,28 @@ export const HostPage = React.createClass({
|
|||
const {timeRange} = this.state;
|
||||
const {source} = this.props;
|
||||
|
||||
const autoflowLayouts = _.remove(layouts, (layout) => {
|
||||
return layout.autoflow === true;
|
||||
});
|
||||
let autoflowCells = [];
|
||||
const autoflowLayouts = layouts.filter((layout) => !!layout.autoflow);
|
||||
|
||||
const cellWidth = 4;
|
||||
const cellHeight = 4;
|
||||
const pageWidth = 12;
|
||||
|
||||
autoflowLayouts.forEach((layout, i) => {
|
||||
layout.cells.forEach((cell, j) => {
|
||||
cell.w = cellWidth;
|
||||
cell.h = cellHeight;
|
||||
cell.x = ((i + j) * cellWidth % pageWidth);
|
||||
cell.y = Math.floor(((i + j) * cellWidth / pageWidth)) * cellHeight;
|
||||
autoflowCells = autoflowCells.concat(cell);
|
||||
});
|
||||
});
|
||||
const autoflowCells = autoflowLayouts.reduce((allCells, layout, i) => {
|
||||
return allCells.concat(layout.cells.map((cell, j) => {
|
||||
return Object.assign(cell, {
|
||||
w: cellWidth,
|
||||
h: cellHeight,
|
||||
x: ((i + j) * cellWidth % pageWidth),
|
||||
y: Math.floor(((i + j) * cellWidth / pageWidth)) * cellHeight,
|
||||
});
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const autoflowLayout = {
|
||||
cells: autoflowCells,
|
||||
autoflow: false,
|
||||
};
|
||||
const staticLayouts = layouts.filter((layout) => !layout.autoflow);
|
||||
staticLayouts.unshift({cells: autoflowCells});
|
||||
|
||||
const staticLayouts = _.remove(layouts, (layout) => {
|
||||
return layout.autoflow === false;
|
||||
});
|
||||
staticLayouts.unshift(autoflowLayout);
|
||||
|
||||
let layoutCells = [];
|
||||
let translateY = 0;
|
||||
staticLayouts.forEach((layout) => {
|
||||
const layoutCells = staticLayouts.reduce((allCells, layout) => {
|
||||
let maxY = 0;
|
||||
layout.cells.forEach((cell) => {
|
||||
cell.y += translateY;
|
||||
|
@ -113,9 +102,8 @@ export const HostPage = React.createClass({
|
|||
});
|
||||
translateY = maxY;
|
||||
|
||||
layoutCells = layoutCells.concat(layout.cells);
|
||||
});
|
||||
|
||||
return allCells.concat(layout.cells);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<LayoutRenderer
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, {PropTypes} from 'react';
|
|||
import Dygraph from '../../external/dygraph';
|
||||
import 'style/_Graph.css';
|
||||
|
||||
const {arrayOf, object, array, number, bool} = PropTypes;
|
||||
const {arrayOf, object, array, number, bool, shape} = PropTypes;
|
||||
|
||||
const LINE_COLORS = [
|
||||
'#00C9FF',
|
||||
|
@ -25,13 +25,17 @@ export default React.createClass({
|
|||
displayName: 'Dygraph',
|
||||
|
||||
propTypes: {
|
||||
yRange: arrayOf(number.isRequired),
|
||||
ranges: shape({
|
||||
y: arrayOf(number.isRequired),
|
||||
y2: arrayOf(number.isRequired),
|
||||
}),
|
||||
timeSeries: array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
fields: array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
labels: array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
options: object, // eslint-disable-line react/forbid-prop-types
|
||||
containerStyle: object, // eslint-disable-line react/forbid-prop-types
|
||||
isGraphFilled: bool,
|
||||
overrideLineColors: array,
|
||||
dygraphSeries: shape({}).isRequired,
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
|
@ -50,7 +54,8 @@ export default React.createClass({
|
|||
|
||||
componentDidMount() {
|
||||
const timeSeries = this.getTimeSeries();
|
||||
const {yRange} = this.props;
|
||||
// dygraphSeries is a legend label and its corresponding y-axis e.g. {legendLabel1: 'y', legendLabel2: 'y2'};
|
||||
const {ranges, dygraphSeries} = this.props;
|
||||
|
||||
const refs = this.refs;
|
||||
const graphContainerNode = refs.graphContainer;
|
||||
|
@ -75,7 +80,15 @@ export default React.createClass({
|
|||
strokeWidth: 1.5,
|
||||
highlightCircleSize: 3,
|
||||
colors: finalLineColors,
|
||||
valueRange: getRange(timeSeries, yRange),
|
||||
series: dygraphSeries,
|
||||
axes: {
|
||||
y: {
|
||||
valueRange: getRange(timeSeries, ranges.y),
|
||||
},
|
||||
y2: {
|
||||
valueRange: getRange(timeSeries, ranges.y2),
|
||||
},
|
||||
},
|
||||
highlightSeriesOpts: {
|
||||
strokeWidth: 2,
|
||||
highlightCircleSize: 5,
|
||||
|
@ -130,12 +143,19 @@ export default React.createClass({
|
|||
}
|
||||
|
||||
const timeSeries = this.getTimeSeries();
|
||||
const {fields, yRange} = this.props;
|
||||
const {labels, ranges} = this.props;
|
||||
|
||||
dygraph.updateOptions({
|
||||
labels: fields,
|
||||
labels,
|
||||
file: timeSeries,
|
||||
valueRange: getRange(timeSeries, yRange),
|
||||
axes: {
|
||||
y: {
|
||||
valueRange: getRange(timeSeries, ranges.y),
|
||||
},
|
||||
y2: {
|
||||
valueRange: getRange(timeSeries, ranges.y2),
|
||||
},
|
||||
},
|
||||
underlayCallback: this.props.options.underlayCallback,
|
||||
});
|
||||
|
||||
|
|
|
@ -17,7 +17,12 @@ export const LayoutRenderer = React.createClass({
|
|||
PropTypes.shape({
|
||||
queries: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
rp: PropTypes.string.isRequired,
|
||||
label: PropTypes.string,
|
||||
range: PropTypes.shape({
|
||||
upper: PropTypes.number,
|
||||
lower: PropTypes.number,
|
||||
}),
|
||||
rp: PropTypes.string,
|
||||
text: PropTypes.string.isRequired,
|
||||
database: PropTypes.string.isRequired,
|
||||
groupbys: PropTypes.arrayOf(PropTypes.string),
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
import React, {PropTypes} from 'react';
|
||||
import Dygraph from './Dygraph';
|
||||
import shallowCompare from 'react-addons-shallow-compare';
|
||||
import _ from 'lodash';
|
||||
|
||||
import timeSeriesToDygraph from 'utils/timeSeriesToDygraph';
|
||||
|
||||
const {array, string, arrayOf, number, bool} = PropTypes;
|
||||
const {array, string, arrayOf, bool, shape} = PropTypes;
|
||||
|
||||
export default React.createClass({
|
||||
displayName: 'LineGraph',
|
||||
propTypes: {
|
||||
data: array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
data: arrayOf(shape({}).isRequired).isRequired,
|
||||
title: string,
|
||||
isFetchingInitially: PropTypes.bool,
|
||||
isRefreshing: PropTypes.bool,
|
||||
yRange: arrayOf(number.isRequired),
|
||||
underlayCallback: PropTypes.func,
|
||||
isGraphFilled: bool,
|
||||
overrideLineColors: array,
|
||||
queries: arrayOf(shape({}).isRequired).isRequired,
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
|
@ -30,19 +31,22 @@ export default React.createClass({
|
|||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return shallowCompare(this, nextProps, nextState);
|
||||
},
|
||||
|
||||
componentWillMount() {
|
||||
this._timeSeries = timeSeriesToDygraph(this.props.data);
|
||||
},
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
if (this.props.data !== nextProps.data) {
|
||||
this._timeSeries = timeSeriesToDygraph(nextProps.data);
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const {fields, timeSeries} = this._timeSeries;
|
||||
|
||||
render() {
|
||||
const {isFetchingInitially, title, underlayCallback, queries} = this.props;
|
||||
const {labels, timeSeries, dygraphSeries} = this._timeSeries;
|
||||
// If data for this graph is being fetched for the first time, show a graph-wide spinner.
|
||||
if (this.props.isFetchingInitially) {
|
||||
if (isFetchingInitially) {
|
||||
return (
|
||||
<div className="graph-panel__graph-fetching">
|
||||
<h3 className="graph-panel__spinner" />
|
||||
|
@ -51,24 +55,56 @@ export default React.createClass({
|
|||
}
|
||||
|
||||
const options = {
|
||||
labels: fields,
|
||||
labels,
|
||||
connectSeparatedPoints: true,
|
||||
labelsKMB: true,
|
||||
height: 300,
|
||||
axisLineColor: '#383846',
|
||||
gridLineColor: '#383846',
|
||||
title: this.props.title,
|
||||
title,
|
||||
rightGap: 0,
|
||||
yRangePad: 10,
|
||||
drawAxesAtZero: true,
|
||||
underlayCallback: this.props.underlayCallback,
|
||||
underlayCallback,
|
||||
ylabel: _.get(queries, ['0', 'label'], ''),
|
||||
y2label: _.get(queries, ['1', 'label'], ''),
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{this.props.isRefreshing ? <h3 className="graph-panel__spinner--small" /> : null}
|
||||
<Dygraph containerStyle={{width: '100%', height: '300px'}} overrideLineColors={this.props.overrideLineColors} isGraphFilled={this.props.isGraphFilled} timeSeries={timeSeries} fields={fields} options={options} />
|
||||
<Dygraph
|
||||
containerStyle={{width: '100%', height: '300px'}}
|
||||
overrideLineColors={this.props.overrideLineColors}
|
||||
isGraphFilled={this.props.isGraphFilled}
|
||||
timeSeries={timeSeries}
|
||||
labels={labels}
|
||||
options={options}
|
||||
dygraphSeries={dygraphSeries}
|
||||
ranges={this.getRanges()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
getRanges() {
|
||||
const {queries} = this.props;
|
||||
if (!queries) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const ranges = {};
|
||||
const q0 = queries[0];
|
||||
const q1 = queries[1];
|
||||
|
||||
if (q0 && q0.range) {
|
||||
ranges.y = [q0.range.lower, q0.range.upper];
|
||||
}
|
||||
|
||||
if (q1 && q1.range) {
|
||||
ranges.y2 = [q1.range.lower, q1.range.upper];
|
||||
}
|
||||
|
||||
return ranges;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
*/
|
||||
|
||||
export default function timeSeriesToDygraph(raw = []) {
|
||||
const fields = ['time']; // all of the effective field names (i.e. <measurement>.<field>)
|
||||
const labels = ['time']; // all of the effective field names (i.e. <measurement>.<field>)
|
||||
const fieldToIndex = {}; // see parseSeries
|
||||
const dates = {}; // map of date as string to date value to minimize string coercion
|
||||
const dygraphSeries = {}; // dygraphSeries is a graph legend label and its corresponding y-axis e.g. {legendLabel1: 'y', legendLabel2: 'y2'};
|
||||
|
||||
/**
|
||||
* dateToFieldValue will look like:
|
||||
|
@ -26,7 +27,7 @@ export default function timeSeriesToDygraph(raw = []) {
|
|||
*/
|
||||
const dateToFieldValue = {};
|
||||
|
||||
raw.forEach(({response}) => {
|
||||
raw.forEach(({response}, queryIndex) => {
|
||||
// If a response is an empty result set or a query returned an error
|
||||
// from InfluxDB, don't try and parse.
|
||||
if (response.results.length) {
|
||||
|
@ -87,8 +88,9 @@ export default function timeSeriesToDygraph(raw = []) {
|
|||
|
||||
// Given a field name, identify which column in the timeSeries result should hold the field's value
|
||||
// ex given this timeSeries [Date, 10, 20, 30] field index at 2 would correspond to value 20
|
||||
fieldToIndex[effectiveFieldName] = fields.length;
|
||||
fields.push(effectiveFieldName);
|
||||
fieldToIndex[effectiveFieldName] = labels.length;
|
||||
labels.push(effectiveFieldName);
|
||||
dygraphSeries[effectiveFieldName] = {axis: queryIndex === 0 ? 'y' : 'y2'};
|
||||
});
|
||||
|
||||
(series.values || []).forEach(parseRow);
|
||||
|
@ -121,7 +123,7 @@ export default function timeSeriesToDygraph(raw = []) {
|
|||
function buildTimeSeries() {
|
||||
const allDates = Object.keys(dateToFieldValue);
|
||||
allDates.sort((a, b) => a - b);
|
||||
const rowLength = fields.length;
|
||||
const rowLength = labels.length;
|
||||
return allDates.map((date) => {
|
||||
const row = new Array(rowLength);
|
||||
|
||||
|
@ -138,8 +140,9 @@ export default function timeSeriesToDygraph(raw = []) {
|
|||
}
|
||||
|
||||
return {
|
||||
fields,
|
||||
labels,
|
||||
timeSeries: buildTimeSeries(),
|
||||
dygraphSeries,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue