Merge branch 'master' into presentational-page-components

pull/10616/head
Alex P 2018-06-20 11:36:35 -07:00
commit dda369c24b
86 changed files with 2005 additions and 1459 deletions

View File

@ -22,6 +22,7 @@
1. [#3649](https://github.com/influxdata/chronograf/pull/3649): Fix erroneous icons in Date Picker widget
1. [#3697](https://github.com/influxdata/chronograf/pull/3697): Fix allowing hyphens in basepath
1. [#3698](https://github.com/influxdata/chronograf/pull/3698): Fix error in cell when tempVar returns no values
1. [#3733](https://github.com/influxdata/chronograf/pull/3733): Change arrows in table columns so that ascending sort points up and descending points down
## v1.5.0.0 [2018-05-15-RC]

View File

@ -341,6 +341,7 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) {
Selected: v.Selected,
Type: v.Type,
Value: v.Value,
Key: v.Key,
}
}
@ -522,6 +523,7 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error {
Selected: v.Selected,
Type: v.Type,
Value: v.Value,
Key: v.Key,
}
}

View File

@ -640,6 +640,7 @@ type TemplateValue struct {
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
Selected bool `protobuf:"varint,3,opt,name=selected,proto3" json:"selected,omitempty"`
Key string `protobuf:"bytes,4,opt,name=key,proto3" json:"key,omitempty"`
}
func (m *TemplateValue) Reset() { *m = TemplateValue{} }
@ -668,6 +669,13 @@ func (m *TemplateValue) GetSelected() bool {
return false
}
func (m *TemplateValue) GetKey() string {
if m != nil {
return m.Key
}
return ""
}
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"`
@ -1388,111 +1396,111 @@ func init() {
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
var fileDescriptorInternal = []byte{
// 1685 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x5b, 0x8f, 0xe3, 0x48,
0x15, 0x96, 0x93, 0x38, 0x89, 0x4f, 0xd2, 0xbd, 0x2d, 0x33, 0xda, 0x35, 0x0b, 0x42, 0xc1, 0xe2,
0xd2, 0x5c, 0x76, 0x58, 0xf5, 0x0a, 0x09, 0xad, 0x76, 0x57, 0xea, 0xcb, 0xce, 0xd0, 0x73, 0xed,
0xa9, 0x74, 0x0f, 0x4f, 0x68, 0x55, 0xb1, 0x2b, 0x49, 0x69, 0x1d, 0xdb, 0x94, 0xcb, 0xdd, 0x6d,
0x9e, 0xf9, 0x1d, 0x48, 0x48, 0xf0, 0x8e, 0x10, 0x8f, 0x48, 0xbc, 0xf3, 0x03, 0xf8, 0x2b, 0xbc,
0xa2, 0x53, 0x17, 0xa7, 0xdc, 0x9d, 0x19, 0x0d, 0x12, 0xda, 0xb7, 0xfa, 0xce, 0x39, 0x39, 0x55,
0x75, 0x2e, 0x5f, 0x1d, 0x07, 0xf6, 0x79, 0x2e, 0x99, 0xc8, 0x69, 0xf6, 0xb0, 0x14, 0x85, 0x2c,
0xc2, 0xb1, 0xc5, 0xf1, 0x1f, 0xfa, 0x30, 0x9c, 0x17, 0xb5, 0x48, 0x58, 0xb8, 0x0f, 0xbd, 0xf3,
0xb3, 0xc8, 0x9b, 0x79, 0x87, 0x7d, 0xd2, 0x3b, 0x3f, 0x0b, 0x43, 0x18, 0xbc, 0xa0, 0x1b, 0x16,
0xf5, 0x66, 0xde, 0x61, 0x40, 0xd4, 0x1a, 0x65, 0x97, 0x4d, 0xc9, 0xa2, 0xbe, 0x96, 0xe1, 0x3a,
0xfc, 0x10, 0xc6, 0x57, 0x15, 0x7a, 0xdb, 0xb0, 0x68, 0xa0, 0xe4, 0x2d, 0x46, 0xdd, 0x05, 0xad,
0xaa, 0x9b, 0x42, 0xa4, 0x91, 0xaf, 0x75, 0x16, 0x87, 0x07, 0xd0, 0xbf, 0x22, 0xcf, 0xa2, 0xa1,
0x12, 0xe3, 0x32, 0x8c, 0x60, 0x74, 0xc6, 0x96, 0xb4, 0xce, 0x64, 0x34, 0x9a, 0x79, 0x87, 0x63,
0x62, 0x21, 0xfa, 0xb9, 0x64, 0x19, 0x5b, 0x09, 0xba, 0x8c, 0xc6, 0xda, 0x8f, 0xc5, 0xe1, 0x43,
0x08, 0xcf, 0xf3, 0x8a, 0x25, 0xb5, 0x60, 0xf3, 0xaf, 0x79, 0xf9, 0x9a, 0x09, 0xbe, 0x6c, 0xa2,
0x40, 0x39, 0xd8, 0xa1, 0xc1, 0x5d, 0x9e, 0x33, 0x49, 0x71, 0x6f, 0x50, 0xae, 0x2c, 0x0c, 0x63,
0x98, 0xce, 0xd7, 0x54, 0xb0, 0x74, 0xce, 0x12, 0xc1, 0x64, 0x34, 0x51, 0xea, 0x8e, 0x0c, 0x6d,
0x5e, 0x8a, 0x15, 0xcd, 0xf9, 0xef, 0xa9, 0xe4, 0x45, 0x1e, 0x4d, 0xb5, 0x8d, 0x2b, 0xc3, 0x28,
0x91, 0x22, 0x63, 0xd1, 0x9e, 0x8e, 0x12, 0xae, 0xc3, 0xef, 0x42, 0x60, 0x2e, 0x43, 0x2e, 0xa2,
0x7d, 0xa5, 0xd8, 0x0a, 0xe2, 0xbf, 0x7b, 0x10, 0x9c, 0xd1, 0x6a, 0xbd, 0x28, 0xa8, 0x48, 0xdf,
0x29, 0x13, 0x1f, 0x81, 0x9f, 0xb0, 0x2c, 0xab, 0xa2, 0xfe, 0xac, 0x7f, 0x38, 0x39, 0xfa, 0xe0,
0x61, 0x9b, 0xe2, 0xd6, 0xcf, 0x29, 0xcb, 0x32, 0xa2, 0xad, 0xc2, 0x8f, 0x21, 0x90, 0x6c, 0x53,
0x66, 0x54, 0xb2, 0x2a, 0x1a, 0xa8, 0x9f, 0x84, 0xdb, 0x9f, 0x5c, 0x1a, 0x15, 0xd9, 0x1a, 0xdd,
0xbb, 0xa8, 0x7f, 0xff, 0xa2, 0xf1, 0xbf, 0x07, 0xb0, 0xd7, 0xd9, 0x2e, 0x9c, 0x82, 0x77, 0xab,
0x4e, 0xee, 0x13, 0xef, 0x16, 0x51, 0xa3, 0x4e, 0xed, 0x13, 0xaf, 0x41, 0x74, 0xa3, 0x2a, 0xc7,
0x27, 0xde, 0x0d, 0xa2, 0xb5, 0xaa, 0x17, 0x9f, 0x78, 0xeb, 0xf0, 0x27, 0x30, 0xfa, 0x5d, 0xcd,
0x04, 0x67, 0x55, 0xe4, 0xab, 0xd3, 0xbd, 0xb7, 0x3d, 0xdd, 0xab, 0x9a, 0x89, 0x86, 0x58, 0x3d,
0x46, 0x43, 0xd5, 0x9a, 0x2e, 0x1c, 0xb5, 0x46, 0x99, 0xc4, 0xba, 0x1c, 0x69, 0x19, 0xae, 0x4d,
0x14, 0x75, 0xb5, 0x60, 0x14, 0x7f, 0x09, 0x03, 0x7a, 0xcb, 0xaa, 0x28, 0x50, 0xfe, 0xbf, 0xff,
0x86, 0x80, 0x3d, 0x3c, 0xbe, 0x65, 0xd5, 0x97, 0xb9, 0x14, 0x0d, 0x51, 0xe6, 0xe1, 0x8f, 0x61,
0x98, 0x14, 0x59, 0x21, 0xaa, 0x08, 0xee, 0x1e, 0xec, 0x14, 0xe5, 0xc4, 0xa8, 0xc3, 0x43, 0x18,
0x66, 0x6c, 0xc5, 0xf2, 0x54, 0xd5, 0xcd, 0xe4, 0xe8, 0x60, 0x6b, 0xf8, 0x4c, 0xc9, 0x89, 0xd1,
0x87, 0x9f, 0xc2, 0x54, 0xd2, 0x45, 0xc6, 0x5e, 0x96, 0x18, 0xc5, 0x4a, 0xd5, 0xd0, 0xe4, 0xe8,
0x7d, 0x27, 0x1f, 0x8e, 0x96, 0x74, 0x6c, 0xc3, 0xcf, 0x60, 0xba, 0xe4, 0x2c, 0x4b, 0xed, 0x6f,
0xf7, 0xd4, 0xa1, 0xa2, 0xed, 0x6f, 0x09, 0xcb, 0xe9, 0x06, 0x7f, 0xf1, 0x08, 0xcd, 0x48, 0xc7,
0x3a, 0xfc, 0x1e, 0x80, 0xe4, 0x1b, 0xf6, 0xa8, 0x10, 0x1b, 0x2a, 0x4d, 0x19, 0x3a, 0x92, 0xf0,
0x73, 0xd8, 0x4b, 0x59, 0xc2, 0x37, 0x34, 0xbb, 0xc8, 0x68, 0xc2, 0xaa, 0xe8, 0x3d, 0x75, 0x34,
0xb7, 0xba, 0x5c, 0x35, 0xe9, 0x5a, 0x7f, 0xf8, 0x18, 0x82, 0x36, 0x7c, 0xd8, 0xdf, 0x5f, 0xb3,
0x46, 0x15, 0x43, 0x40, 0x70, 0x19, 0xfe, 0x00, 0xfc, 0x6b, 0x9a, 0xd5, 0xba, 0x90, 0x27, 0x47,
0xfb, 0x5b, 0xaf, 0xc7, 0xb7, 0xbc, 0x22, 0x5a, 0xf9, 0x69, 0xef, 0x57, 0x5e, 0xfc, 0x18, 0xf6,
0x3a, 0x1b, 0xe1, 0xc1, 0x79, 0xf5, 0x65, 0xbe, 0x2c, 0x44, 0xc2, 0x52, 0xe5, 0x73, 0x4c, 0x1c,
0x49, 0xf8, 0x3e, 0x0c, 0x53, 0xbe, 0xe2, 0xb2, 0x32, 0xe5, 0x66, 0x50, 0xfc, 0x0f, 0x0f, 0xa6,
0x6e, 0x34, 0xc3, 0x9f, 0xc2, 0xc1, 0x35, 0x13, 0x92, 0x27, 0x34, 0xbb, 0xe4, 0x1b, 0x86, 0x1b,
0xab, 0x9f, 0x8c, 0xc9, 0x3d, 0x79, 0xf8, 0x31, 0x0c, 0xab, 0x42, 0xc8, 0x93, 0x46, 0x55, 0xed,
0xdb, 0xa2, 0x6c, 0xec, 0x90, 0xa7, 0x6e, 0x04, 0x2d, 0x4b, 0x9e, 0xaf, 0x2c, 0x17, 0x5a, 0x1c,
0xfe, 0x08, 0xf6, 0x97, 0xfc, 0xf6, 0x11, 0x17, 0x95, 0x3c, 0x2d, 0xb2, 0x7a, 0x93, 0xab, 0x0a,
0x1e, 0x93, 0x3b, 0xd2, 0x27, 0x83, 0xb1, 0x77, 0xd0, 0x7b, 0x32, 0x18, 0xfb, 0x07, 0xc3, 0xb8,
0x84, 0xfd, 0xee, 0x4e, 0xd8, 0x96, 0xf6, 0x10, 0x8a, 0x13, 0x74, 0x78, 0x3b, 0xb2, 0x70, 0x06,
0x93, 0x94, 0x57, 0x65, 0x46, 0x1b, 0x87, 0x36, 0x5c, 0x11, 0x72, 0xe0, 0x35, 0xaf, 0xf8, 0x22,
0xd3, 0x54, 0x3e, 0x26, 0x16, 0xc6, 0x2b, 0xf0, 0x55, 0x59, 0x3b, 0x24, 0x14, 0x58, 0x12, 0x52,
0xd4, 0xdf, 0x73, 0xa8, 0xff, 0x00, 0xfa, 0xbf, 0x66, 0xb7, 0xe6, 0x35, 0xc0, 0x65, 0x4b, 0x55,
0x03, 0x87, 0xaa, 0x1e, 0x80, 0xff, 0x5a, 0xa5, 0x5d, 0x53, 0x88, 0x06, 0xf1, 0x17, 0x30, 0xd4,
0x6d, 0xd1, 0x7a, 0xf6, 0x1c, 0xcf, 0x33, 0x98, 0xbc, 0x14, 0x9c, 0xe5, 0x52, 0x93, 0x8f, 0xb9,
0x82, 0x23, 0x8a, 0xff, 0xe6, 0xc1, 0x40, 0x65, 0x29, 0x86, 0x69, 0xc6, 0x56, 0x34, 0x69, 0x4e,
0x8a, 0x3a, 0x4f, 0xab, 0xc8, 0x9b, 0xf5, 0x0f, 0xfb, 0xa4, 0x23, 0xc3, 0xf2, 0x58, 0x68, 0x6d,
0x6f, 0xd6, 0x3f, 0x0c, 0x88, 0x41, 0x78, 0xb4, 0x8c, 0x2e, 0x58, 0x66, 0xae, 0xa0, 0x01, 0x5a,
0x97, 0x82, 0x2d, 0xf9, 0xad, 0xb9, 0x86, 0x41, 0x28, 0xaf, 0xea, 0x25, 0xca, 0xf5, 0x4d, 0x0c,
0xc2, 0x0b, 0x2c, 0x68, 0xd5, 0x32, 0x12, 0xae, 0xd1, 0x73, 0x95, 0xd0, 0xcc, 0x52, 0x92, 0x06,
0xf1, 0x3f, 0x3d, 0x7c, 0xc8, 0x34, 0xc5, 0xde, 0x8b, 0xf0, 0xb7, 0x61, 0x8c, 0xf4, 0xfb, 0xd5,
0x35, 0x15, 0xe6, 0xc2, 0x23, 0xc4, 0xaf, 0xa9, 0x08, 0x7f, 0x01, 0x43, 0xd5, 0x1c, 0x3b, 0xe8,
0xde, 0xba, 0x53, 0x51, 0x25, 0xc6, 0xac, 0x25, 0xc4, 0x81, 0x43, 0x88, 0xed, 0x65, 0x7d, 0xf7,
0xb2, 0x1f, 0x81, 0x8f, 0xcc, 0xda, 0xa8, 0xd3, 0xef, 0xf4, 0xac, 0xf9, 0x57, 0x5b, 0xc5, 0x57,
0xb0, 0xd7, 0xd9, 0xb1, 0xdd, 0xc9, 0xeb, 0xee, 0xb4, 0x6d, 0xf4, 0xc0, 0x34, 0x36, 0x36, 0x47,
0xc5, 0x32, 0x96, 0x48, 0x96, 0x9a, 0xaa, 0x6b, 0x71, 0xfc, 0x27, 0x6f, 0xeb, 0x57, 0xed, 0x87,
0x25, 0x9a, 0x14, 0x9b, 0x0d, 0xcd, 0x53, 0xe3, 0xda, 0x42, 0x8c, 0x5b, 0xba, 0x30, 0xae, 0x7b,
0xe9, 0x02, 0xb1, 0x28, 0x4d, 0x06, 0x7b, 0xa2, 0xc4, 0xda, 0xd9, 0x30, 0x5a, 0xd5, 0x82, 0x6d,
0x58, 0x2e, 0x4d, 0x08, 0x5c, 0x51, 0xf8, 0x01, 0x8c, 0x24, 0x5d, 0x7d, 0x85, 0xf4, 0x64, 0x32,
0x29, 0xe9, 0xea, 0x29, 0x6b, 0xc2, 0xef, 0x40, 0xa0, 0xf8, 0x52, 0xa9, 0x74, 0x3a, 0xc7, 0x4a,
0xf0, 0x94, 0x35, 0xf1, 0x5f, 0x7b, 0x30, 0x9c, 0x33, 0x71, 0xcd, 0xc4, 0x3b, 0xbd, 0xd0, 0xee,
0x5c, 0xd4, 0x7f, 0xcb, 0x5c, 0x34, 0xd8, 0x3d, 0x17, 0xf9, 0xdb, 0xb9, 0xe8, 0x01, 0xf8, 0x73,
0x91, 0x9c, 0x9f, 0xa9, 0x13, 0xf5, 0x89, 0x06, 0x58, 0x8d, 0xc7, 0x89, 0xe4, 0xd7, 0xcc, 0x0c,
0x4b, 0x06, 0xdd, 0x7b, 0xb8, 0xc7, 0x3b, 0x26, 0x94, 0xff, 0x75, 0x66, 0xb2, 0x2d, 0x0a, 0x4e,
0x8b, 0xc6, 0x30, 0xc5, 0xc1, 0x29, 0xa5, 0x92, 0x3e, 0x99, 0xbf, 0x7c, 0x61, 0xa7, 0x25, 0x57,
0x16, 0xff, 0xd1, 0x83, 0xe1, 0x33, 0xda, 0x14, 0xb5, 0xbc, 0x57, 0xed, 0x33, 0x98, 0x1c, 0x97,
0x65, 0xc6, 0x93, 0x4e, 0x87, 0x3b, 0x22, 0xb4, 0x78, 0xee, 0xe4, 0x51, 0xc7, 0xd0, 0x15, 0xe1,
0x83, 0x72, 0xaa, 0x86, 0x20, 0x3d, 0xd1, 0x38, 0x0f, 0x8a, 0x9e, 0x7d, 0x94, 0x12, 0x83, 0x7d,
0x5c, 0xcb, 0x62, 0x99, 0x15, 0x37, 0x2a, 0xaa, 0x63, 0xd2, 0xe2, 0xf8, 0x5f, 0x3d, 0x18, 0x7c,
0x53, 0x83, 0xcb, 0x14, 0x3c, 0x6e, 0x8a, 0xca, 0xe3, 0xed, 0x18, 0x33, 0x72, 0xc6, 0x98, 0x08,
0x46, 0x8d, 0xa0, 0xf9, 0x8a, 0x55, 0xd1, 0x58, 0xb1, 0x98, 0x85, 0x4a, 0xa3, 0xfa, 0x55, 0xcf,
0x2f, 0x01, 0xb1, 0xb0, 0xed, 0x3f, 0x70, 0xfa, 0xef, 0xe7, 0x66, 0xd4, 0x99, 0xdc, 0x1d, 0x0e,
0x76, 0x4d, 0x38, 0xff, 0xbf, 0x57, 0xfb, 0x3f, 0x1e, 0xf8, 0x6d, 0xf3, 0x9e, 0x76, 0x9b, 0xf7,
0x74, 0xdb, 0xbc, 0x67, 0x27, 0xb6, 0x79, 0xcf, 0x4e, 0x10, 0x93, 0x0b, 0xdb, 0xbc, 0xe4, 0x02,
0x93, 0xf5, 0x58, 0x14, 0x75, 0x79, 0xd2, 0xe8, 0xac, 0x06, 0xa4, 0xc5, 0x58, 0xf1, 0xbf, 0x59,
0x33, 0x61, 0x42, 0x1d, 0x10, 0x83, 0xb0, 0x3f, 0x9e, 0x29, 0x62, 0xd3, 0xc1, 0xd5, 0x20, 0xfc,
0x21, 0xf8, 0x04, 0x83, 0xa7, 0x22, 0xdc, 0xc9, 0x8b, 0x12, 0x13, 0xad, 0x45, 0xa7, 0xfa, 0x03,
0xc8, 0x34, 0x8a, 0xfd, 0x1c, 0xfa, 0x19, 0x0c, 0xe7, 0x6b, 0xbe, 0x94, 0x76, 0x60, 0xfc, 0x96,
0x43, 0x8c, 0x7c, 0xc3, 0x94, 0x8e, 0x18, 0x93, 0xf8, 0x15, 0x04, 0xad, 0x70, 0x7b, 0x1c, 0xcf,
0x3d, 0x4e, 0x08, 0x83, 0xab, 0x9c, 0x4b, 0x4b, 0x11, 0xb8, 0xc6, 0xcb, 0xbe, 0xaa, 0x69, 0x2e,
0xb9, 0x6c, 0x2c, 0x45, 0x58, 0x1c, 0x7f, 0x62, 0x8e, 0x8f, 0xee, 0xae, 0xca, 0x92, 0x09, 0x43,
0x37, 0x1a, 0xa8, 0x4d, 0x8a, 0x1b, 0xa6, 0x5f, 0x8a, 0x3e, 0xd1, 0x20, 0xfe, 0x2d, 0x04, 0xc7,
0x19, 0x13, 0x92, 0xd4, 0x19, 0xdb, 0xf5, 0x82, 0xab, 0x46, 0x35, 0x27, 0xc0, 0xf5, 0x96, 0x5a,
0xfa, 0x77, 0xa8, 0xe5, 0x29, 0x2d, 0xe9, 0xf9, 0x99, 0xaa, 0xf3, 0x3e, 0x31, 0x28, 0xfe, 0xb3,
0x07, 0x03, 0xe4, 0x30, 0xc7, 0xf5, 0xe0, 0x6d, 0xfc, 0x77, 0x21, 0x8a, 0x6b, 0x9e, 0x32, 0x61,
0x2f, 0x67, 0xb1, 0x0a, 0x7a, 0xb2, 0x66, 0xed, 0xa0, 0x60, 0x10, 0xd6, 0x1a, 0x7e, 0x2d, 0xd9,
0x5e, 0x72, 0x6a, 0x0d, 0xc5, 0x44, 0x2b, 0x71, 0x18, 0x9c, 0xd7, 0x25, 0x13, 0xc7, 0xe9, 0x86,
0xdb, 0x29, 0xca, 0x91, 0xc4, 0x5f, 0xe8, 0xef, 0xaf, 0x7b, 0x4c, 0xe8, 0xed, 0xfe, 0x56, 0xbb,
0x7b, 0xf2, 0xf8, 0x2f, 0x1e, 0x8c, 0x9e, 0x9b, 0xa9, 0xcd, 0xbd, 0x85, 0xf7, 0xc6, 0x5b, 0xf4,
0x3a, 0xb7, 0x38, 0x82, 0x07, 0xd6, 0xa6, 0xb3, 0xbf, 0x8e, 0xc2, 0x4e, 0x9d, 0x89, 0xe8, 0xa0,
0x4d, 0xd6, 0xbb, 0x7c, 0x7e, 0x5d, 0x76, 0x6d, 0x76, 0x25, 0xfc, 0x5e, 0x56, 0x66, 0x30, 0xb1,
0x9f, 0x9d, 0x45, 0x66, 0x1f, 0x26, 0x57, 0x14, 0x1f, 0xc1, 0xf0, 0xb4, 0xc8, 0x97, 0x7c, 0x15,
0x1e, 0xc2, 0xe0, 0xb8, 0x96, 0x6b, 0xe5, 0x71, 0x72, 0xf4, 0xc0, 0x69, 0xfc, 0x5a, 0xae, 0xb5,
0x0d, 0x51, 0x16, 0xf1, 0x67, 0x00, 0x5b, 0x19, 0xbe, 0x2e, 0xdb, 0x6c, 0xbc, 0x60, 0x37, 0x58,
0x32, 0x95, 0x19, 0xda, 0x77, 0x68, 0xe2, 0xcf, 0x21, 0x38, 0xa9, 0x79, 0x96, 0x9e, 0xe7, 0xcb,
0x02, 0xa9, 0xe3, 0x35, 0x13, 0xd5, 0x36, 0x5f, 0x16, 0x62, 0xb8, 0x91, 0x45, 0xda, 0x1e, 0x32,
0x68, 0x31, 0x54, 0x7f, 0x6a, 0x7c, 0xf2, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd3, 0xfb, 0xbd,
0x7b, 0xe6, 0x10, 0x00, 0x00,
// 1691 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xdd, 0x8f, 0xe3, 0x48,
0x11, 0x97, 0x13, 0x3b, 0x89, 0x2b, 0x99, 0xb9, 0x91, 0x59, 0xdd, 0x99, 0x03, 0xa1, 0x60, 0xf1,
0x31, 0x7c, 0xdc, 0x72, 0x9a, 0x13, 0x12, 0x3a, 0xdd, 0x9d, 0x34, 0x1f, 0xb7, 0xcb, 0xec, 0xe7,
0x6c, 0x67, 0x76, 0x78, 0x42, 0xa7, 0x4e, 0xdc, 0x49, 0x5a, 0xe7, 0xd8, 0xa6, 0xdd, 0x9e, 0x19,
0xf3, 0xcc, 0xdf, 0x81, 0x84, 0x04, 0xef, 0x08, 0xf1, 0x88, 0xc4, 0x3b, 0x7f, 0x00, 0xff, 0x0a,
0xaf, 0xa8, 0xfa, 0xc3, 0x69, 0xcf, 0x64, 0x57, 0x8b, 0x84, 0xee, 0xad, 0x7f, 0x55, 0x95, 0xea,
0xea, 0xea, 0xaa, 0x5f, 0x97, 0x03, 0xfb, 0x3c, 0x97, 0x4c, 0xe4, 0x34, 0x7b, 0x58, 0x8a, 0x42,
0x16, 0xd1, 0xc8, 0xe2, 0xe4, 0x0f, 0x7d, 0x18, 0xcc, 0x8a, 0x5a, 0x2c, 0x58, 0xb4, 0x0f, 0xbd,
0xf3, 0xb3, 0xd8, 0x9b, 0x7a, 0x87, 0x7d, 0xd2, 0x3b, 0x3f, 0x8b, 0x22, 0xf0, 0x5f, 0xd0, 0x0d,
0x8b, 0x7b, 0x53, 0xef, 0x30, 0x24, 0x6a, 0x8d, 0xb2, 0xcb, 0xa6, 0x64, 0x71, 0x5f, 0xcb, 0x70,
0x1d, 0x7d, 0x08, 0xa3, 0xd7, 0x15, 0x7a, 0xdb, 0xb0, 0xd8, 0x57, 0xf2, 0x16, 0xa3, 0xee, 0x82,
0x56, 0xd5, 0x4d, 0x21, 0xd2, 0x38, 0xd0, 0x3a, 0x8b, 0xa3, 0x03, 0xe8, 0xbf, 0x26, 0xcf, 0xe2,
0x81, 0x12, 0xe3, 0x32, 0x8a, 0x61, 0x78, 0xc6, 0x96, 0xb4, 0xce, 0x64, 0x3c, 0x9c, 0x7a, 0x87,
0x23, 0x62, 0x21, 0xfa, 0xb9, 0x64, 0x19, 0x5b, 0x09, 0xba, 0x8c, 0x47, 0xda, 0x8f, 0xc5, 0xd1,
0x43, 0x88, 0xce, 0xf3, 0x8a, 0x2d, 0x6a, 0xc1, 0x66, 0x5f, 0xf3, 0xf2, 0x8a, 0x09, 0xbe, 0x6c,
0xe2, 0x50, 0x39, 0xd8, 0xa1, 0xc1, 0x5d, 0x9e, 0x33, 0x49, 0x71, 0x6f, 0x50, 0xae, 0x2c, 0x8c,
0x12, 0x98, 0xcc, 0xd6, 0x54, 0xb0, 0x74, 0xc6, 0x16, 0x82, 0xc9, 0x78, 0xac, 0xd4, 0x1d, 0x19,
0xda, 0xbc, 0x14, 0x2b, 0x9a, 0xf3, 0xdf, 0x53, 0xc9, 0x8b, 0x3c, 0x9e, 0x68, 0x1b, 0x57, 0x86,
0x59, 0x22, 0x45, 0xc6, 0xe2, 0x3d, 0x9d, 0x25, 0x5c, 0x47, 0xdf, 0x85, 0xd0, 0x1c, 0x86, 0x5c,
0xc4, 0xfb, 0x4a, 0xb1, 0x15, 0x24, 0x7f, 0xf7, 0x20, 0x3c, 0xa3, 0xd5, 0x7a, 0x5e, 0x50, 0x91,
0xbe, 0xd3, 0x4d, 0x7c, 0x04, 0xc1, 0x82, 0x65, 0x59, 0x15, 0xf7, 0xa7, 0xfd, 0xc3, 0xf1, 0xd1,
0x07, 0x0f, 0xdb, 0x2b, 0x6e, 0xfd, 0x9c, 0xb2, 0x2c, 0x23, 0xda, 0x2a, 0xfa, 0x18, 0x42, 0xc9,
0x36, 0x65, 0x46, 0x25, 0xab, 0x62, 0x5f, 0xfd, 0x24, 0xda, 0xfe, 0xe4, 0xd2, 0xa8, 0xc8, 0xd6,
0xe8, 0xde, 0x41, 0x83, 0xfb, 0x07, 0x4d, 0xfe, 0xed, 0xc3, 0x5e, 0x67, 0xbb, 0x68, 0x02, 0xde,
0xad, 0x8a, 0x3c, 0x20, 0xde, 0x2d, 0xa2, 0x46, 0x45, 0x1d, 0x10, 0xaf, 0x41, 0x74, 0xa3, 0x2a,
0x27, 0x20, 0xde, 0x0d, 0xa2, 0xb5, 0xaa, 0x97, 0x80, 0x78, 0xeb, 0xe8, 0x27, 0x30, 0xfc, 0x5d,
0xcd, 0x04, 0x67, 0x55, 0x1c, 0xa8, 0xe8, 0xde, 0xdb, 0x46, 0xf7, 0xaa, 0x66, 0xa2, 0x21, 0x56,
0x8f, 0xd9, 0x50, 0xb5, 0xa6, 0x0b, 0x47, 0xad, 0x51, 0x26, 0xb1, 0x2e, 0x87, 0x5a, 0x86, 0x6b,
0x93, 0x45, 0x5d, 0x2d, 0x98, 0xc5, 0x5f, 0x82, 0x4f, 0x6f, 0x59, 0x15, 0x87, 0xca, 0xff, 0xf7,
0xdf, 0x90, 0xb0, 0x87, 0xc7, 0xb7, 0xac, 0xfa, 0x32, 0x97, 0xa2, 0x21, 0xca, 0x3c, 0xfa, 0x31,
0x0c, 0x16, 0x45, 0x56, 0x88, 0x2a, 0x86, 0xbb, 0x81, 0x9d, 0xa2, 0x9c, 0x18, 0x75, 0x74, 0x08,
0x83, 0x8c, 0xad, 0x58, 0x9e, 0xaa, 0xba, 0x19, 0x1f, 0x1d, 0x6c, 0x0d, 0x9f, 0x29, 0x39, 0x31,
0xfa, 0xe8, 0x53, 0x98, 0x48, 0x3a, 0xcf, 0xd8, 0xcb, 0x12, 0xb3, 0x58, 0xa9, 0x1a, 0x1a, 0x1f,
0xbd, 0xef, 0xdc, 0x87, 0xa3, 0x25, 0x1d, 0xdb, 0xe8, 0x33, 0x98, 0x2c, 0x39, 0xcb, 0x52, 0xfb,
0xdb, 0x3d, 0x15, 0x54, 0xbc, 0xfd, 0x2d, 0x61, 0x39, 0xdd, 0xe0, 0x2f, 0x1e, 0xa1, 0x19, 0xe9,
0x58, 0x47, 0xdf, 0x03, 0x90, 0x7c, 0xc3, 0x1e, 0x15, 0x62, 0x43, 0xa5, 0x29, 0x43, 0x47, 0x12,
0x7d, 0x0e, 0x7b, 0x29, 0x5b, 0xf0, 0x0d, 0xcd, 0x2e, 0x32, 0xba, 0x60, 0x55, 0xfc, 0x9e, 0x0a,
0xcd, 0xad, 0x2e, 0x57, 0x4d, 0xba, 0xd6, 0x1f, 0x3e, 0x86, 0xb0, 0x4d, 0x1f, 0xf6, 0xf7, 0xd7,
0xac, 0x51, 0xc5, 0x10, 0x12, 0x5c, 0x46, 0x3f, 0x80, 0xe0, 0x9a, 0x66, 0xb5, 0x2e, 0xe4, 0xf1,
0xd1, 0xfe, 0xd6, 0xeb, 0xf1, 0x2d, 0xaf, 0x88, 0x56, 0x7e, 0xda, 0xfb, 0x95, 0x97, 0x3c, 0x86,
0xbd, 0xce, 0x46, 0x18, 0x38, 0xaf, 0xbe, 0xcc, 0x97, 0x85, 0x58, 0xb0, 0x54, 0xf9, 0x1c, 0x11,
0x47, 0x12, 0xbd, 0x0f, 0x83, 0x94, 0xaf, 0xb8, 0xac, 0x4c, 0xb9, 0x19, 0x94, 0xfc, 0xc3, 0x83,
0x89, 0x9b, 0xcd, 0xe8, 0xa7, 0x70, 0x70, 0xcd, 0x84, 0xe4, 0x0b, 0x9a, 0x5d, 0xf2, 0x0d, 0xc3,
0x8d, 0xd5, 0x4f, 0x46, 0xe4, 0x9e, 0x3c, 0xfa, 0x18, 0x06, 0x55, 0x21, 0xe4, 0x49, 0xa3, 0xaa,
0xf6, 0x6d, 0x59, 0x36, 0x76, 0xc8, 0x53, 0x37, 0x82, 0x96, 0x25, 0xcf, 0x57, 0x96, 0x0b, 0x2d,
0x8e, 0x7e, 0x04, 0xfb, 0x4b, 0x7e, 0xfb, 0x88, 0x8b, 0x4a, 0x9e, 0x16, 0x59, 0xbd, 0xc9, 0x55,
0x05, 0x8f, 0xc8, 0x1d, 0xe9, 0x13, 0x7f, 0xe4, 0x1d, 0xf4, 0x9e, 0xf8, 0xa3, 0xe0, 0x60, 0x90,
0x94, 0xb0, 0xdf, 0xdd, 0x09, 0xdb, 0xd2, 0x06, 0xa1, 0x38, 0x41, 0xa7, 0xb7, 0x23, 0x8b, 0xa6,
0x30, 0x4e, 0x79, 0x55, 0x66, 0xb4, 0x71, 0x68, 0xc3, 0x15, 0x21, 0x07, 0x5e, 0xf3, 0x8a, 0xcf,
0x33, 0x4d, 0xe5, 0x23, 0x62, 0x61, 0xb2, 0x82, 0x40, 0x95, 0xb5, 0x43, 0x42, 0xa1, 0x25, 0x21,
0x45, 0xfd, 0x3d, 0x87, 0xfa, 0x0f, 0xa0, 0xff, 0x6b, 0x76, 0x6b, 0x5e, 0x03, 0x5c, 0xb6, 0x54,
0xe5, 0x3b, 0x54, 0xf5, 0x00, 0x82, 0x2b, 0x75, 0xed, 0x9a, 0x42, 0x34, 0x48, 0xbe, 0x80, 0x81,
0x6e, 0x8b, 0xd6, 0xb3, 0xe7, 0x78, 0x9e, 0xc2, 0xf8, 0xa5, 0xe0, 0x2c, 0x97, 0x9a, 0x7c, 0xcc,
0x11, 0x1c, 0x51, 0xf2, 0x37, 0x0f, 0x7c, 0x75, 0x4b, 0x09, 0x4c, 0x32, 0xb6, 0xa2, 0x8b, 0xe6,
0xa4, 0xa8, 0xf3, 0xb4, 0x8a, 0xbd, 0x69, 0xff, 0xb0, 0x4f, 0x3a, 0x32, 0x2c, 0x8f, 0xb9, 0xd6,
0xf6, 0xa6, 0xfd, 0xc3, 0x90, 0x18, 0x84, 0xa1, 0x65, 0x74, 0xce, 0x32, 0x73, 0x04, 0x0d, 0xd0,
0xba, 0x14, 0x6c, 0xc9, 0x6f, 0xcd, 0x31, 0x0c, 0x42, 0x79, 0x55, 0x2f, 0x51, 0xae, 0x4f, 0x62,
0x10, 0x1e, 0x60, 0x4e, 0xab, 0x96, 0x91, 0x70, 0x8d, 0x9e, 0xab, 0x05, 0xcd, 0x2c, 0x25, 0x69,
0x90, 0xfc, 0xd3, 0xc3, 0x87, 0x4c, 0x53, 0xec, 0xbd, 0x0c, 0x7f, 0x1b, 0x46, 0x48, 0xbf, 0x5f,
0x5d, 0x53, 0x61, 0x0e, 0x3c, 0x44, 0x7c, 0x45, 0x45, 0xf4, 0x0b, 0x18, 0xa8, 0xe6, 0xd8, 0x41,
0xf7, 0xd6, 0x9d, 0xca, 0x2a, 0x31, 0x66, 0x2d, 0x21, 0xfa, 0x0e, 0x21, 0xb6, 0x87, 0x0d, 0xdc,
0xc3, 0x7e, 0x04, 0x01, 0x32, 0x6b, 0xa3, 0xa2, 0xdf, 0xe9, 0x59, 0xf3, 0xaf, 0xb6, 0x4a, 0x56,
0xb0, 0xd7, 0xd9, 0xb1, 0xdd, 0xc9, 0xeb, 0xee, 0xb4, 0x6d, 0xf4, 0xd0, 0x34, 0x36, 0x36, 0x47,
0xc5, 0x32, 0xb6, 0x90, 0x2c, 0x35, 0x55, 0xd7, 0x62, 0x4b, 0x16, 0x7e, 0x4b, 0x16, 0xc9, 0x9f,
0xbc, 0xed, 0x4e, 0x2a, 0x02, 0x2c, 0xda, 0x45, 0xb1, 0xd9, 0xd0, 0x3c, 0x35, 0x9b, 0x59, 0x88,
0x99, 0x4c, 0xe7, 0x66, 0xb3, 0x5e, 0x3a, 0x47, 0x2c, 0x4a, 0x73, 0xa7, 0x3d, 0x51, 0x62, 0x35,
0x6d, 0x18, 0xad, 0x6a, 0xc1, 0x36, 0x2c, 0x97, 0x66, 0x17, 0x57, 0x14, 0x7d, 0x00, 0x43, 0x49,
0x57, 0x5f, 0x61, 0x0c, 0xe6, 0x6e, 0x25, 0x5d, 0x3d, 0x65, 0x4d, 0xf4, 0x1d, 0x08, 0x15, 0x83,
0x2a, 0x95, 0xbe, 0xe0, 0x91, 0x12, 0x3c, 0x65, 0x4d, 0xf2, 0xd7, 0x1e, 0x0c, 0x66, 0x4c, 0x5c,
0x33, 0xf1, 0x4e, 0x6f, 0xb6, 0x3b, 0x29, 0xf5, 0xdf, 0x32, 0x29, 0xf9, 0xbb, 0x27, 0xa5, 0x60,
0x3b, 0x29, 0x3d, 0x80, 0x60, 0x26, 0x16, 0xe7, 0x67, 0x2a, 0xa2, 0x3e, 0xd1, 0x00, 0xeb, 0xf3,
0x78, 0x21, 0xf9, 0x35, 0x33, 0xe3, 0x93, 0x41, 0xf7, 0x9e, 0xf2, 0xd1, 0x8e, 0x99, 0xe5, 0x7f,
0x9d, 0xa2, 0x6c, 0xd3, 0x82, 0xd3, 0xb4, 0x09, 0x4c, 0x70, 0x94, 0x4a, 0xa9, 0xa4, 0x4f, 0x66,
0x2f, 0x5f, 0xd8, 0xf9, 0xc9, 0x95, 0x25, 0x7f, 0xf4, 0x60, 0xf0, 0x8c, 0x36, 0x45, 0x2d, 0xef,
0xd5, 0xff, 0x14, 0xc6, 0xc7, 0x65, 0x99, 0xf1, 0x45, 0xa7, 0xe7, 0x1d, 0x11, 0x5a, 0x3c, 0x77,
0xee, 0x51, 0xe7, 0xd0, 0x15, 0xe1, 0x13, 0x73, 0xaa, 0xc6, 0x22, 0x3d, 0xe3, 0x38, 0x4f, 0x8c,
0x9e, 0x86, 0x94, 0x12, 0x93, 0x7d, 0x5c, 0xcb, 0x62, 0x99, 0x15, 0x37, 0x2a, 0xab, 0x23, 0xd2,
0xe2, 0xe4, 0x5f, 0x3d, 0xf0, 0xbf, 0xa9, 0x51, 0x66, 0x02, 0x1e, 0x37, 0x45, 0xe5, 0xf1, 0x76,
0xb0, 0x19, 0x3a, 0x83, 0x4d, 0x0c, 0xc3, 0x46, 0xd0, 0x7c, 0xc5, 0xaa, 0x78, 0xa4, 0x78, 0xcd,
0x42, 0xa5, 0x51, 0x1d, 0xac, 0x27, 0x9a, 0x90, 0x58, 0xd8, 0x76, 0x24, 0x38, 0x1d, 0xf9, 0x73,
0x33, 0xfc, 0x8c, 0xef, 0x8e, 0x0b, 0xbb, 0x66, 0x9e, 0xff, 0xdf, 0x3b, 0xfe, 0x1f, 0x0f, 0x82,
0xb6, 0x79, 0x4f, 0xbb, 0xcd, 0x7b, 0xba, 0x6d, 0xde, 0xb3, 0x13, 0xdb, 0xbc, 0x67, 0x27, 0x88,
0xc9, 0x85, 0x6d, 0x5e, 0x72, 0x81, 0x97, 0xf5, 0x58, 0x14, 0x75, 0x79, 0xd2, 0xe8, 0x5b, 0x0d,
0x49, 0x8b, 0xb1, 0xe2, 0x7f, 0xb3, 0x66, 0xc2, 0xa4, 0x3a, 0x24, 0x06, 0x61, 0x7f, 0x3c, 0x53,
0x54, 0xa7, 0x93, 0xab, 0x41, 0xf4, 0x43, 0x08, 0x08, 0x26, 0x4f, 0x65, 0xb8, 0x73, 0x2f, 0x4a,
0x4c, 0xb4, 0x16, 0x9d, 0xea, 0x4f, 0x22, 0xd3, 0x28, 0xf6, 0x03, 0xe9, 0x67, 0x30, 0x98, 0xad,
0xf9, 0x52, 0xda, 0x11, 0xf2, 0x5b, 0x0e, 0x55, 0xf2, 0x0d, 0x53, 0x3a, 0x62, 0x4c, 0x92, 0x57,
0x10, 0xb6, 0xc2, 0x6d, 0x38, 0x9e, 0x1b, 0x4e, 0x04, 0xfe, 0xeb, 0x9c, 0x4b, 0x4b, 0x11, 0xb8,
0xc6, 0xc3, 0xbe, 0xaa, 0x69, 0x2e, 0xb9, 0x6c, 0x2c, 0x45, 0x58, 0x9c, 0x7c, 0x62, 0xc2, 0x47,
0x77, 0xaf, 0xcb, 0x92, 0x09, 0x43, 0x37, 0x1a, 0xa8, 0x4d, 0x8a, 0x1b, 0xa6, 0xdf, 0x8e, 0x3e,
0xd1, 0x20, 0xf9, 0x2d, 0x84, 0xc7, 0x19, 0x13, 0x92, 0xd4, 0x19, 0xdb, 0xf5, 0xa6, 0xab, 0x46,
0x35, 0x11, 0xe0, 0x7a, 0x4b, 0x2d, 0xfd, 0x3b, 0xd4, 0xf2, 0x94, 0x96, 0xf4, 0xfc, 0x4c, 0xd5,
0x79, 0x9f, 0x18, 0x94, 0xfc, 0xd9, 0x03, 0x1f, 0x39, 0xcc, 0x71, 0xed, 0xbf, 0x8d, 0xff, 0x2e,
0x44, 0x71, 0xcd, 0x53, 0x26, 0xec, 0xe1, 0x2c, 0x56, 0x49, 0x5f, 0xac, 0x59, 0x3b, 0x3a, 0x18,
0x84, 0xb5, 0x86, 0xdf, 0x4f, 0xb6, 0x97, 0x9c, 0x5a, 0x43, 0x31, 0xd1, 0x4a, 0x1c, 0x0f, 0x67,
0x75, 0xc9, 0xc4, 0x71, 0xba, 0xe1, 0x76, 0xae, 0x72, 0x24, 0xc9, 0x17, 0xfa, 0x8b, 0xec, 0x1e,
0x13, 0x7a, 0xbb, 0xbf, 0xde, 0xee, 0x46, 0x9e, 0xfc, 0xc5, 0x83, 0xe1, 0x73, 0x33, 0xc7, 0xb9,
0xa7, 0xf0, 0xde, 0x78, 0x8a, 0x5e, 0xe7, 0x14, 0x47, 0xf0, 0xc0, 0xda, 0x74, 0xf6, 0xd7, 0x59,
0xd8, 0xa9, 0x33, 0x19, 0xf5, 0xdb, 0xcb, 0x7a, 0x97, 0x0f, 0xb2, 0xcb, 0xae, 0xcd, 0xae, 0x0b,
0xbf, 0x77, 0x2b, 0x53, 0x18, 0xdb, 0x0f, 0xd1, 0x22, 0xb3, 0x0f, 0x93, 0x2b, 0x4a, 0x8e, 0x60,
0x70, 0x5a, 0xe4, 0x4b, 0xbe, 0x8a, 0x0e, 0xc1, 0x3f, 0xae, 0xe5, 0x5a, 0x79, 0x1c, 0x1f, 0x3d,
0x70, 0x1a, 0xbf, 0x96, 0x6b, 0x6d, 0x43, 0x94, 0x45, 0xf2, 0x19, 0xc0, 0x56, 0x86, 0xaf, 0xcb,
0xf6, 0x36, 0x5e, 0xb0, 0x1b, 0x2c, 0x99, 0xca, 0x8c, 0xf1, 0x3b, 0x34, 0xc9, 0xe7, 0x10, 0x9e,
0xd4, 0x3c, 0x4b, 0xcf, 0xf3, 0x65, 0x81, 0xd4, 0x71, 0xc5, 0x44, 0xb5, 0xbd, 0x2f, 0x0b, 0x31,
0xdd, 0xc8, 0x22, 0x6d, 0x0f, 0x19, 0x34, 0x1f, 0xa8, 0xbf, 0x39, 0x3e, 0xf9, 0x6f, 0x00, 0x00,
0x00, 0xff, 0xff, 0x5f, 0x2f, 0x55, 0x31, 0xf8, 0x10, 0x00, 0x00,
}

View File

@ -100,6 +100,7 @@ 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 key = 4; // Key is the key for a specific Value if the Template Type is map (optional)
}
message TemplateQuery {

View File

@ -159,8 +159,9 @@ type Range struct {
// TemplateValue is a value use to replace a template in an InfluxQL query
type TemplateValue struct {
Value string `json:"value"` // Value is the specific value used to replace a template in an InfluxQL query
Type string `json:"type"` // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant
Type string `json:"type"` // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant, influxql
Selected bool `json:"selected"` // Selected states that this variable has been picked to use for replacement
Key string `json:"key,omitempty"` // Key is the key for the Value if the Template Type is 'map'
}
// TemplateVar is a named variable within an InfluxQL query to be replaced with Values
@ -176,7 +177,7 @@ type TemplateID string
type Template struct {
TemplateVar
ID TemplateID `json:"id"` // ID is the unique ID associated with this template
Type string `json:"type"` // Type can be fieldKeys, tagKeys, tagValues, CSV, constant, query, measurements, databases
Type string `json:"type"` // Type can be fieldKeys, tagKeys, tagValues, CSV, constant, measurements, databases, map, influxql
Label string `json:"label"` // Label is a user-facing description of the Template
Query *TemplateQuery `json:"query,omitempty"` // Query is used to generate the choices for a template
}

View File

@ -72,7 +72,7 @@ func RenderTemplate(query string, t chronograf.TemplateVar, now time.Time) (stri
return strings.Replace(q, t.Var, `"`+t.Values[0].Value+`"`, -1), nil
case "tagValue", "timeStamp":
return strings.Replace(q, t.Var, `'`+t.Values[0].Value+`'`, -1), nil
case "csv", "constant":
case "csv", "constant", "influxql":
return strings.Replace(q, t.Var, t.Values[0].Value, -1), nil
}

View File

@ -4090,6 +4090,10 @@
"enum": ["csv", "tagKey", "tagValue", "fieldKey", "timeStamp"],
"description":
"The type will change the format of the output value. tagKey/fieldKey are double quoted; tagValue are single quoted; csv and timeStamp are not quoted."
},
"key": {
"type": "string",
"description":"This will be the key for a specific value of a template variable. Used if the templateVar type is 'map'"
}
}
},

View File

@ -15,19 +15,23 @@ func ValidTemplateRequest(template *chronograf.Template) error {
switch template.Type {
default:
return fmt.Errorf("Unknown template type %s", template.Type)
case "query", "constant", "csv", "fieldKeys", "tagKeys", "tagValues", "measurements", "databases":
case "constant", "csv", "fieldKeys", "tagKeys", "tagValues", "measurements", "databases", "map", "influxql":
}
for _, v := range template.Values {
switch v.Type {
default:
return fmt.Errorf("Unknown template variable type %s", v.Type)
case "csv", "fieldKey", "tagKey", "tagValue", "measurement", "database", "constant":
case "csv", "fieldKey", "tagKey", "tagValue", "measurement", "database", "constant", "influxql":
}
if template.Type == "map" && v.Key == "" {
return fmt.Errorf("Templates of type 'map' require a 'key'")
}
}
if template.Type == "query" && template.Query == nil {
return fmt.Errorf("No query set for template of type 'query'")
if template.Type == "influxql" && template.Query == nil {
return fmt.Errorf("No query set for template of type 'influxql'")
}
return nil

View File

@ -57,7 +57,37 @@ func TestValidTemplateRequest(t *testing.T) {
name: "No query set",
wantErr: true,
template: &chronograf.Template{
Type: "query",
Type: "influxql",
},
},
{
name: "Valid Map type",
template: &chronograf.Template{
Type: "map",
TemplateVar: chronograf.TemplateVar{
Values: []chronograf.TemplateValue{
{
Key: "key",
Value: "value",
Type: "constant",
},
},
},
},
},
{
name: "Map without Key",
wantErr: true,
template: &chronograf.Template{
Type: "map",
TemplateVar: chronograf.TemplateVar{
Values: []chronograf.TemplateValue{
{
Value: "value",
Type: "constant",
},
},
},
},
},
}

View File

@ -48,7 +48,7 @@ interface State {
@ErrorHandling
export class AllUsersPage extends PureComponent<Props, State> {
constructor(props) {
constructor(props: Props) {
super(props)
this.state = {

View File

@ -1,5 +1,5 @@
import {proxy} from 'src/utils/queryUrlGenerator'
import {TimeRange} from '../../types'
import {TimeRange} from 'src/types'
export const getAlerts = (
source: string,

View File

@ -5,7 +5,9 @@ import {replace} from 'react-router-redux'
import _ from 'lodash'
import queryString from 'query-string'
import {proxy} from 'src/utils/queryUrlGenerator'
import {isUserAuthorized, EDITOR_ROLE} from 'src/auth/Authorized'
import {parseMetaQuery} from 'src/tempVars/utils/parsing'
import {
getDashboards as getDashboardsAJAX,
@ -15,7 +17,6 @@ import {
updateDashboardCell as updateDashboardCellAJAX,
addDashboardCell as addDashboardCellAJAX,
deleteDashboardCell as deleteDashboardCellAJAX,
getTempVarValuesBySourceQuery,
createDashboard as createDashboardAJAX,
} from 'src/dashboards/apis'
import {getMe} from 'src/shared/apis/auth'
@ -48,7 +49,6 @@ import {
} from 'src/shared/copy/notifications'
import {makeQueryForTemplate} from 'src/dashboards/utils/tempVars'
import parsers from 'src/shared/parsing'
import {getDeep} from 'src/utils/wrappers'
import idNormalizer, {TYPE_ID} from 'src/normalizers/id'
@ -418,7 +418,7 @@ export const getDashboardsNamesAsync = (sourceID: string) => async (
}
}
export const getDashboardAsync = (dashboardID: string) => async (
export const getDashboardAsync = (dashboardID: number) => async (
dispatch
): Promise<Dashboard | null> => {
try {
@ -628,25 +628,23 @@ export const hydrateTempVarValuesAsync = (
const dashboard = getState().dashboardUI.dashboards.find(
d => d.id === dashboardID
)
const tempsWithQueries = dashboard.templates.filter(
({query}) => !!query.influxql
const templates: Template[] = dashboard.templates
const queries = templates
.filter(
template => getDeep<string>(template, 'query.influxql', '') !== ''
)
.map(async template => {
const query = makeQueryForTemplate(template.query)
const response = await proxy({source: source.links.proxy, query})
const values = parseMetaQuery(query, response.data)
const asyncQueries = tempsWithQueries.map(({query}) =>
getTempVarValuesBySourceQuery(source, {
query: makeQueryForTemplate(query),
return {template, values}
})
)
const results = await Promise.all(queries)
const results = await Promise.all(asyncQueries)
results.forEach(({data}, i) => {
const {type, query, id} = tempsWithQueries[i]
const parsed = parsers[type](data, query.tagKey || query.measurement)
const vals = parsed[type]
dispatch(editTemplateVariableValues(+dashboard.id, id, vals))
})
for (const {template, values} of results) {
dispatch(editTemplateVariableValues(+dashboard.id, template.id, values))
}
} catch (error) {
console.error(error)
dispatch(errorThrown(error))
@ -814,7 +812,7 @@ const syncDashboardFromURLQueryParams = (
}
export const getDashboardWithHydratedAndSyncedTempVarsAsync = (
dashboardID: string,
dashboardID: number,
source: Source,
router: InjectedRouter,
location: Location

View File

@ -1,5 +1,4 @@
import AJAX from 'utils/ajax'
import {proxy} from 'utils/queryUrlGenerator'
export function getDashboards() {
return AJAX({
@ -98,19 +97,3 @@ export const editTemplateVariables = async templateVariable => {
throw error
}
}
export const getTempVarValuesBySourceQuery = async (source, templateQuery) => {
const {
query,
db,
// rp, TODO
tempVars,
} = templateQuery
try {
// TODO: add rp as argument to proxy
return await proxy({source: source.links.proxy, query, db, tempVars})
} catch (error) {
console.error(error)
throw error
}
}

View File

@ -495,7 +495,7 @@ DashboardPage.propTypes = {
sources: arrayOf(shape({})).isRequired,
params: shape({
sourceID: string.isRequired,
dashboardID: string.isRequired,
dashboardID: number.isRequired,
}).isRequired,
location: shape({
pathname: string.isRequired,

View File

@ -134,7 +134,7 @@ class AlertTabs extends PureComponent<Props, State> {
this.setState({services})
} catch (error) {
this.setState({services: null})
this.props.notify(notifyCouldNotRetrieveKapacitorServices(kapacitor))
this.props.notify(notifyCouldNotRetrieveKapacitorServices(kapacitor.name))
}
}

View File

@ -1,24 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
const CodeData = ({onClickTemplate, template}) => (
<code
className="rule-builder--message-template"
data-tip={template.text}
onClick={onClickTemplate}
>
{template.label}
</code>
)
const {func, shape, string} = PropTypes
CodeData.propTypes = {
onClickTemplate: func,
template: shape({
label: string,
text: string,
}),
}
export default CodeData

View File

@ -0,0 +1,20 @@
import React, {SFC} from 'react'
import {RuleMessage} from 'src/types/kapacitor'
interface Props {
onClickTemplate: () => void
template: RuleMessage
}
const CodeData: SFC<Props> = ({onClickTemplate, template}) => (
<code
className="rule-builder--message-template"
data-tip={template.text}
onClick={onClickTemplate}
>
{template.label}
</code>
)
export default CodeData

View File

@ -24,7 +24,7 @@ interface Props {
query: QueryConfig
isDeadman: boolean
isKapacitorRule: boolean
onAddEvery: () => void
onAddEvery: (every?: string) => void
timeRange: TimeRange
}

View File

@ -1,13 +1,24 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
import {PERIODS} from 'src/kapacitor/constants'
import Dropdown from 'shared/components/Dropdown'
import Dropdown from 'src/shared/components/Dropdown'
import {AlertRule} from 'src/types'
const periods = PERIODS.map(text => {
return {text}
})
const Deadman = ({rule, onChange}) => (
interface Item {
text: string
}
interface Props {
rule: AlertRule
onChange: (item: Item) => void
}
const Deadman: SFC<Props> = ({rule, onChange}) => (
<div className="rule-section--row rule-section--row-first rule-section--row-last">
<p>Send Alert if Data is missing for</p>
<Dropdown
@ -20,15 +31,4 @@ const Deadman = ({rule, onChange}) => (
</div>
)
const {shape, string, func} = PropTypes
Deadman.propTypes = {
rule: shape({
values: shape({
period: string,
}),
}),
onChange: func.isRequired,
}
export default Deadman

View File

@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import React, {Component, ChangeEvent} from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import {InjectedRouter} from 'react-router'
import PageHeader from 'src/shared/components/PageHeader'
import NameSection from 'src/kapacitor/components/NameSection'
@ -9,13 +9,13 @@ import ValuesSection from 'src/kapacitor/components/ValuesSection'
import RuleHeaderSave from 'src/kapacitor/components/RuleHeaderSave'
import RuleHandlers from 'src/kapacitor/components/RuleHandlers'
import RuleMessage from 'src/kapacitor/components/RuleMessage'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import {createRule, editRule} from 'src/kapacitor/apis'
import buildInfluxQLQuery from 'utils/influxql'
import {timeRanges} from 'shared/data/timeRanges'
import buildInfluxQLQuery from 'src/utils/influxql'
import {timeRanges} from 'src/shared/data/timeRanges'
import {DEFAULT_RULE_ID} from 'src/kapacitor/constants'
import {notify as notifyAction} from 'shared/actions/notifications'
import {notify as notifyAction} from 'src/shared/actions/notifications'
import {
notifyAlertRuleCreated,
@ -25,11 +25,52 @@ import {
notifyAlertRuleRequiresQuery,
notifyAlertRuleRequiresConditionValue,
notifyAlertRuleDeadmanInvalid,
} from 'shared/copy/notifications'
} from 'src/shared/copy/notifications'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {
Source,
AlertRule,
Notification,
Kapacitor,
QueryConfig,
TimeRange,
} from 'src/types'
import {Handler} from 'src/types/kapacitor'
import {
KapacitorQueryConfigActions,
KapacitorRuleActions,
} from 'src/types/actions'
interface Props {
source: Source
rule: AlertRule
query: QueryConfig
queryConfigs: QueryConfig[]
queryConfigActions: KapacitorQueryConfigActions
ruleActions: KapacitorRuleActions
notify: (message: Notification) => void
ruleID: string
handlersFromConfig: Handler[]
router: InjectedRouter
kapacitor: Kapacitor
configLink: string
}
interface Item {
text: string
}
interface TypeItem extends Item {
type: string
}
interface State {
timeRange: TimeRange
}
@ErrorHandling
class KapacitorRule extends Component {
class KapacitorRule extends Component<Props, State> {
constructor(props) {
super(props)
this.state = {
@ -37,146 +78,7 @@ class KapacitorRule extends Component {
}
}
handleChooseTimeRange = ({lower}) => {
const timeRange = timeRanges.find(range => range.lower === lower)
this.setState({timeRange})
}
handleCreate = pathname => {
const {notify, queryConfigs, rule, source, router, kapacitor} = this.props
const newRule = Object.assign({}, rule, {
query: queryConfigs[rule.queryID],
})
delete newRule.queryID
createRule(kapacitor, newRule)
.then(() => {
router.push(pathname || `/sources/${source.id}/alert-rules`)
notify(notifyAlertRuleCreated(newRule.name))
})
.catch(e => {
notify(notifyAlertRuleCreateFailed(newRule.name, e.data.message))
})
}
handleEdit = pathname => {
const {notify, queryConfigs, rule, router, source} = this.props
const updatedRule = Object.assign({}, rule, {
query: queryConfigs[rule.queryID],
})
editRule(updatedRule)
.then(() => {
router.push(pathname || `/sources/${source.id}/alert-rules`)
notify(notifyAlertRuleUpdated(rule.name))
})
.catch(e => {
notify(notifyAlertRuleUpdateFailed(rule.name, e.data.message))
})
}
handleSave = () => {
const {rule} = this.props
if (rule.id === DEFAULT_RULE_ID) {
this.handleCreate()
} else {
this.handleEdit()
}
}
handleSaveToConfig = configName => () => {
const {rule, configLink, router} = this.props
const pathname = `${configLink}#${configName}`
if (this.validationError()) {
router.push({
pathname,
})
return
}
if (rule.id === DEFAULT_RULE_ID) {
this.handleCreate(pathname)
} else {
this.handleEdit(pathname)
}
}
handleAddEvery = frequency => {
const {
rule: {id: ruleID},
ruleActions: {addEvery},
} = this.props
addEvery(ruleID, frequency)
}
handleRemoveEvery = () => {
const {
rule: {id: ruleID},
ruleActions: {removeEvery},
} = this.props
removeEvery(ruleID)
}
validationError = () => {
const {rule, query} = this.props
if (rule.trigger === 'deadman') {
return this.deadmanValidation()
}
if (!buildInfluxQLQuery({}, query)) {
return notifyAlertRuleRequiresQuery()
}
if (!rule.values.value) {
return notifyAlertRuleRequiresConditionValue()
}
return ''
}
deadmanValidation = () => {
const {query} = this.props
if (query && (!query.database || !query.measurement)) {
return notifyAlertRuleDeadmanInvalid()
}
return ''
}
handleRuleTypeDropdownChange = ({type, text}) => {
const {ruleActions, rule} = this.props
ruleActions.updateRuleValues(rule.id, rule.trigger, {
...this.props.rule.values,
[type]: text,
})
}
handleRuleTypeInputChange = e => {
const {ruleActions, rule} = this.props
const {lower, upper} = e.target.form
ruleActions.updateRuleValues(rule.id, rule.trigger, {
...this.props.rule.values,
value: lower.value,
rangeValue: upper ? upper.value : '',
})
}
handleDeadmanChange = ({text}) => {
const {ruleActions, rule} = this.props
ruleActions.updateRuleValues(rule.id, rule.trigger, {period: text})
}
optionsComponents = () => {
return (
<RuleHeaderSave
onSave={this.handleSave}
validationError={this.validationError()}
/>
)
}
render() {
public render() {
const {
rule,
source,
@ -191,7 +93,7 @@ class KapacitorRule extends Component {
return (
<div className="page">
<PageHeader
title="Alert Rule Builder"
titleText="Alert Rule Builder"
optionsComponents={this.optionsComponents}
sourceIndicator={true}
/>
@ -225,7 +127,7 @@ class KapacitorRule extends Component {
ruleActions={ruleActions}
handlersFromConfig={handlersFromConfig}
onGoToConfig={this.handleSaveToConfig}
validationError={this.validationError()}
validationError={this.validationError}
/>
<RuleMessage rule={rule} ruleActions={ruleActions} />
</div>
@ -236,27 +138,147 @@ class KapacitorRule extends Component {
</div>
)
}
}
const {arrayOf, func, shape, string} = PropTypes
private handleChooseTimeRange = ({lower}: TimeRange) => {
const timeRange = timeRanges.find(range => range.lower === lower)
this.setState({timeRange})
}
KapacitorRule.propTypes = {
source: shape({}).isRequired,
rule: shape({
values: shape({}),
}).isRequired,
query: shape({}).isRequired,
queryConfigs: shape({}).isRequired,
queryConfigActions: shape({}).isRequired,
ruleActions: shape({}).isRequired,
notify: func.isRequired,
ruleID: string.isRequired,
handlersFromConfig: arrayOf(shape({})).isRequired,
router: shape({
push: func.isRequired,
}).isRequired,
kapacitor: shape({}).isRequired,
configLink: string.isRequired,
private handleCreate = (pathname?: string) => {
const {notify, queryConfigs, rule, source, router, kapacitor} = this.props
const newRule = Object.assign({}, rule, {
query: queryConfigs[rule.queryID],
})
delete newRule.queryID
createRule(kapacitor, newRule)
.then(() => {
router.push(pathname || `/sources/${source.id}/alert-rules`)
notify(notifyAlertRuleCreated(newRule.name))
})
.catch(e => {
notify(notifyAlertRuleCreateFailed(newRule.name, e.data.message))
})
}
private handleEdit = (pathname?: string) => {
const {notify, queryConfigs, rule, router, source} = this.props
const updatedRule = Object.assign({}, rule, {
query: queryConfigs[rule.queryID],
})
editRule(updatedRule)
.then(() => {
router.push(pathname || `/sources/${source.id}/alert-rules`)
notify(notifyAlertRuleUpdated(rule.name))
})
.catch(e => {
notify(notifyAlertRuleUpdateFailed(rule.name, e.data.message))
})
}
private handleSave = () => {
const {rule} = this.props
if (rule.id === DEFAULT_RULE_ID) {
this.handleCreate()
} else {
this.handleEdit()
}
}
private handleSaveToConfig = (configName: string) => () => {
const {rule, configLink, router} = this.props
const pathname = `${configLink}#${configName}`
if (this.validationError) {
router.push({
pathname,
})
return
}
if (rule.id === DEFAULT_RULE_ID) {
this.handleCreate(pathname)
} else {
this.handleEdit(pathname)
}
}
private handleAddEvery = (frequency: string) => {
const {
rule: {id: ruleID},
ruleActions: {addEvery},
} = this.props
addEvery(ruleID, frequency)
}
private handleRemoveEvery = () => {
const {
rule: {id: ruleID},
ruleActions: {removeEvery},
} = this.props
removeEvery(ruleID)
}
private get validationError(): string {
const {rule, query} = this.props
if (rule.trigger === 'deadman') {
return this.deadmanValidation()
}
if (!buildInfluxQLQuery({lower: ''}, query)) {
return notifyAlertRuleRequiresQuery()
}
if (!rule.values.value) {
return notifyAlertRuleRequiresConditionValue()
}
return ''
}
private deadmanValidation = () => {
const {query} = this.props
if (query && (!query.database || !query.measurement)) {
return notifyAlertRuleDeadmanInvalid()
}
return ''
}
private handleRuleTypeDropdownChange = ({type, text}: TypeItem) => {
const {ruleActions, rule} = this.props
ruleActions.updateRuleValues(rule.id, rule.trigger, {
...this.props.rule.values,
[type]: text,
})
}
private handleRuleTypeInputChange = (e: ChangeEvent<HTMLInputElement>) => {
const {ruleActions, rule} = this.props
const {lower, upper} = e.target.form
ruleActions.updateRuleValues(rule.id, rule.trigger, {
...this.props.rule.values,
value: lower.value,
rangeValue: upper ? upper.value : '',
})
}
private handleDeadmanChange = ({text}: Item) => {
const {ruleActions, rule} = this.props
ruleActions.updateRuleValues(rule.id, rule.trigger, {period: text})
}
private get optionsComponents(): JSX.Element {
return (
<RuleHeaderSave
onSave={this.handleSave}
validationError={this.validationError}
/>
)
}
}
const mapDispatchToProps = dispatch => ({

View File

@ -1,7 +1,12 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
const LogItemHTTP = ({logItem}) => (
import {LogItem} from 'src/types/kapacitor'
interface Props {
logItem: LogItem
}
const LogItemHTTP: SFC<Props> = ({logItem}) => (
<div className="logs-table--row">
<div className="logs-table--divider">
<div className={`logs-table--level ${logItem.lvl}`} />
@ -16,17 +21,4 @@ const LogItemHTTP = ({logItem}) => (
</div>
)
const {shape, string} = PropTypes
LogItemHTTP.propTypes = {
logItem: shape({
lvl: string.isRequired,
ts: string.isRequired,
method: string.isRequired,
username: string.isRequired,
host: string.isRequired,
duration: string.isRequired,
}),
}
export default LogItemHTTP

View File

@ -1,7 +1,12 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
const LogItemHTTPError = ({logItem}) => (
import {LogItem} from 'src/types/kapacitor'
interface Props {
logItem: LogItem
}
const LogItemHTTPError: SFC<Props> = ({logItem}) => (
<div className="logs-table--row" key={logItem.key}>
<div className="logs-table--divider">
<div className={`logs-table--level ${logItem.lvl}`} />
@ -16,15 +21,4 @@ const LogItemHTTPError = ({logItem}) => (
</div>
)
const {shape, string} = PropTypes
LogItemHTTPError.propTypes = {
logItem: shape({
key: string.isRequired,
lvl: string.isRequired,
ts: string.isRequired,
msg: string.isRequired,
}),
}
export default LogItemHTTPError

View File

@ -1,7 +1,12 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
const LogItemInfluxDBDebug = ({logItem}) => (
import {LogItem} from 'src/types/kapacitor'
interface Props {
logItem: LogItem
}
const LogItemInfluxDBDebug: SFC<Props> = ({logItem}) => (
<div className="logs-table--row">
<div className="logs-table--divider">
<div className={`logs-table--level ${logItem.lvl}`} />
@ -20,15 +25,4 @@ const LogItemInfluxDBDebug = ({logItem}) => (
</div>
)
const {shape, string} = PropTypes
LogItemInfluxDBDebug.propTypes = {
logItem: shape({
lvl: string.isRequired,
ts: string.isRequired,
msg: string.isRequired,
cluster: string.isRequired,
}),
}
export default LogItemInfluxDBDebug

View File

@ -1,7 +1,12 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
const LogItemKapacitorDebug = ({logItem}) => (
import {LogItem} from 'src/types/kapacitor'
interface Props {
logItem: LogItem
}
const LogItemKapacitorDebug: SFC<Props> = ({logItem}) => (
<div className="logs-table--row">
<div className="logs-table--divider">
<div className={`logs-table--level ${logItem.lvl}`} />
@ -16,14 +21,4 @@ const LogItemKapacitorDebug = ({logItem}) => (
</div>
)
const {shape, string} = PropTypes
LogItemKapacitorDebug.propTypes = {
logItem: shape({
lvl: string.isRequired,
ts: string.isRequired,
msg: string.isRequired,
}),
}
export default LogItemKapacitorDebug

View File

@ -1,7 +1,12 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
const LogItemKapacitorError = ({logItem}) => (
import {LogItem} from 'src/types/kapacitor'
interface Props {
logItem: LogItem
}
const LogItemKapacitorError: SFC<Props> = ({logItem}) => (
<div className="logs-table--row">
<div className="logs-table--divider">
<div className={`logs-table--level ${logItem.lvl}`} />
@ -16,14 +21,4 @@ const LogItemKapacitorError = ({logItem}) => (
</div>
)
const {shape, string} = PropTypes
LogItemKapacitorError.propTypes = {
logItem: shape({
lvl: string.isRequired,
ts: string.isRequired,
msg: string.isRequired,
}),
}
export default LogItemKapacitorError

View File

@ -1,51 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
const renderKeysAndValues = (object, name) => {
if (!object) {
return <span className="logs-table--empty-cell">--</span>
}
const sortedObjKeys = Object.keys(object).sort()
return (
<div className="logs-table--column">
<h1>{`${sortedObjKeys.length} ${name}`}</h1>
<div className="logs-table--scrollbox">
{sortedObjKeys.map(objKey => (
<div key={objKey} className="logs-table--key-value">
{objKey}: <span>{object[objKey]}</span>
</div>
))}
</div>
</div>
)
}
const LogItemKapacitorPoint = ({logItem}) => (
<div className="logs-table--row">
<div className="logs-table--divider">
<div className={`logs-table--level ${logItem.lvl}`} />
<div className="logs-table--timestamp">{logItem.ts}</div>
</div>
<div className="logs-table--details">
<div className="logs-table--service">Kapacitor Point</div>
<div className="logs-table--columns">
{renderKeysAndValues(logItem.tag, 'Tags')}
{renderKeysAndValues(logItem.field, 'Fields')}
</div>
</div>
</div>
)
const {shape, string} = PropTypes
LogItemKapacitorPoint.propTypes = {
logItem: shape({
lvl: string.isRequired,
ts: string.isRequired,
tag: shape.isRequired,
field: shape.isRequired,
}),
}
export default LogItemKapacitorPoint

View File

@ -0,0 +1,55 @@
import React, {PureComponent} from 'react'
import _ from 'lodash'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {LogItem} from 'src/types/kapacitor'
interface Props {
logItem: LogItem
}
@ErrorHandling
class LogItemKapacitorPoint extends PureComponent<Props> {
public render() {
const {logItem} = this.props
return (
<div className="logs-table--row">
<div className="logs-table--divider">
<div className={`logs-table--level ${logItem.lvl}`} />
<div className="logs-table--timestamp">{logItem.ts}</div>
</div>
<div className="logs-table--details">
<div className="logs-table--service">Kapacitor Point</div>
<div className="logs-table--columns">
{this.renderKeysAndValues(logItem.tag, 'Tags')}
{this.renderKeysAndValues(logItem.field, 'Fields')}
</div>
</div>
</div>
)
}
private renderKeysAndValues = (object: any, name: string) => {
if (_.isEmpty(object)) {
return <span className="logs-table--empty-cell">--</span>
}
const sortedObjKeys = Object.keys(object).sort()
return (
<div className="logs-table--column">
<h1>{`${sortedObjKeys.length} ${name}`}</h1>
<div className="logs-table--scrollbox">
{sortedObjKeys.map(objKey => (
<div key={objKey} className="logs-table--key-value">
{objKey}: <span>{object[objKey]}</span>
</div>
))}
</div>
</div>
)
}
}
export default LogItemKapacitorPoint

View File

@ -1,7 +1,12 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
const LogItemSession = ({logItem}) => (
import {LogItem} from 'src/types/kapacitor'
interface Props {
logItem: LogItem
}
const LogItemSession: SFC<Props> = ({logItem}) => (
<div className="logs-table--row">
<div className="logs-table--divider">
<div className={`logs-table--level ${logItem.lvl}`} />
@ -13,14 +18,4 @@ const LogItemSession = ({logItem}) => (
</div>
)
const {shape, string} = PropTypes
LogItemSession.propTypes = {
logItem: shape({
lvl: string.isRequired,
ts: string.isRequired,
msg: string.isRequired,
}),
}
export default LogItemSession

View File

@ -1,12 +1,17 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
import LogsTableRow from 'src/kapacitor/components/LogsTableRow'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import {LogItem} from 'src/types/kapacitor'
const numLogsToRender = 200
const LogsTable = ({logs}) => (
interface Props {
logs: LogItem[]
}
const LogsTable: SFC<Props> = ({logs}) => (
<div className="logs-table">
<div className="logs-table--header">
{`${numLogsToRender} Most Recent Logs`}
@ -22,17 +27,4 @@ const LogsTable = ({logs}) => (
</div>
)
const {arrayOf, shape, string} = PropTypes
LogsTable.propTypes = {
logs: arrayOf(
shape({
key: string.isRequired,
ts: string.isRequired,
lvl: string.isRequired,
msg: string.isRequired,
})
).isRequired,
}
export default LogsTable

View File

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
import LogItemSession from 'src/kapacitor/components/LogItemSession'
import LogItemHTTP from 'src/kapacitor/components/LogItemHTTP'
@ -9,7 +8,13 @@ import LogItemKapacitorError from 'src/kapacitor/components/LogItemKapacitorErro
import LogItemKapacitorDebug from 'src/kapacitor/components/LogItemKapacitorDebug'
import LogItemInfluxDBDebug from 'src/kapacitor/components/LogItemInfluxDBDebug'
const LogsTableRow = ({logItem}) => {
import {LogItem} from 'src/types/kapacitor'
interface Props {
logItem: LogItem
}
const LogsTableRow: SFC<Props> = ({logItem}) => {
if (logItem.service === 'sessions') {
return <LogItemSession logItem={logItem} />
}
@ -51,15 +56,4 @@ const LogsTableRow = ({logItem}) => {
)
}
const {shape, string} = PropTypes
LogsTableRow.propTypes = {
logItem: shape({
key: string.isRequired,
ts: string.isRequired,
lvl: string.isRequired,
msg: string.isRequired,
}).isRequired,
}
export default LogsTableRow

View File

@ -1,11 +1,25 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import React, {Component, ChangeEvent, KeyboardEvent} from 'react'
import {DEFAULT_RULE_ID} from 'src/kapacitor/constants'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {AlertRule} from 'src/types'
interface Props {
defaultName: string
onRuleRename: (id: string, name: string) => void
rule: AlertRule
}
interface State {
reset: boolean
}
@ErrorHandling
class NameSection extends Component {
constructor(props) {
class NameSection extends Component<Props, State> {
private inputRef: HTMLInputElement
constructor(props: Props) {
super(props)
this.state = {
@ -13,14 +27,22 @@ class NameSection extends Component {
}
}
handleInputBlur = reset => e => {
public handleInputBlur = (reset: boolean) => (
e: ChangeEvent<HTMLInputElement>
): void => {
const {defaultName, onRuleRename, rule} = this.props
onRuleRename(rule.id, reset ? defaultName : e.target.value)
let ruleName: string
if (reset) {
ruleName = defaultName
} else {
ruleName = e.target.value
}
onRuleRename(rule.id, ruleName)
this.setState({reset: false})
}
handleKeyDown = e => {
public handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
this.inputRef.blur()
}
@ -30,15 +52,13 @@ class NameSection extends Component {
}
}
render() {
const {rule, defaultName} = this.props
public render() {
const {defaultName} = this.props
const {reset} = this.state
return (
<div className="rule-section">
<h3 className="rule-section--heading">
{rule.id === DEFAULT_RULE_ID ? 'Name this Alert Rule' : 'Name'}
</h3>
<h3 className="rule-section--heading">{this.header}</h3>
<div className="rule-section--body">
<div className="rule-section--row rule-section--row-first rule-section--row-last">
<input
@ -55,14 +75,18 @@ class NameSection extends Component {
</div>
)
}
}
const {func, string, shape} = PropTypes
private get header() {
const {
rule: {id},
} = this.props
NameSection.propTypes = {
defaultName: string.isRequired,
onRuleRename: func.isRequired,
rule: shape({}).isRequired,
if (id === DEFAULT_RULE_ID) {
return 'Name this Alert Rule'
}
return 'Name'
}
}
export default NameSection

View File

@ -1,14 +1,27 @@
import React from 'react'
import PropTypes from 'prop-types'
import {CHANGES, RELATIVE_OPERATORS, SHIFTS} from 'src/kapacitor/constants'
import Dropdown from 'shared/components/Dropdown'
import React, {SFC, ChangeEvent} from 'react'
const mapToItems = (arr, type) => arr.map(text => ({text, type}))
import {CHANGES, RELATIVE_OPERATORS, SHIFTS} from 'src/kapacitor/constants'
import Dropdown from 'src/shared/components/Dropdown'
import {AlertRule} from 'src/types'
const mapToItems = (arr: string[], type: string) =>
arr.map(text => ({text, type}))
const changes = mapToItems(CHANGES, 'change')
const shifts = mapToItems(SHIFTS, 'shift')
const operators = mapToItems(RELATIVE_OPERATORS, 'operator')
const Relative = ({
interface TypeItem {
type: string
text: string
}
interface Props {
onRuleTypeInputChange: (e: ChangeEvent<HTMLInputElement>) => void
onDropdownChange: (item: TypeItem) => void
rule: AlertRule
}
const Relative: SFC<Props> = ({
onRuleTypeInputChange,
onDropdownChange,
rule: {
@ -46,7 +59,7 @@ const Relative = ({
style={{width: '160px', marginLeft: '6px'}}
type="text"
name="lower"
spellCheck="false"
spellCheck={false}
value={value}
onChange={onRuleTypeInputChange}
required={true}
@ -56,19 +69,4 @@ const Relative = ({
</div>
)
const {shape, string, func} = PropTypes
Relative.propTypes = {
onRuleTypeInputChange: func.isRequired,
onDropdownChange: func.isRequired,
rule: shape({
values: shape({
change: string,
shift: string,
operator: string,
value: string,
}),
}),
}
export default Relative

View File

@ -26,7 +26,7 @@ interface Props {
rule: AlertRule
ruleActions: RuleActions
handlersFromConfig: Handler[]
onGoToConfig: () => void
onGoToConfig: (configName: string) => void
validationError: string
}

View File

@ -1,22 +1,24 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import React, {Component, ChangeEvent} from 'react'
import RuleMessageText from 'src/kapacitor/components/RuleMessageText'
import RuleMessageTemplates from 'src/kapacitor/components/RuleMessageTemplates'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {AlertRule} from 'src/types'
import {KapacitorRuleActions} from 'src/types/actions'
interface Props {
rule: AlertRule
ruleActions: KapacitorRuleActions
}
@ErrorHandling
class RuleMessage extends Component {
class RuleMessage extends Component<Props> {
constructor(props) {
super(props)
}
handleChangeMessage = e => {
const {ruleActions, rule} = this.props
ruleActions.updateMessage(rule.id, e.target.value)
}
render() {
public render() {
const {rule, ruleActions} = this.props
return (
@ -35,15 +37,11 @@ class RuleMessage extends Component {
</div>
)
}
}
const {func, shape} = PropTypes
RuleMessage.propTypes = {
rule: shape().isRequired,
ruleActions: shape({
updateMessage: func.isRequired,
}).isRequired,
private handleChangeMessage = (e: ChangeEvent<HTMLTextAreaElement>) => {
const {ruleActions, rule} = this.props
ruleActions.updateMessage(rule.id, e.target.value)
}
}
export default RuleMessage

View File

@ -1,5 +1,5 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import ReactTooltip from 'react-tooltip'
@ -8,19 +8,22 @@ import CodeData from 'src/kapacitor/components/CodeData'
import {RULE_MESSAGE_TEMPLATES} from 'src/kapacitor/constants'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {RuleMessage} from 'src/types/kapacitor'
import {AlertRule} from 'src/types'
interface Props {
rule: AlertRule
updateMessage: (id: string, message: string) => void
}
// needs to be React Component for CodeData click handler to work
@ErrorHandling
class RuleMessageTemplates extends Component {
class RuleMessageTemplates extends Component<Props> {
constructor(props) {
super(props)
}
handleClickTemplate = template => () => {
const {updateMessage, rule} = this.props
updateMessage(rule.id, `${rule.message} ${template.label}`)
}
render() {
public render() {
return (
<div className="rule-section--row rule-section--row-last">
<p>Templates:</p>
@ -41,13 +44,11 @@ class RuleMessageTemplates extends Component {
</div>
)
}
}
const {func, shape} = PropTypes
RuleMessageTemplates.propTypes = {
rule: shape().isRequired,
updateMessage: func.isRequired,
private handleClickTemplate = (template: RuleMessage) => () => {
const {updateMessage, rule} = this.props
updateMessage(rule.id, `${rule.message} ${template.label}`)
}
}
export default RuleMessageTemplates

View File

@ -1,7 +1,13 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC, ChangeEvent} from 'react'
const RuleMessageText = ({rule, updateMessage}) => (
import {AlertRule} from 'src/types'
interface Props {
rule: AlertRule
updateMessage: (e: ChangeEvent<HTMLTextAreaElement>) => void
}
const RuleMessageText: SFC<Props> = ({rule, updateMessage}) => (
<div className="rule-builder--message">
<textarea
className="form-control input-sm form-malachite monotype"
@ -13,11 +19,4 @@ const RuleMessageText = ({rule, updateMessage}) => (
</div>
)
const {func, shape} = PropTypes
RuleMessageText.propTypes = {
rule: shape().isRequired,
updateMessage: func.isRequired,
}
export default RuleMessageText

View File

@ -1,85 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import {THRESHOLD_OPERATORS} from 'src/kapacitor/constants'
import Dropdown from 'shared/components/Dropdown'
import _ from 'lodash'
const mapToItems = (arr, type) => arr.map(text => ({text, type}))
const operators = mapToItems(THRESHOLD_OPERATORS, 'operator')
const noopSubmit = e => e.preventDefault()
const getField = ({fields}) => {
const alias = _.get(fields, ['0', 'alias'], false)
if (!alias) {
return _.get(fields, ['0', 'value'], 'Select a Time-Series')
}
return alias
}
const Threshold = ({
rule: {
values: {operator, value, rangeValue},
},
query,
onDropdownChange,
onRuleTypeInputChange,
}) => (
<div className="rule-section--row rule-section--row-first rule-section--border-bottom">
<p>Send Alert where</p>
<span className="rule-builder--metric">{getField(query)}</span>
<p>is</p>
<Dropdown
className="dropdown-180"
menuClass="dropdown-malachite"
items={operators}
selected={operator}
onChoose={onDropdownChange}
/>
<form style={{display: 'flex'}} onSubmit={noopSubmit}>
<input
className="form-control input-sm form-malachite monotype"
style={{width: '160px', marginLeft: '6px'}}
type="text"
name="lower"
spellCheck="false"
value={value}
onChange={onRuleTypeInputChange}
placeholder={
operator === 'inside range' || operator === 'outside range'
? 'Lower'
: null
}
/>
{(operator === 'inside range' || operator === 'outside range') && (
<input
className="form-control input-sm form-malachite monotype"
name="upper"
style={{width: '160px'}}
placeholder="Upper"
type="text"
spellCheck="false"
value={rangeValue}
onChange={onRuleTypeInputChange}
/>
)}
</form>
</div>
)
const {shape, string, func} = PropTypes
Threshold.propTypes = {
rule: shape({
values: shape({
operator: string,
rangeOperator: string,
value: string,
rangeValue: string,
}),
}),
onDropdownChange: func.isRequired,
onRuleTypeInputChange: func.isRequired,
query: shape({}).isRequired,
}
export default Threshold

View File

@ -0,0 +1,122 @@
import React, {Component, FormEvent, ChangeEvent} from 'react'
import {THRESHOLD_OPERATORS} from 'src/kapacitor/constants'
import Dropdown from 'src/shared/components/Dropdown'
import {getDeep} from 'src/utils/wrappers'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {AlertRule, QueryConfig} from 'src/types'
interface TypeItem {
type: string
text: string
}
interface Props {
rule: AlertRule
onDropdownChange: (item: TypeItem) => void
onRuleTypeInputChange: (e: ChangeEvent<HTMLInputElement>) => void
query: QueryConfig
}
@ErrorHandling
class Threshold extends Component<Props> {
public render() {
const {
rule: {
values: {operator, value},
},
query,
onDropdownChange,
onRuleTypeInputChange,
} = this.props
return (
<div className="rule-section--row rule-section--row-first rule-section--border-bottom">
<p>Send Alert where</p>
<span className="rule-builder--metric">{this.getField(query)}</span>
<p>is</p>
<Dropdown
className="dropdown-180"
menuClass="dropdown-malachite"
items={this.operators}
selected={operator}
onChoose={onDropdownChange}
/>
<form style={{display: 'flex'}} onSubmit={this.noopSubmit}>
<input
className="form-control input-sm form-malachite monotype"
style={{width: '160px', marginLeft: '6px'}}
type="text"
name="lower"
spellCheck={false}
value={value}
onChange={onRuleTypeInputChange}
placeholder={this.firstInputPlaceholder}
/>
{this.secondInput}
</form>
</div>
)
}
private get operators() {
const type = 'operator'
return THRESHOLD_OPERATORS.map(text => {
return {text, type}
})
}
private get isSecondInputRequired() {
const {rule} = this.props
const operator = getDeep<string>(rule, 'values.operator', '')
if (operator === 'inside range' || operator === 'outside range') {
return true
}
return false
}
private get firstInputPlaceholder() {
if (this.isSecondInputRequired) {
return 'lower'
}
return null
}
private get secondInput() {
const {rule, onRuleTypeInputChange} = this.props
const rangeValue = getDeep<string>(rule, 'values.rangeValue', '')
if (this.isSecondInputRequired) {
return (
<input
className="form-control input-sm form-malachite monotype"
name="upper"
style={{width: '160px'}}
placeholder="Upper"
type="text"
spellCheck={false}
value={rangeValue}
onChange={onRuleTypeInputChange}
/>
)
}
}
private noopSubmit = (e: FormEvent<HTMLElement>) => e.preventDefault()
private getField = ({fields}: QueryConfig): string => {
const alias = getDeep<string>(fields, '0.alias', '')
if (!alias) {
return getDeep<string>(fields, '0.value', 'Select a Time-Series')
}
return alias
}
}
export default Threshold

View File

@ -1,89 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader'
import TickscriptEditor from 'src/kapacitor/components/TickscriptEditor'
import TickscriptEditorControls from 'src/kapacitor/components/TickscriptEditorControls'
import TickscriptEditorConsole from 'src/kapacitor/components/TickscriptEditorConsole'
import LogsTable from 'src/kapacitor/components/LogsTable'
const Tickscript = ({
onSave,
onExit,
task,
logs,
consoleMessage,
onSelectDbrps,
onChangeScript,
onChangeType,
onChangeID,
unsavedChanges,
isNewTickscript,
areLogsVisible,
areLogsEnabled,
onToggleLogsVisibility,
}) => (
<div className="page">
<TickscriptHeader
task={task}
onSave={onSave}
onExit={onExit}
unsavedChanges={unsavedChanges}
areLogsVisible={areLogsVisible}
areLogsEnabled={areLogsEnabled}
onToggleLogsVisibility={onToggleLogsVisibility}
isNewTickscript={isNewTickscript}
/>
<div className="page-contents--split">
<div
className="tickscript"
style={areLogsVisible ? {maxWidth: '50%'} : null}
>
<TickscriptEditorControls
isNewTickscript={isNewTickscript}
onSelectDbrps={onSelectDbrps}
onChangeType={onChangeType}
onChangeID={onChangeID}
task={task}
/>
<TickscriptEditor
script={task.tickscript}
onChangeScript={onChangeScript}
/>
<TickscriptEditorConsole
consoleMessage={consoleMessage}
unsavedChanges={unsavedChanges}
/>
</div>
{areLogsVisible ? <LogsTable logs={logs} /> : null}
</div>
</div>
)
const {arrayOf, bool, func, shape, string} = PropTypes
Tickscript.propTypes = {
logs: arrayOf(shape()).isRequired,
onSave: func.isRequired,
onExit: func.isRequired,
source: shape({
id: string,
}),
areLogsVisible: bool,
areLogsEnabled: bool,
onToggleLogsVisibility: func.isRequired,
task: shape({
id: string,
script: string,
dbsrps: arrayOf(shape()),
}).isRequired,
onChangeScript: func.isRequired,
onSelectDbrps: func.isRequired,
consoleMessage: string,
onChangeType: func.isRequired,
onChangeID: func.isRequired,
isNewTickscript: bool.isRequired,
unsavedChanges: bool,
}
export default Tickscript

View File

@ -0,0 +1,101 @@
import React, {PureComponent, MouseEvent, ChangeEvent} from 'react'
import TickscriptHeader from 'src/kapacitor/components/TickscriptHeader'
import TickscriptEditor from 'src/kapacitor/components/TickscriptEditor'
import TickscriptEditorControls from 'src/kapacitor/components/TickscriptEditorControls'
import TickscriptEditorConsole from 'src/kapacitor/components/TickscriptEditorConsole'
import LogsTable from 'src/kapacitor/components/LogsTable'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {Task} from 'src/types'
import {LogItem, DBRP} from 'src/types/kapacitor'
interface Props {
logs: LogItem[]
onSave: () => void
onExit: () => void
areLogsVisible: boolean
areLogsEnabled: boolean
onToggleLogsVisibility: () => void
task: Task
onChangeScript: (tickscript: string) => void
onSelectDbrps: (dbrps: DBRP[]) => void
consoleMessage: string
onChangeType: (type: string) => (event: MouseEvent<HTMLLIElement>) => void
onChangeID: (e: ChangeEvent<HTMLInputElement>) => void
isNewTickscript: boolean
unsavedChanges: boolean
}
@ErrorHandling
class Tickscript extends PureComponent<Props> {
public render() {
const {
onSave,
onExit,
task,
consoleMessage,
onSelectDbrps,
onChangeScript,
onChangeType,
onChangeID,
unsavedChanges,
isNewTickscript,
areLogsVisible,
areLogsEnabled,
onToggleLogsVisibility,
} = this.props
return (
<div className="page">
<TickscriptHeader
task={task}
onSave={onSave}
onExit={onExit}
unsavedChanges={unsavedChanges}
areLogsVisible={areLogsVisible}
areLogsEnabled={areLogsEnabled}
onToggleLogsVisibility={onToggleLogsVisibility}
isNewTickscript={isNewTickscript}
/>
<div className="page-contents--split">
<div className="tickscript" style={this.style}>
<TickscriptEditorControls
isNewTickscript={isNewTickscript}
onSelectDbrps={onSelectDbrps}
onChangeType={onChangeType}
onChangeID={onChangeID}
task={task}
/>
<TickscriptEditor
script={task.tickscript}
onChangeScript={onChangeScript}
/>
<TickscriptEditorConsole
consoleMessage={consoleMessage}
unsavedChanges={unsavedChanges}
/>
</div>
{this.logsTable}
</div>
</div>
)
}
private get style() {
const {areLogsVisible} = this.props
if (areLogsVisible) {
return {maxWidth: '50%'}
}
}
private get logsTable() {
const {areLogsVisible, logs} = this.props
if (areLogsVisible) {
return <LogsTable logs={logs} />
}
}
}
export default Tickscript

View File

@ -1,20 +1,24 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {Controlled as CodeMirror} from 'react-codemirror2'
import 'src/external/codemirror'
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
onChangeScript: (tickscript: string) => void
script: string
}
const NOOP = () => {}
@ErrorHandling
class TickscriptEditor extends Component {
class TickscriptEditor extends Component<Props> {
constructor(props) {
super(props)
}
updateCode = (_, __, script) => {
this.props.onChangeScript(script)
}
render() {
public render() {
const {script} = this.props
const options = {
@ -31,17 +35,15 @@ class TickscriptEditor extends Component {
value={script}
onBeforeChange={this.updateCode}
options={options}
onTouchStart={NOOP}
/>
</div>
)
}
}
const {func, string} = PropTypes
TickscriptEditor.propTypes = {
onChangeScript: func,
script: string,
private updateCode = (_, __, script) => {
this.props.onChangeScript(script)
}
}
export default TickscriptEditor

View File

@ -1,7 +1,14 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
const TickscriptEditorConsole = ({consoleMessage, unsavedChanges}) => {
interface Props {
consoleMessage: string
unsavedChanges: boolean
}
const TickscriptEditorConsole: SFC<Props> = ({
consoleMessage,
unsavedChanges,
}) => {
let consoleOutput = 'TICKscript is valid'
let consoleClass = 'tickscript-console--valid'
@ -20,11 +27,4 @@ const TickscriptEditorConsole = ({consoleMessage, unsavedChanges}) => {
)
}
const {bool, string} = PropTypes
TickscriptEditorConsole.propTypes = {
consoleMessage: string,
unsavedChanges: bool,
}
export default TickscriptEditorConsole

View File

@ -1,48 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import TickscriptType from 'src/kapacitor/components/TickscriptType'
import MultiSelectDBDropdown from 'shared/components/MultiSelectDBDropdown'
import TickscriptID, {
TickscriptStaticID,
} from 'src/kapacitor/components/TickscriptID'
const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`}))
const TickscriptEditorControls = ({
isNewTickscript,
onSelectDbrps,
onChangeType,
onChangeID,
task,
}) => (
<div className="tickscript-controls">
{isNewTickscript ? (
<TickscriptID onChangeID={onChangeID} id={task.id} />
) : (
<TickscriptStaticID id={task.name ? task.name : task.id} />
)}
<div className="tickscript-controls--right">
<TickscriptType type={task.type} onChangeType={onChangeType} />
<MultiSelectDBDropdown
selectedItems={addName(task.dbrps)}
onApply={onSelectDbrps}
/>
</div>
</div>
)
const {arrayOf, bool, func, shape, string} = PropTypes
TickscriptEditorControls.propTypes = {
isNewTickscript: bool.isRequired,
onSelectDbrps: func.isRequired,
onChangeType: func.isRequired,
onChangeID: func.isRequired,
task: shape({
id: string,
script: string,
dbsrps: arrayOf(shape()),
}).isRequired,
}
export default TickscriptEditorControls

View File

@ -0,0 +1,67 @@
import React, {Component, MouseEvent, ChangeEvent} from 'react'
import TickscriptType from 'src/kapacitor/components/TickscriptType'
import MultiSelectDBDropdown from 'src/shared/components/MultiSelectDBDropdown'
import TickscriptID, {
TickscriptStaticID,
} from 'src/kapacitor/components/TickscriptID'
import {Task} from 'src/types'
import {DBRP} from 'src/types/kapacitor'
interface DBRPDropdownItem extends DBRP {
name: string
}
interface Props {
isNewTickscript: boolean
onSelectDbrps: (dbrps: DBRP[]) => void
onChangeType: (type: string) => (event: MouseEvent<HTMLLIElement>) => void
onChangeID: (e: ChangeEvent<HTMLInputElement>) => void
task: Task
}
class TickscriptEditorControls extends Component<Props> {
public render() {
const {onSelectDbrps, onChangeType, task} = this.props
return (
<div className="tickscript-controls">
{this.tickscriptID}
<div className="tickscript-controls--right">
<TickscriptType type={task.type} onChangeType={onChangeType} />
<MultiSelectDBDropdown
selectedItems={this.addName(task.dbrps)}
onApply={onSelectDbrps}
/>
</div>
</div>
)
}
private get tickscriptID() {
const {isNewTickscript, onChangeID, task} = this.props
if (isNewTickscript) {
return <TickscriptID onChangeID={onChangeID} id={task.id} />
}
return <TickscriptStaticID id={this.taskID} />
}
private get taskID() {
const {
task: {name, id},
} = this.props
if (name) {
return name
}
return id
}
private addName = (list: DBRP[]): DBRPDropdownItem[] => {
const listWithName = list.map(l => ({...l, name: `${l.db}.${l.rp}`}))
return listWithName
}
}
export default TickscriptEditorControls

View File

@ -1,14 +1,18 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import React, {Component, SFC, ChangeEvent} from 'react'
import {ErrorHandling} from 'src/shared/decorators/errors'
interface TickscriptIDProps {
onChangeID: (e: ChangeEvent<HTMLInputElement>) => void
id: string
}
@ErrorHandling
class TickscriptID extends Component {
class TickscriptID extends Component<TickscriptIDProps> {
constructor(props) {
super(props)
}
render() {
public render() {
const {onChangeID, id} = this.props
return (
@ -25,19 +29,11 @@ class TickscriptID extends Component {
}
}
export const TickscriptStaticID = ({id}) => (
interface TickscriptStaticIDProps {
id: string
}
export const TickscriptStaticID: SFC<TickscriptStaticIDProps> = ({id}) => (
<h1 className="tickscript-controls--name">{id}</h1>
)
const {func, string} = PropTypes
TickscriptID.propTypes = {
onChangeID: func.isRequired,
id: string.isRequired,
}
TickscriptStaticID.propTypes = {
id: string.isRequired,
}
export default TickscriptID

View File

@ -1,30 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
const STREAM = 'stream'
const BATCH = 'batch'
const TickscriptType = ({type, onChangeType}) => (
<ul className="nav nav-tablist nav-tablist-sm">
<li
className={type === STREAM ? 'active' : ''}
onClick={onChangeType(STREAM)}
>
Stream
</li>
<li
className={type === BATCH ? 'active' : ''}
onClick={onChangeType(BATCH)}
>
Batch
</li>
</ul>
)
const {string, func} = PropTypes
TickscriptType.propTypes = {
type: string,
onChangeType: func,
}
export default TickscriptType

View File

@ -0,0 +1,40 @@
import React, {PureComponent, MouseEvent} from 'react'
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
type: string
onChangeType: (type: string) => (event: MouseEvent<HTMLLIElement>) => void
}
const STREAM = 'stream'
const BATCH = 'batch'
@ErrorHandling
class TickscriptType extends PureComponent<Props> {
public render() {
const {onChangeType} = this.props
return (
<ul className="nav nav-tablist nav-tablist-sm">
<li
className={this.getClassName(STREAM)}
onClick={onChangeType(STREAM)}
>
Stream
</li>
<li className={this.getClassName(BATCH)} onClick={onChangeType(BATCH)}>
Batch
</li>
</ul>
)
}
private getClassName(type: string) {
if (type === this.props.type) {
return 'active'
}
return ''
}
}
export default TickscriptType

View File

@ -1,6 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC, ChangeEvent} from 'react'
import _ from 'lodash'
import Deadman from 'src/kapacitor/components/Deadman'
@ -9,7 +7,16 @@ import Relative from 'src/kapacitor/components/Relative'
import DataSection from 'src/kapacitor/components/DataSection'
import RuleGraph from 'src/kapacitor/components/RuleGraph'
import {Tab, TabList, TabPanels, TabPanel, Tabs} from 'shared/components/Tabs'
import {
Tab,
TabList,
TabPanels,
TabPanel,
Tabs,
} from 'src/shared/components/Tabs'
import {AlertRule, QueryConfig, Source, TimeRange} from 'src/types'
import {KapacitorQueryConfigActions} from 'src/types/actions'
const TABS = ['Threshold', 'Relative', 'Deadman']
@ -22,13 +29,36 @@ const handleChooseTrigger = (rule, onChooseTrigger) => triggerIndex => {
const initialIndex = rule => TABS.indexOf(_.startCase(rule.trigger))
const isDeadman = rule => rule.trigger === 'deadman'
const ValuesSection = ({
interface Item {
text: string
}
interface TypeItem extends Item {
type: string
}
interface Props {
rule: AlertRule
onChooseTrigger: () => void
onUpdateValues: () => void
query: QueryConfig
onDeadmanChange: (item: Item) => void
onRuleTypeDropdownChange: (item: TypeItem) => void
onRuleTypeInputChange: (e: ChangeEvent<HTMLInputElement>) => void
onAddEvery: (frequency: string) => void
onRemoveEvery: () => void
timeRange: TimeRange
queryConfigActions: KapacitorQueryConfigActions
source: Source
onChooseTimeRange: (timeRange: TimeRange) => void
}
const ValuesSection: SFC<Props> = ({
rule,
query,
source,
timeRange,
onAddEvery,
onRemoveEvery,
onChooseTrigger,
onDeadmanChange,
onChooseTimeRange,
@ -58,7 +88,6 @@ const ValuesSection = ({
isKapacitorRule={true}
actions={queryConfigActions}
onAddEvery={onAddEvery}
onRemoveEvery={onRemoveEvery}
isDeadman={isDeadman(rule)}
/>
</div>
@ -97,24 +126,4 @@ const ValuesSection = ({
</div>
)
const {shape, string, func} = PropTypes
ValuesSection.propTypes = {
rule: shape({
id: string,
}).isRequired,
onChooseTrigger: func.isRequired,
onUpdateValues: func.isRequired,
query: shape({}).isRequired,
onDeadmanChange: func.isRequired,
onRuleTypeDropdownChange: func.isRequired,
onRuleTypeInputChange: func.isRequired,
onAddEvery: func.isRequired,
onRemoveEvery: func.isRequired,
timeRange: shape({}).isRequired,
queryConfigActions: shape({}).isRequired,
source: shape({}).isRequired,
onChooseTimeRange: func.isRequired,
}
export default ValuesSection

View File

@ -1,26 +1,59 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import {InjectedRouter} from 'react-router'
import {connect} from 'react-redux'
import * as kapacitorRuleActionCreators from 'src/kapacitor/actions/view'
import * as kapacitorQueryConfigActionCreators from 'src/kapacitor/actions/queryConfigs'
import {bindActionCreators} from 'redux'
import {getActiveKapacitor, getKapacitorConfig} from 'shared/apis/index'
import {getActiveKapacitor, getKapacitorConfig} from 'src/shared/apis/index'
import {DEFAULT_RULE_ID} from 'src/kapacitor/constants'
import KapacitorRule from 'src/kapacitor/components/KapacitorRule'
import parseHandlersFromConfig from 'src/shared/parsing/parseHandlersFromConfig'
import {notify as notifyAction} from 'shared/actions/notifications'
import {notify as notifyAction} from 'src/shared/actions/notifications'
import {
notifyKapacitorCreateFailed,
notifyCouldNotFindKapacitor,
} from 'shared/copy/notifications'
} from 'src/shared/copy/notifications'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {
Source,
Notification,
AlertRule,
QueryConfig,
Kapacitor,
} from 'src/types'
import {
KapacitorQueryConfigActions,
KapacitorRuleActions,
} from 'src/types/actions'
interface Params {
ruleID: string
}
interface Props {
source: Source
notify: (notification: Notification) => void
rules: AlertRule[]
queryConfigs: QueryConfig[]
ruleActions: KapacitorRuleActions
queryConfigActions: KapacitorQueryConfigActions
params: Params
router: InjectedRouter
}
interface State {
handlersFromConfig: any[]
kapacitor: Kapacitor | {}
}
@ErrorHandling
class KapacitorRulePage extends Component {
constructor(props) {
class KapacitorRulePage extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
@ -29,7 +62,7 @@ class KapacitorRulePage extends Component {
}
}
async componentDidMount() {
public async componentDidMount() {
const {params, source, ruleActions, notify} = this.props
if (params.ruleID === 'new') {
@ -54,9 +87,8 @@ class KapacitorRulePage extends Component {
}
}
render() {
public render() {
const {
rules,
params,
source,
router,
@ -65,8 +97,7 @@ class KapacitorRulePage extends Component {
queryConfigActions,
} = this.props
const {handlersFromConfig, kapacitor} = this.state
const rule =
params.ruleID === 'new' ? rules[DEFAULT_RULE_ID] : rules[params.ruleID]
const rule = this.rule
const query = rule && queryConfigs[rule.queryID]
if (!query) {
@ -84,41 +115,26 @@ class KapacitorRulePage extends Component {
ruleID={params.ruleID}
router={router}
kapacitor={kapacitor}
configLink={`/sources/${source.id}/kapacitors/${kapacitor.id}/edit`}
configLink={`/sources/${source.id}/kapacitors/${this.kapacitorID}/edit`}
/>
)
}
}
const {func, shape, string} = PropTypes
private get kapacitorID(): string {
const {kapacitor} = this.state
return _.get(kapacitor, 'id')
}
KapacitorRulePage.propTypes = {
source: shape({
links: shape({
proxy: string.isRequired,
self: string.isRequired,
}),
}),
notify: func,
rules: shape({}).isRequired,
queryConfigs: shape({}).isRequired,
ruleActions: shape({
loadDefaultRule: func.isRequired,
fetchRule: func.isRequired,
chooseTrigger: func.isRequired,
addEvery: func.isRequired,
removeEvery: func.isRequired,
updateRuleValues: func.isRequired,
updateMessage: func.isRequired,
updateRuleName: func.isRequired,
}).isRequired,
queryConfigActions: shape({}).isRequired,
params: shape({
ruleID: string,
}).isRequired,
router: shape({
push: func.isRequired,
}).isRequired,
private get rule(): AlertRule {
const {params, rules} = this.props
const ruleID = _.get(params, 'ruleID')
if (ruleID === 'new') {
return rules[DEFAULT_RULE_ID]
}
return rules[params.ruleID]
}
}
const mapStateToProps = ({rules, kapacitorQueryConfigs: queryConfigs}) => ({

View File

@ -1,4 +1,4 @@
import React, {PureComponent} from 'react'
import React, {PureComponent, ChangeEvent} from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import uuid from 'uuid'
@ -18,7 +18,7 @@ import {
Notification,
NotificationFunc,
} from 'src/types'
import {LogItem, DBRP} from 'src/types/kapacitor'
import {
notifyTickscriptLoggingUnavailable,
notifyTickscriptLoggingError,
@ -76,7 +76,7 @@ interface State {
task: Task
consoleMessage: string
isEditingID: boolean
logs: object[]
logs: LogItem[]
areLogsVisible: boolean
areLogsEnabled: boolean
failStr: string
@ -247,22 +247,22 @@ export class TickscriptPage extends PureComponent<Props, State> {
return router.push(`/sources/${sourceID}/alert-rules`)
}
private handleChangeScript = tickscript => {
private handleChangeScript = (tickscript: string) => {
this.setState({
task: {...this.state.task, tickscript},
unsavedChanges: true,
})
}
private handleSelectDbrps = dbrps => {
private handleSelectDbrps = (dbrps: DBRP[]) => {
this.setState({task: {...this.state.task, dbrps}, unsavedChanges: true})
}
private handleChangeType = type => () => {
private handleChangeType = (type: string) => () => {
this.setState({task: {...this.state.task, type}, unsavedChanges: true})
}
private handleChangeID = e => {
private handleChangeID = (e: ChangeEvent<HTMLInputElement>) => {
this.setState({
task: {...this.state.task, id: e.target.value},
unsavedChanges: true,

View File

@ -3,7 +3,7 @@ import {Notification} from 'src/types'
export type Action = ActionPublishNotification | ActionDismissNotification
// Publish notification
export type PubishNotification = (n: Notification) => ActionPublishNotification
export type PublishNotification = (n: Notification) => ActionPublishNotification
export interface ActionPublishNotification {
type: 'PUBLISH_NOTIFICATION'
payload: {

View File

@ -1,20 +1,47 @@
import {proxy} from 'src/utils/queryUrlGenerator'
import {noop} from 'src/shared/actions/app'
import _ from 'lodash'
import {errorThrown} from 'src/shared/actions/errors'
import {TimeSeriesResponse, TimeSeriesSeries} from 'src/types/series'
import {Status} from 'src/types'
import {getDeep} from 'src/utils/wrappers'
export const handleLoading = (query, editQueryStatus) => {
interface Query {
host: string | string[]
text: string
id: string
database?: string
db?: string
rp?: string
}
interface Payload {
source: string
query: Query
tempVars: any[]
db?: string
rp?: string
resolution?: number
}
type EditQueryStatusFunction = (queryID: string, status: Status) => void
const handleLoading = (
query: Query,
editQueryStatus: EditQueryStatusFunction
): void =>
editQueryStatus(query.id, {
loading: true,
})
}
// {results: [{}]}
export const handleSuccess = (data, query, editQueryStatus) => {
const handleSuccess = (
data: TimeSeriesResponse,
query: Query,
editQueryStatus: EditQueryStatusFunction
): TimeSeriesResponse => {
const {results} = data
const error = _.get(results, ['0', 'error'], false)
const series = _.get(results, ['0', 'series'], false)
const error = getDeep<string>(results, '0.error', null)
const series = getDeep<TimeSeriesSeries>(results, '0.series', null)
// 200 from server and no results = warn
if (!series && !error) {
editQueryStatus(query.id, {
@ -38,12 +65,14 @@ export const handleSuccess = (data, query, editQueryStatus) => {
return data
}
export const handleError = (error, query, editQueryStatus) => {
const message = _.get(
const handleError = (
error,
['data', 'message'],
error.message || 'Could not retrieve data'
)
query: Query,
editQueryStatus: EditQueryStatusFunction
): void => {
const message =
getDeep<string>(error, 'data.message', '') ||
getDeep<string>(error, 'message', 'Could not retrieve data')
// 400 from chrono server = fail
editQueryStatus(query.id, {
@ -51,28 +80,10 @@ export const handleError = (error, query, editQueryStatus) => {
})
}
interface Query {
host: string | string[]
text: string
id: string
database?: string
db?: string
rp?: string
}
interface Payload {
source: string
query: Query
tempVars: any[]
db?: string
rp?: string
resolution?: number
}
export const fetchTimeSeriesAsync = async (
{source, db, rp, query, tempVars, resolution}: Payload,
editQueryStatus = noop
) => {
editQueryStatus: EditQueryStatusFunction = noop
): Promise<TimeSeriesResponse> => {
handleLoading(query, editQueryStatus)
try {
const {data} = await proxy({

View File

@ -178,9 +178,11 @@ class Dygraph extends Component<Props, State> {
}
public componentWillUnmount() {
if (this.dygraph) {
this.dygraph.destroy()
delete this.dygraph
}
}
public shouldComponentUpdate(nextProps: Props, nextState: State) {
const arePropsEqual = _.isEqual(this.props, nextProps)

View File

@ -48,7 +48,7 @@ interface TabListProps {
activeIndex?: number
onActivate?: (index: number) => void
isKapacitorTabs?: string
customClass: string
customClass?: string
}
export const TabList: SFC<TabListProps> = ({
@ -97,7 +97,7 @@ TabList.defaultProps = {
interface TabPanelsProps {
children: JSX.Element[] | JSX.Element
activeIndex?: number
customClass: string
customClass?: string
}
export const TabPanels: SFC<TabPanelsProps> = ({
@ -120,7 +120,7 @@ export const TabPanel: SFC<TabPanelProps> = ({children}) => (
interface TabsProps {
children: JSX.Element[] | JSX.Element
onSelect?: (activeIndex: number) => void
tabContentsClass: string
tabContentsClass?: string
tabsClass?: string
initialIndex?: number
}

View File

@ -1,22 +1,29 @@
// All copy for notifications should be stored here for easy editing
// and ensuring stylistic consistency
import {Notification} from 'src/types'
import {TemplateUpdate} from 'src/types/tempVars'
type NotificationExcludingMessage = Pick<
Notification,
Exclude<keyof Notification, 'message'>
>
import {FIVE_SECONDS, TEN_SECONDS, INFINITE} from 'src/shared/constants/index'
import {MAX_RESPONSE_BYTES} from 'src/flux/constants'
const defaultErrorNotification = {
const defaultErrorNotification: NotificationExcludingMessage = {
type: 'error',
icon: 'alert-triangle',
duration: TEN_SECONDS,
}
const defaultSuccessNotification = {
const defaultSuccessNotification: NotificationExcludingMessage = {
type: 'success',
icon: 'checkmark',
duration: FIVE_SECONDS,
}
const defaultDeletionNotification = {
const defaultDeletionNotification: NotificationExcludingMessage = {
type: 'primary',
icon: 'trash',
duration: FIVE_SECONDS,
@ -24,211 +31,240 @@ const defaultDeletionNotification = {
// Misc Notifications
// ----------------------------------------------------------------------------
export const notifyGenericFail = () => 'Could not communicate with server.'
export const notifyGenericFail = (): string =>
'Could not communicate with server.'
export const notifyNewVersion = version => ({
export const notifyNewVersion = (version: string): Notification => ({
type: 'info',
icon: 'cubo-uniform',
duration: INFINITE,
message: `Welcome to the latest Chronograf${version}. Local settings cleared.`,
})
export const notifyLoadLocalSettingsFailed = error => ({
export const notifyLoadLocalSettingsFailed = (error: string): Notification => ({
...defaultErrorNotification,
message: `Loading local settings failed: ${error}`,
})
export const notifyErrorWithAltText = (type, message) => ({
export const notifyErrorWithAltText = (
type: string,
message: string
): Notification => ({
type,
icon: 'triangle',
duration: TEN_SECONDS,
message,
})
export const notifyPresentationMode = () => ({
export const notifyPresentationMode = (): Notification => ({
type: 'primary',
icon: 'expand-b',
duration: 7500,
message: 'Press ESC to exit Presentation Mode.',
})
export const notifyDataWritten = () => ({
export const notifyDataWritten = (): Notification => ({
...defaultSuccessNotification,
message: 'Data was written successfully.',
})
export const notifyDataWriteFailed = errorMessage => ({
export const notifyDataWriteFailed = (errorMessage: string): Notification => ({
...defaultErrorNotification,
message: `Data write failed: ${errorMessage}`,
})
export const notifySessionTimedOut = () => ({
export const notifySessionTimedOut = (): Notification => ({
type: 'primary',
icon: 'triangle',
duration: INFINITE,
message: 'Your session has timed out. Log in again to continue.',
})
export const notifyServerError = {
export const notifyServerError: Notification = {
...defaultErrorNotification,
message: 'Internal Server Error. Check API Logs.',
}
export const notifyCouldNotRetrieveKapacitors = sourceID => ({
export const notifyCouldNotRetrieveKapacitors = (
sourceID: string
): Notification => ({
...defaultErrorNotification,
message: `Internal Server Error. Could not retrieve Kapacitor Connections for source ${sourceID}.`,
})
export const notifyCouldNotRetrieveKapacitorServices = kapacitor => ({
export const notifyCouldNotRetrieveKapacitorServices = (
kapacitor: string
): Notification => ({
...defaultErrorNotification,
message: `Interanl Server Error. Could not retrieve services for Kapacitor ${kapacitor}`,
message: `Internal Server Error. Could not retrieve services for Kapacitor ${kapacitor}`,
})
export const notifyCouldNotDeleteKapacitor = () => ({
export const notifyCouldNotDeleteKapacitor = (): Notification => ({
...defaultErrorNotification,
message: 'Internal Server Error. Could not delete Kapacitor Connection.',
})
export const notifyCSVDownloadFailed = () => ({
export const notifyCSVDownloadFailed = (): Notification => ({
...defaultErrorNotification,
message: 'Unable to download .CSV file',
})
// Hosts Page Notifications
// ----------------------------------------------------------------------------
export const notifyUnableToGetHosts = () => ({
export const notifyUnableToGetHosts = (): Notification => ({
...defaultErrorNotification,
message: 'Unable to get Hosts.',
})
export const notifyUnableToGetApps = () => ({
export const notifyUnableToGetApps = (): Notification => ({
...defaultErrorNotification,
message: 'Unable to get Apps for Hosts.',
})
// InfluxDB Sources Notifications
// ----------------------------------------------------------------------------
export const notifySourceCreationSucceeded = sourceName => ({
export const notifySourceCreationSucceeded = (
sourceName: string
): Notification => ({
...defaultSuccessNotification,
icon: 'server2',
message: `Connected to InfluxDB ${sourceName} successfully.`,
})
export const notifySourceCreationFailed = (sourceName, errorMessage) => ({
export const notifySourceCreationFailed = (
sourceName: string,
errorMessage: string
): Notification => ({
...defaultErrorNotification,
icon: 'server2',
message: `Unable to connect to InfluxDB ${sourceName}: ${errorMessage}`,
})
export const notifySourceUdpated = sourceName => ({
export const notifySourceUdpated = (sourceName: string): Notification => ({
...defaultSuccessNotification,
icon: 'server2',
message: `Updated InfluxDB ${sourceName} Connection successfully.`,
})
export const notifySourceUdpateFailed = (sourceName, errorMessage) => ({
export const notifySourceUdpateFailed = (
sourceName: string,
errorMessage: string
): Notification => ({
...defaultErrorNotification,
icon: 'server2',
message: `Failed to update InfluxDB ${sourceName} Connection: ${errorMessage}`,
})
export const notifySourceDeleted = (sourceName: string) => ({
export const notifySourceDeleted = (sourceName: string): Notification => ({
...defaultSuccessNotification,
icon: 'server2',
message: `${sourceName} deleted successfully.`,
})
export const notifySourceDeleteFailed = sourceName => ({
export const notifySourceDeleteFailed = (sourceName: string): Notification => ({
...defaultErrorNotification,
icon: 'server2',
message: `There was a problem deleting ${sourceName}.`,
})
export const notifySourceNoLongerAvailable = sourceName =>
`Source ${sourceName} is no longer available. Please ensure InfluxDB is running.`
export const notifyNoSourcesAvailable = sourceName =>
`Unable to connect to source ${sourceName}. No other sources available.`
export const notifyUnableToRetrieveSources = () => 'Unable to retrieve sources.'
export const notifyUnableToConnectSource = sourceName =>
`Unable to connect to source ${sourceName}.`
export const notifyErrorConnectingToSource = errorMessage => ({
export const notifySourceNoLongerAvailable = (
sourceName: string
): Notification => ({
...defaultErrorNotification,
icon: 'server2',
message: `Source ${sourceName} is no longer available. Please ensure InfluxDB is running.`,
})
export const notifyErrorConnectingToSource = (
errorMessage: string
): Notification => ({
...defaultErrorNotification,
icon: 'server2',
message: `Unable to connect to InfluxDB source: ${errorMessage}`,
})
// Multitenancy User Notifications
// ----------------------------------------------------------------------------
export const notifyUserRemovedFromAllOrgs = () => ({
export const notifyUserRemovedFromAllOrgs = (): Notification => ({
...defaultErrorNotification,
duration: INFINITE,
message:
'You have been removed from all organizations. Please contact your administrator.',
})
export const notifyUserRemovedFromCurrentOrg = () => ({
export const notifyUserRemovedFromCurrentOrg = (): Notification => ({
...defaultErrorNotification,
duration: INFINITE,
message: 'You were removed from your current organization.',
})
export const notifyOrgHasNoSources = () => ({
export const notifyOrgHasNoSources = (): Notification => ({
...defaultErrorNotification,
duration: INFINITE,
message: 'Organization has no sources configured.',
})
export const notifyUserSwitchedOrgs = (orgName, roleName) => ({
export const notifyUserSwitchedOrgs = (
orgName: string,
roleName: string
): Notification => ({
...defaultSuccessNotification,
type: 'primary',
message: `Now logged in to '${orgName}' as '${roleName}'.`,
})
export const notifyOrgIsPrivate = () => ({
export const notifyOrgIsPrivate = (): Notification => ({
...defaultErrorNotification,
duration: INFINITE,
message:
'This organization is private. To gain access, you must be explicitly added by an administrator.',
})
export const notifyCurrentOrgDeleted = () => ({
export const notifyCurrentOrgDeleted = (): Notification => ({
...defaultErrorNotification,
duration: INFINITE,
message: 'Your current organization was deleted.',
})
export const notifyJSONFeedFailed = url => ({
export const notifyJSONFeedFailed = (url: string): Notification => ({
...defaultErrorNotification,
message: `Failed to fetch JSON Feed for News Feed from '${url}'`,
})
// Chronograf Admin Notifications
// ----------------------------------------------------------------------------
export const notifyMappingDeleted = (id, scheme) => ({
export const notifyMappingDeleted = (
id: string,
scheme: string
): Notification => ({
...defaultSuccessNotification,
message: `Mapping ${id}/${scheme} deleted successfully.`,
})
export const notifyChronografUserAddedToOrg = (user, organization) =>
`${user} has been added to ${organization} successfully.`
export const notifyChronografUserAddedToOrg = (
user: string,
organization: string
): string => `${user} has been added to ${organization} successfully.`
export const notifyChronografUserRemovedFromOrg = (user, organization) =>
`${user} has been removed from ${organization} successfully.`
export const notifyChronografUserRemovedFromOrg = (
user: string,
organization: string
): string => `${user} has been removed from ${organization} successfully.`
export const notifyChronografUserUpdated = message => ({
export const notifyChronografUserUpdated = (message: string): Notification => ({
...defaultSuccessNotification,
message,
})
export const notifyChronografOrgDeleted = orgName => ({
export const notifyChronografOrgDeleted = (orgName: string): Notification => ({
...defaultSuccessNotification,
message: `Organization ${orgName} deleted successfully.`,
})
export const notifyChronografUserDeleted = (user, isAbsoluteDelete) => ({
export const notifyChronografUserDeleted = (
user: string,
isAbsoluteDelete: boolean
): Notification => ({
...defaultSuccessNotification,
message: `${user} has been removed from ${
isAbsoluteDelete
@ -237,7 +273,7 @@ export const notifyChronografUserDeleted = (user, isAbsoluteDelete) => ({
}`,
})
export const notifyChronografUserMissingNameAndProvider = () => ({
export const notifyChronografUserMissingNameAndProvider = (): Notification => ({
...defaultErrorNotification,
type: 'warning',
message: 'User must have a Name and Provider.',
@ -245,220 +281,238 @@ export const notifyChronografUserMissingNameAndProvider = () => ({
// InfluxDB Admin Notifications
// ----------------------------------------------------------------------------
export const notifyDBUserCreated = () => ({
export const notifyDBUserCreated = (): Notification => ({
...defaultSuccessNotification,
message: 'User created successfully.',
})
export const notifyDBUserCreationFailed = errorMessage =>
export const notifyDBUserCreationFailed = (errorMessage: string): string =>
`Failed to create User: ${errorMessage}`
export const notifyDBUserDeleted = userName => ({
export const notifyDBUserDeleted = (userName: string): Notification => ({
...defaultSuccessNotification,
message: `User "${userName}" deleted successfully.`,
})
export const notifyDBUserDeleteFailed = errorMessage =>
export const notifyDBUserDeleteFailed = (errorMessage: string): string =>
`Failed to delete User: ${errorMessage}`
export const notifyDBUserPermissionsUpdated = () => ({
export const notifyDBUserPermissionsUpdated = (): Notification => ({
...defaultSuccessNotification,
message: 'User Permissions updated successfully.',
})
export const notifyDBUserPermissionsUpdateFailed = errorMessage =>
`Failed to update User Permissions: ${errorMessage}`
export const notifyDBUserPermissionsUpdateFailed = (
errorMessage: string
): string => `Failed to update User Permissions: ${errorMessage}`
export const notifyDBUserRolesUpdated = () => ({
export const notifyDBUserRolesUpdated = (): Notification => ({
...defaultSuccessNotification,
message: 'User Roles updated successfully.',
})
export const notifyDBUserRolesUpdateFailed = errorMessage =>
export const notifyDBUserRolesUpdateFailed = (errorMessage: string): string =>
`Failed to update User Roles: ${errorMessage}`
export const notifyDBUserPasswordUpdated = () => ({
export const notifyDBUserPasswordUpdated = (): Notification => ({
...defaultSuccessNotification,
message: 'User Password updated successfully.',
})
export const notifyDBUserPasswordUpdateFailed = errorMessage =>
`Failed to update User Password: ${errorMessage}`
export const notifyDBUserPasswordUpdateFailed = (
errorMessage: string
): string => `Failed to update User Password: ${errorMessage}`
export const notifyDatabaseCreated = () => ({
export const notifyDatabaseCreated = (): Notification => ({
...defaultSuccessNotification,
message: 'Database created successfully.',
})
export const notifyDBCreationFailed = errorMessage =>
export const notifyDBCreationFailed = (errorMessage: string): string =>
`Failed to create Database: ${errorMessage}`
export const notifyDBDeleted = databaseName => ({
export const notifyDBDeleted = (databaseName: string): Notification => ({
...defaultSuccessNotification,
message: `Database "${databaseName}" deleted successfully.`,
})
export const notifyDBDeleteFailed = errorMessage =>
export const notifyDBDeleteFailed = (errorMessage: string): string =>
`Failed to delete Database: ${errorMessage}`
export const notifyRoleCreated = () => ({
export const notifyRoleCreated = (): Notification => ({
...defaultSuccessNotification,
message: 'Role created successfully.',
})
export const notifyRoleCreationFailed = errorMessage =>
export const notifyRoleCreationFailed = (errorMessage: string): string =>
`Failed to create Role: ${errorMessage}`
export const notifyRoleDeleted = roleName => ({
export const notifyRoleDeleted = (roleName: string): Notification => ({
...defaultSuccessNotification,
message: `Role "${roleName}" deleted successfully.`,
})
export const notifyRoleDeleteFailed = errorMessage =>
export const notifyRoleDeleteFailed = (errorMessage: string): string =>
`Failed to delete Role: ${errorMessage}`
export const notifyRoleUsersUpdated = () => ({
export const notifyRoleUsersUpdated = (): Notification => ({
...defaultSuccessNotification,
message: 'Role Users updated successfully.',
})
export const notifyRoleUsersUpdateFailed = errorMessage =>
export const notifyRoleUsersUpdateFailed = (errorMessage: string): string =>
`Failed to update Role Users: ${errorMessage}`
export const notifyRolePermissionsUpdated = () => ({
export const notifyRolePermissionsUpdated = (): Notification => ({
...defaultSuccessNotification,
message: 'Role Permissions updated successfully.',
})
export const notifyRolePermissionsUpdateFailed = errorMessage =>
`Failed to update Role Permissions: ${errorMessage}`
export const notifyRolePermissionsUpdateFailed = (
errorMessage: string
): string => `Failed to update Role Permissions: ${errorMessage}`
export const notifyRetentionPolicyCreated = () => ({
export const notifyRetentionPolicyCreated = (): Notification => ({
...defaultSuccessNotification,
message: 'Retention Policy created successfully.',
})
export const notifyRetentionPolicyCreationError = () => ({
export const notifyRetentionPolicyCreationError = (): Notification => ({
...defaultErrorNotification,
message: 'Failed to create Retention Policy. Please check name and duration.',
})
export const notifyRetentionPolicyCreationFailed = errorMessage =>
`Failed to create Retention Policy: ${errorMessage}`
export const notifyRetentionPolicyCreationFailed = (
errorMessage: string
): string => `Failed to create Retention Policy: ${errorMessage}`
export const notifyRetentionPolicyDeleted = rpName => ({
export const notifyRetentionPolicyDeleted = (rpName: string): Notification => ({
...defaultSuccessNotification,
message: `Retention Policy "${rpName}" deleted successfully.`,
})
export const notifyRetentionPolicyDeleteFailed = errorMessage =>
`Failed to delete Retention Policy: ${errorMessage}`
export const notifyRetentionPolicyDeleteFailed = (
errorMessage: string
): string => `Failed to delete Retention Policy: ${errorMessage}`
export const notifyRetentionPolicyUpdated = () => ({
export const notifyRetentionPolicyUpdated = (): Notification => ({
...defaultSuccessNotification,
message: 'Retention Policy updated successfully.',
})
export const notifyRetentionPolicyUpdateFailed = errorMessage =>
`Failed to update Retention Policy: ${errorMessage}`
export const notifyRetentionPolicyUpdateFailed = (
errorMessage: string
): string => `Failed to update Retention Policy: ${errorMessage}`
export const notifyQueriesError = errorMessage => ({
export const notifyQueriesError = (errorMessage: string): Notification => ({
...defaultErrorNotification,
errorMessage,
message: errorMessage,
})
export const notifyRetentionPolicyCantHaveEmptyFields = () => ({
export const notifyRetentionPolicyCantHaveEmptyFields = (): Notification => ({
...defaultErrorNotification,
message: 'Fields cannot be empty.',
})
export const notifyDatabaseDeleteConfirmationRequired = databaseName => ({
export const notifyDatabaseDeleteConfirmationRequired = (
databaseName: string
): Notification => ({
...defaultErrorNotification,
message: `Type "DELETE ${databaseName}" to confirm. This action cannot be undone.`,
})
export const notifyDBUserNamePasswordInvalid = () => ({
export const notifyDBUserNamePasswordInvalid = (): Notification => ({
...defaultErrorNotification,
message: 'Username and/or Password too short.',
})
export const notifyRoleNameInvalid = () => ({
export const notifyRoleNameInvalid = (): Notification => ({
...defaultErrorNotification,
message: 'Role name is too short.',
})
export const notifyDatabaseNameInvalid = () => ({
export const notifyDatabaseNameInvalid = (): Notification => ({
...defaultErrorNotification,
message: 'Database name cannot be blank.',
})
export const notifyDatabaseNameAlreadyExists = () => ({
export const notifyDatabaseNameAlreadyExists = (): Notification => ({
...defaultErrorNotification,
message: 'A Database by this name already exists.',
})
// Dashboard Notifications
// ----------------------------------------------------------------------------
export const notifyTempVarAlreadyExists = tempVarName => ({
export const notifyTempVarAlreadyExists = (
tempVarName: string
): Notification => ({
...defaultErrorNotification,
icon: 'cube',
message: `Variable '${tempVarName}' already exists. Please enter a new value.`,
})
export const notifyDashboardNotFound = dashboardID => ({
export const notifyDashboardNotFound = (dashboardID: number): Notification => ({
...defaultErrorNotification,
icon: 'dash-h',
message: `Dashboard ${dashboardID} could not be found`,
})
export const notifyDashboardDeleted = name => ({
export const notifyDashboardDeleted = (name: string): Notification => ({
...defaultSuccessNotification,
icon: 'dash-h',
message: `Dashboard ${name} deleted successfully.`,
})
export const notifyDashboardExported = name => ({
export const notifyDashboardExported = (name: string): Notification => ({
...defaultSuccessNotification,
icon: 'dash-h',
message: `Dashboard ${name} exported successfully.`,
})
export const notifyDashboardExportFailed = (name, errorMessage) => ({
export const notifyDashboardExportFailed = (
name: string,
errorMessage: string
): Notification => ({
...defaultErrorNotification,
duration: INFINITE,
message: `Failed to export Dashboard ${name}: ${errorMessage}.`,
})
export const notifyDashboardImported = name => ({
export const notifyDashboardImported = (name: string): Notification => ({
...defaultSuccessNotification,
icon: 'dash-h',
message: `Dashboard ${name} imported successfully.`,
})
export const notifyDashboardImportFailed = (fileName, errorMessage) => ({
export const notifyDashboardImportFailed = (
fileName: string,
errorMessage: string
): Notification => ({
...defaultErrorNotification,
duration: INFINITE,
message: `Failed to import Dashboard from file ${fileName}: ${errorMessage}.`,
})
export const notifyDashboardDeleteFailed = (name, errorMessage) =>
`Failed to delete Dashboard ${name}: ${errorMessage}.`
export const notifyDashboardDeleteFailed = (
name: string,
errorMessage: string
): string => `Failed to delete Dashboard ${name}: ${errorMessage}.`
export const notifyCellAdded = name => ({
export const notifyCellAdded = (name: string): Notification => ({
...defaultSuccessNotification,
icon: 'dash-h',
duration: 1900,
message: `Added "${name}" to dashboard.`,
})
export const notifyCellDeleted = name => ({
export const notifyCellDeleted = (name: string): Notification => ({
...defaultDeletionNotification,
icon: 'dash-h',
duration: 1900,
message: `Deleted "${name}" from dashboard.`,
})
export const notifyBuilderDisabled = () => ({
export const notifyBuilderDisabled = (): Notification => ({
type: 'info',
icon: 'graphline',
duration: 7500,
@ -467,249 +521,276 @@ export const notifyBuilderDisabled = () => ({
// Template Variables & URL Queries
// ----------------------------------------------------------------------------
export const notifyInvalidTempVarValueInURLQuery = ({key, value}) => ({
export const notifyInvalidTempVarValueInURLQuery = ({
key,
value,
}: TemplateUpdate): Notification => ({
...defaultErrorNotification,
icon: 'cube',
message: `Invalid URL query value of '${value}' supplied for template variable '${key}'.`,
})
export const notifyInvalidTimeRangeValueInURLQuery = () => ({
export const notifyInvalidTimeRangeValueInURLQuery = (): Notification => ({
...defaultErrorNotification,
icon: 'cube',
message: `Invalid URL query value supplied for lower or upper time range.`,
})
export const notifyInvalidZoomedTimeRangeValueInURLQuery = () => ({
export const notifyInvalidZoomedTimeRangeValueInURLQuery = (): Notification => ({
...defaultErrorNotification,
icon: 'cube',
message: `Invalid URL query value supplied for zoomed lower or zoomed upper time range.`,
})
export const notifyViewerUnauthorizedToSetTempVars = () => ({
export const notifyViewerUnauthorizedToSetTempVars = (): Notification => ({
...defaultErrorNotification,
message: `Viewer role unauthorized to override template variable values from URL.`,
})
// Rule Builder Notifications
// ----------------------------------------------------------------------------
export const notifyAlertRuleCreated = ruleName => ({
export const notifyAlertRuleCreated = (ruleName: string): Notification => ({
...defaultSuccessNotification,
message: `${ruleName} created successfully.`,
})
export const notifyAlertRuleCreateFailed = (ruleName, errorMessage) => ({
export const notifyAlertRuleCreateFailed = (
ruleName: string,
errorMessage: string
): Notification => ({
...defaultErrorNotification,
message: `There was a problem creating ${ruleName}: ${errorMessage}`,
})
export const notifyAlertRuleUpdated = ruleName => ({
export const notifyAlertRuleUpdated = (ruleName: string): Notification => ({
...defaultSuccessNotification,
message: `${ruleName} saved successfully.`,
})
export const notifyAlertRuleUpdateFailed = (ruleName, errorMessage) => ({
export const notifyAlertRuleUpdateFailed = (
ruleName: string,
errorMessage: string
): Notification => ({
...defaultErrorNotification,
message: `There was a problem saving ${ruleName}: ${errorMessage}`,
})
export const notifyAlertRuleDeleted = ruleName => ({
export const notifyAlertRuleDeleted = (ruleName: string): Notification => ({
...defaultSuccessNotification,
message: `${ruleName} deleted successfully.`,
})
export const notifyAlertRuleDeleteFailed = ruleName => ({
export const notifyAlertRuleDeleteFailed = (
ruleName: string
): Notification => ({
...defaultErrorNotification,
message: `${ruleName} could not be deleted.`,
})
export const notifyAlertRuleStatusUpdated = (ruleName, updatedStatus) => ({
export const notifyAlertRuleStatusUpdated = (
ruleName: string,
updatedStatus: string
): Notification => ({
...defaultSuccessNotification,
message: `${ruleName} ${updatedStatus} successfully.`,
})
export const notifyAlertRuleStatusUpdateFailed = (ruleName, updatedStatus) => ({
export const notifyAlertRuleStatusUpdateFailed = (
ruleName: string,
updatedStatus: string
): Notification => ({
...defaultSuccessNotification,
message: `${ruleName} could not be ${updatedStatus}.`,
})
export const notifyAlertRuleRequiresQuery = () =>
export const notifyAlertRuleRequiresQuery = (): string =>
'Please select a Database, Measurement, and Field.'
export const notifyAlertRuleRequiresConditionValue = () =>
export const notifyAlertRuleRequiresConditionValue = (): string =>
'Please enter a value in the Conditions section.'
export const notifyAlertRuleDeadmanInvalid = () =>
export const notifyAlertRuleDeadmanInvalid = (): string =>
'Deadman rules require a Database and Measurement.'
// Kapacitor Configuration Notifications
// ----------------------------------------------------------------------------
export const notifyKapacitorNameAlreadyTaken = kapacitorName => ({
export const notifyKapacitorNameAlreadyTaken = (
kapacitorName: string
): Notification => ({
...defaultErrorNotification,
message: `There is already a Kapacitor Connection named "${kapacitorName}".`,
})
export const notifyCouldNotFindKapacitor = () => ({
export const notifyCouldNotFindKapacitor = (): Notification => ({
...defaultErrorNotification,
message: 'We could not find a Kapacitor configuration for this source.',
})
export const notifyRefreshKapacitorFailed = () => ({
export const notifyRefreshKapacitorFailed = (): Notification => ({
...defaultErrorNotification,
message: 'There was an error getting the Kapacitor configuration.',
})
export const notifyAlertEndpointSaved = endpoint => ({
export const notifyAlertEndpointSaved = (endpoint: string): Notification => ({
...defaultSuccessNotification,
message: `Alert configuration for ${endpoint} saved successfully.`,
})
export const notifyAlertEndpointSaveFailed = (endpoint, errorMessage) => ({
export const notifyAlertEndpointSaveFailed = (
endpoint: string,
errorMessage: string
): Notification => ({
...defaultErrorNotification,
message: `There was an error saving the alert configuration for ${endpoint}: ${errorMessage}`,
})
export const notifyAlertEndpointDeleteFailed = (
endpoint,
config,
errorMessage
) => ({
endpoint: string,
config: string,
errorMessage: string
): Notification => ({
...defaultErrorNotification,
message: `There was an error deleting the alert configuration for ${endpoint}/${config}: ${errorMessage}`,
})
export const notifyAlertEndpointDeleted = (endpoint, config) => ({
export const notifyAlertEndpointDeleted = (
endpoint: string,
config: string
): Notification => ({
...defaultSuccessNotification,
message: `Alert configuration for ${endpoint}/${config} deleted successfully.`,
})
export const notifyTestAlertSent = endpoint => ({
export const notifyTestAlertSent = (endpoint: string): Notification => ({
...defaultSuccessNotification,
duration: TEN_SECONDS,
message: `Test Alert sent to ${endpoint}. If the Alert does not reach its destination, please check your endpoint configuration settings.`,
})
export const notifyTestAlertFailed = (endpoint, errorMessage?) => ({
export const notifyTestAlertFailed = (
endpoint: string,
errorMessage: string = ''
): Notification => ({
...defaultErrorNotification,
message: `There was an error sending a Test Alert to ${endpoint}${
errorMessage ? `: ${errorMessage}` : '.'
}`,
message: `There was an error sending a Test Alert to ${endpoint} ${errorMessage}.`,
})
export const notifyInvalidBatchSizeValue = () => ({
export const notifyInvalidBatchSizeValue = (): Notification => ({
...defaultErrorNotification,
message: 'Batch Size cannot be empty.',
})
export const notifyKapacitorConnectionFailed = () => ({
export const notifyKapacitorConnectionFailed = (): Notification => ({
...defaultErrorNotification,
message:
'Could not connect to Kapacitor. Check your connection settings in the Configuration page.',
})
export const notifyKapacitorCreated = () => ({
export const notifyKapacitorCreated = (): Notification => ({
...defaultSuccessNotification,
message:
'Connected to Kapacitor successfully! Configuring endpoints is optional.',
})
export const notifyKapacitorCreateFailed = () => ({
export const notifyKapacitorCreateFailed = (): Notification => ({
...defaultErrorNotification,
message: 'There was a problem connecting to Kapacitor.',
})
export const notifyKapacitorUpdated = () => ({
export const notifyKapacitorUpdated = (): Notification => ({
...defaultSuccessNotification,
message: 'Kapacitor Connection updated successfully.',
})
export const notifyKapacitorUpdateFailed = () => ({
export const notifyKapacitorUpdateFailed = (): Notification => ({
...defaultErrorNotification,
message: 'There was a problem updating the Kapacitor Connection.',
})
// TICKscript Notifications
// ----------------------------------------------------------------------------
export const notifyTickScriptCreated = () => ({
export const notifyTickScriptCreated = (): Notification => ({
...defaultSuccessNotification,
message: 'TICKscript successfully created.',
})
export const notifyTickscriptCreationFailed = () =>
export const notifyTickscriptCreationFailed = (): string =>
'Failed to create TICKscript.'
export const notifyTickscriptUpdated = () => ({
export const notifyTickscriptUpdated = (): Notification => ({
...defaultSuccessNotification,
message: 'TICKscript successfully updated.',
})
export const notifyTickscriptUpdateFailed = () => 'Failed to update TICKscript.'
export const notifyTickscriptUpdateFailed = (): string =>
'Failed to update TICKscript.'
export const notifyTickscriptLoggingUnavailable = () => ({
export const notifyTickscriptLoggingUnavailable = (): Notification => ({
type: 'warning',
icon: 'alert-triangle',
duration: INFINITE,
message: 'Kapacitor version 1.4 required to view TICKscript logs',
})
export const notifyTickscriptLoggingError = () => ({
export const notifyTickscriptLoggingError = (): Notification => ({
...defaultErrorNotification,
message: 'Could not collect kapacitor logs',
})
export const notifyKapacitorNotFound = () => ({
export const notifyKapacitorNotFound = (): Notification => ({
...defaultErrorNotification,
message: 'We could not find a Kapacitor configuration for this source.',
})
// Flux notifications
export const validateSuccess = () => ({
export const validateSuccess = (): Notification => ({
...defaultSuccessNotification,
message: 'No errors found. Happy Happy Joy Joy!',
})
export const notifyCopyToClipboardSuccess = text => ({
export const notifyCopyToClipboardSuccess = (text: string): Notification => ({
...defaultSuccessNotification,
icon: 'dash-h',
message: `'${text}' has been copied to clipboard.`,
})
export const notifyCopyToClipboardFailed = text => ({
export const notifyCopyToClipboardFailed = (text: string): Notification => ({
...defaultErrorNotification,
message: `'${text}' was not copied to clipboard.`,
})
// Service notifications
export const couldNotGetServices = {
export const couldNotGetServices: Notification = {
...defaultErrorNotification,
message: 'We could not get services',
}
export const fluxCreated = {
export const fluxCreated: Notification = {
...defaultSuccessNotification,
message: 'Flux Connection Created. Script your heart out!',
}
export const fluxNotCreated = (message: string) => ({
export const fluxNotCreated = (message: string): Notification => ({
...defaultErrorNotification,
message,
})
export const fluxNotUpdated = (message: string) => ({
export const fluxNotUpdated = (message: string): Notification => ({
...defaultErrorNotification,
message,
})
export const fluxUpdated = {
export const fluxUpdated: Notification = {
...defaultSuccessNotification,
message: 'Connection Updated. Rejoice!',
}
export const fluxTimeSeriesError = (message: string) => ({
export const fluxTimeSeriesError = (message: string): Notification => ({
...defaultErrorNotification,
message: `Could not get data: ${message}`,
})
export const fluxResponseTruncatedError = () => {
export const fluxResponseTruncatedError = (): Notification => {
const BYTES_TO_MB = 1 / 1e6
const APPROX_MAX_RESPONSE_MB = +(MAX_RESPONSE_BYTES * BYTES_TO_MB).toFixed(2)

View File

@ -6,6 +6,8 @@ interface TimeRangeOption extends TimeRange {
menuOption: string
}
const nowMinus30d = 'now() - 30d'
export const timeRanges: TimeRangeOption[] = [
{
defaultGroupBy: '10s',
@ -75,7 +77,7 @@ export const timeRanges: TimeRangeOption[] = [
defaultGroupBy: '6h',
seconds: 2592000,
inputValue: 'Past 30d',
lower: 'now() - 30d',
lower: nowMinus30d,
upper: null,
menuOption: 'Past 30d',
},
@ -89,3 +91,7 @@ export const defaultTimeRange = {
seconds: 900,
format: FORMAT_INFLUXQL,
}
export const STATUS_PAGE_TIME_RANGE = timeRanges.find(
tr => tr.lower === nowMinus30d
)

View File

@ -0,0 +1,26 @@
import {getDeep} from 'src/utils/wrappers'
interface ParseShowSeriesResponse {
errors: string[]
series: string[]
}
const parseShowSeries = (response): ParseShowSeriesResponse => {
const results = response.results[0]
if (results.error) {
return {errors: [results.error], series: []}
}
const seriesValues = getDeep<string[]>(results, 'series.0.values', [])
if (!seriesValues.length) {
return {errors: [], series: []}
}
const series = seriesValues.map(s => s[0])
return {series, errors: []}
}
export default parseShowSeries

View File

@ -14,12 +14,12 @@ import {
notifySourceDeleteFailed,
} from 'src/shared/copy/notifications'
import {Source, NotificationFunc} from 'src/types'
import {Source, Notification} from 'src/types'
interface Props {
source: Source
sources: Source[]
notify: NotificationFunc
notify: (n: Notification) => void
deleteKapacitor: actions.DeleteKapacitorAsync
fetchKapacitors: actions.FetchKapacitorsAsync
removeAndLoadSources: actions.RemoveAndLoadSources

View File

@ -11,7 +11,7 @@ import {
} from 'src/shared/actions/sources'
import {
notify as notifyAction,
PubishNotification,
PublishNotification,
} from 'src/shared/actions/notifications'
import {connect} from 'react-redux'
@ -34,7 +34,7 @@ import {
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props extends WithRouterProps {
notify: PubishNotification
notify: PublishNotification
addSource: AddSource
updateSource: UpdateSource
}

View File

@ -1,49 +0,0 @@
// he is a library for safely encoding and decoding HTML Entities
import he from 'he'
import {fetchJSONFeed as fetchJSONFeedAJAX} from 'src/status/apis'
import {notify} from 'src/shared/actions/notifications'
import {notifyJSONFeedFailed} from 'src/shared/copy/notifications'
import * as actionTypes from 'src/status/constants/actionTypes'
const fetchJSONFeedRequested = () => ({
type: actionTypes.FETCH_JSON_FEED_REQUESTED,
})
const fetchJSONFeedCompleted = data => ({
type: actionTypes.FETCH_JSON_FEED_COMPLETED,
payload: {data},
})
const fetchJSONFeedFailed = () => ({
type: actionTypes.FETCH_JSON_FEED_FAILED,
})
export const fetchJSONFeedAsync = url => async dispatch => {
dispatch(fetchJSONFeedRequested())
try {
const {data} = await fetchJSONFeedAJAX(url)
// data could be from a webpage, and thus would be HTML
if (typeof data === 'string' || !data) {
dispatch(fetchJSONFeedFailed())
} else {
// decode HTML entities from response text
const decodedData = {
...data,
items: data.items.map(item => {
item.title = he.decode(item.title)
item.content_text = he.decode(item.content_text)
return item
}),
}
dispatch(fetchJSONFeedCompleted(decodedData))
}
} catch (error) {
console.error(error)
dispatch(fetchJSONFeedFailed())
dispatch(notify(notifyJSONFeedFailed(url)))
}
}

View File

@ -0,0 +1,78 @@
// he is a library for safely encoding and decoding HTML Entities
import he from 'he'
import {Dispatch} from 'redux'
import {fetchJSONFeed as fetchJSONFeedAJAX} from 'src/status/apis'
import {notify} from 'src/shared/actions/notifications'
import {notifyJSONFeedFailed} from 'src/shared/copy/notifications'
import {JSONFeedData} from 'src/types'
import {AxiosResponse} from 'axios'
export enum ActionTypes {
FETCH_JSON_FEED_REQUESTED = 'FETCH_JSON_FEED_REQUESTED',
FETCH_JSON_FEED_COMPLETED = 'FETCH_JSON_FEED_COMPLETED',
FETCH_JSON_FEED_FAILED = 'FETCH_JSON_FEED_FAILED',
}
interface FetchJSONFeedRequestedAction {
type: ActionTypes.FETCH_JSON_FEED_REQUESTED
}
interface FetchJSONFeedCompletedAction {
type: ActionTypes.FETCH_JSON_FEED_COMPLETED
payload: {data: JSONFeedData}
}
interface FetchJSONFeedFailedAction {
type: ActionTypes.FETCH_JSON_FEED_FAILED
}
export type Action =
| FetchJSONFeedRequestedAction
| FetchJSONFeedCompletedAction
| FetchJSONFeedFailedAction
const fetchJSONFeedRequested = (): FetchJSONFeedRequestedAction => ({
type: ActionTypes.FETCH_JSON_FEED_REQUESTED,
})
const fetchJSONFeedCompleted = (
data: JSONFeedData
): FetchJSONFeedCompletedAction => ({
type: ActionTypes.FETCH_JSON_FEED_COMPLETED,
payload: {data},
})
const fetchJSONFeedFailed = (): FetchJSONFeedFailedAction => ({
type: ActionTypes.FETCH_JSON_FEED_FAILED,
})
export const fetchJSONFeedAsync = (url: string) => async (
dispatch: Dispatch<Action>
): Promise<void> => {
dispatch(fetchJSONFeedRequested())
try {
const {data} = (await fetchJSONFeedAJAX(url)) as AxiosResponse<JSONFeedData>
// data could be from a webpage, and thus would be HTML
if (typeof data === 'string' || !data) {
dispatch(fetchJSONFeedFailed())
} else {
// decode HTML entities from response text
const decodedData = {
...data,
items: data.items.map(item => {
item.title = he.decode(item.title)
item.content_text = he.decode(item.content_text)
return item
}),
}
dispatch(fetchJSONFeedCompleted(decodedData))
}
} catch (error) {
console.error(error)
dispatch(fetchJSONFeedFailed())
dispatch(notify(notifyJSONFeedFailed(url)))
}
}

View File

@ -1,9 +1,10 @@
import AJAX from 'src/utils/ajax'
import {JSONFeedData} from 'src/types'
const excludeBasepath = true // don't prefix route of external link with basepath/
export const fetchJSONFeed = url =>
AJAX(
export const fetchJSONFeed = (url: string) =>
AJAX<JSONFeedData>(
{
method: 'GET',
url,

View File

@ -1,15 +1,11 @@
import React, {Component} from 'react'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import {ErrorHandling} from 'src/shared/decorators/errors'
@ErrorHandling
class GettingStarted extends Component {
constructor(props) {
super(props)
}
render() {
public render() {
return (
<FancyScrollbar className="getting-started--container">
<div className="getting-started">

View File

@ -1,9 +1,13 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
import {JSONFeedData} from 'src/types'
import moment from 'moment'
const JSONFeedReader = ({data}) =>
interface Props {
data: JSONFeedData
}
const JSONFeedReader: SFC<Props> = ({data}) =>
data && data.items ? (
<div className="newsfeed">
{data.items
@ -38,24 +42,4 @@ const JSONFeedReader = ({data}) =>
</div>
) : null
const {arrayOf, shape, string} = PropTypes
JSONFeedReader.propTypes = {
data: shape({
items: arrayOf(
shape({
author: shape({
name: string.isRequired,
}).isRequired,
content_text: string.isRequired,
date_published: string.isRequired,
id: string.isRequired,
image: string,
title: string.isRequired,
url: string.isRequired,
})
),
}).isRequired,
}
export default JSONFeedReader

View File

@ -1,23 +1,26 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import {fetchJSONFeedAsync} from 'src/status/actions'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import JSONFeedReader from 'src/status/components/JSONFeedReader'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {JSONFeedData} from 'src/types'
interface Props {
hasCompletedFetchOnce: boolean
isFetching: boolean
isFailed: boolean
data: JSONFeedData
fetchJSONFeed: (statusFeedURL: string) => void
statusFeedURL: string
}
@ErrorHandling
class NewsFeed extends Component {
constructor(props) {
super(props)
}
// TODO: implement shouldComponentUpdate based on fetching conditions
render() {
class NewsFeed extends Component<Props> {
public render() {
const {hasCompletedFetchOnce, isFetching, isFailed, data} = this.props
if (!hasCompletedFetchOnce) {
@ -54,25 +57,13 @@ class NewsFeed extends Component {
}
// TODO: implement interval polling a la AutoRefresh
componentDidMount() {
public componentDidMount() {
const {statusFeedURL, fetchJSONFeed} = this.props
fetchJSONFeed(statusFeedURL)
}
}
const {bool, func, shape, string} = PropTypes
NewsFeed.propTypes = {
hasCompletedFetchOnce: bool.isRequired,
isFetching: bool.isRequired,
isFailed: bool.isRequired,
data: shape(),
fetchJSONFeed: func.isRequired,
statusFeedURL: string,
}
const mapStateToProps = ({
const mstp = ({
links: {
external: {statusFeed: statusFeedURL},
},
@ -85,8 +76,8 @@ const mapStateToProps = ({
statusFeedURL,
})
const mapDispatchToProps = dispatch => ({
fetchJSONFeed: bindActionCreators(fetchJSONFeedAsync, dispatch),
})
const mdtp = {
fetchJSONFeed: fetchJSONFeedAsync,
}
export default connect(mapStateToProps, mapDispatchToProps)(NewsFeed)
export default connect(mstp, mdtp)(NewsFeed)

View File

@ -1,6 +0,0 @@
export const SET_STATUS_PAGE_AUTOREFRESH = 'SET_STATUS_PAGE_AUTOREFRESH'
export const SET_STATUS_PAGE_TIME_RANGE = 'SET_STATUS_PAGE_TIME_RANGE'
export const FETCH_JSON_FEED_REQUESTED = 'FETCH_JSON_FEED_REQUESTED'
export const FETCH_JSON_FEED_COMPLETED = 'FETCH_JSON_FEED_COMPLETED'
export const FETCH_JSON_FEED_FAILED = 'FETCH_JSON_FEED_FAILED'

View File

@ -1,11 +1,10 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import SourceIndicator from 'shared/components/SourceIndicator'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import LayoutRenderer from 'shared/components/LayoutRenderer'
import PageHeader from 'shared/components/PageHeader'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import LayoutRenderer from 'src/shared/components/LayoutRenderer'
import {STATUS_PAGE_TIME_RANGE} from 'src/shared/data/timeRanges'
import {AUTOREFRESH_DEFAULT} from 'src/shared/constants'
import PageHeader from 'src/shared/components/PageHeader'
import {fixtureStatusPageCells} from 'src/status/fixtures'
import {ErrorHandling} from 'src/shared/decorators/errors'
@ -13,10 +12,22 @@ import {
TEMP_VAR_DASHBOARD_TIME,
TEMP_VAR_UPPER_DASHBOARD_TIME,
} from 'src/shared/constants'
import {Source, Cell} from 'src/types'
interface State {
cells: Cell[]
}
interface Props {
source: Source
}
const autoRefresh = AUTOREFRESH_DEFAULT
const timeRange = STATUS_PAGE_TIME_RANGE
@ErrorHandling
class StatusPage extends Component {
constructor(props) {
class StatusPage extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
@ -24,8 +35,8 @@ class StatusPage extends Component {
}
}
render() {
const {source, autoRefresh, timeRange} = this.props
public render() {
const {source} = this.props
const {cells} = this.state
const dashboardTime = {
@ -59,9 +70,9 @@ class StatusPage extends Component {
return (
<div className="page">
<PageHeader
title="Status"
titleText="Status"
fullWidth={true}
optionsComponents={this.optionsComponents()}
sourceIndicator={true}
/>
<FancyScrollbar className="page-contents">
<div className="dashboard container-fluid full-width">
@ -84,30 +95,6 @@ class StatusPage extends Component {
</div>
)
}
optionsComponents = () => {
return <SourceIndicator />
}
}
const {number, shape, string} = PropTypes
StatusPage.propTypes = {
source: shape({
name: string.isRequired,
links: shape({
proxy: string.isRequired,
}).isRequired,
}).isRequired,
autoRefresh: number.isRequired,
timeRange: shape({
lower: string.isRequired,
}).isRequired,
}
const mapStateToProps = ({statusUI: {autoRefresh, timeRange}}) => ({
autoRefresh,
timeRange,
})
export default connect(mapStateToProps, null)(StatusPage)
export default StatusPage

View File

@ -1,9 +1,38 @@
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
import {TEMP_VAR_DASHBOARD_TIME} from 'src/shared/constants'
import {NEW_DEFAULT_DASHBOARD_CELL} from 'src/dashboards/constants/index'
import {DEFAULT_AXIS} from 'src/dashboards/constants/cellEditor'
import {Cell, CellQuery, Axes} from 'src/types'
import {CellType} from 'src/types/dashboard'
export const fixtureStatusPageCells = [
const emptyQuery: CellQuery = {
query: '',
source: '',
queryConfig: {
database: '',
measurement: '',
retentionPolicy: '',
fields: [],
tags: {},
groupBy: {},
areTagsAccepted: false,
rawText: null,
range: null,
},
}
const emptyAxes: Axes = {
x: DEFAULT_AXIS,
y: DEFAULT_AXIS,
y2: DEFAULT_AXIS,
}
export const fixtureStatusPageCells: Cell[] = [
{
...NEW_DEFAULT_DASHBOARD_CELL,
axes: emptyAxes,
i: 'alerts-bar-graph',
type: CellType.Bar,
isWidget: false,
x: 0,
y: 0,
@ -15,7 +44,7 @@ export const fixtureStatusPageCells = [
queries: [
{
query: `SELECT count("value") AS "count_value" FROM "chronograf"."autogen"."alerts" WHERE time > ${TEMP_VAR_DASHBOARD_TIME} GROUP BY time(1d)`,
label: 'Events',
source: '',
queryConfig: {
database: 'chronograf',
measurement: 'alerts',
@ -44,90 +73,56 @@ export const fixtureStatusPageCells = [
},
},
],
type: 'bar',
links: {
self: '/chronograf/v1/status/23/cells/c-bar-graphs-fly',
},
},
{
...NEW_DEFAULT_DASHBOARD_CELL,
axes: emptyAxes,
i: 'recent-alerts',
type: CellType.Alerts,
isWidget: true,
name: 'Alerts Last 30 Days',
type: 'alerts',
x: 0,
y: 5,
w: 6.5,
h: 6,
legend: {},
queries: [
{
query: '',
queryConfig: {
database: '',
measurement: '',
retentionPolicy: '',
fields: [],
tags: {},
groupBy: {},
areTagsAccepted: false,
rawText: null,
range: null,
},
},
],
queries: [emptyQuery],
colors: DEFAULT_LINE_COLORS,
links: {self: ''},
},
{
...NEW_DEFAULT_DASHBOARD_CELL,
axes: emptyAxes,
i: 'news-feed',
type: CellType.News,
isWidget: true,
name: 'News Feed',
type: 'news',
x: 6.5,
y: 5,
w: 3,
h: 6,
legend: {},
queries: [
{
query: '',
queryConfig: {
database: '',
measurement: '',
retentionPolicy: '',
fields: [],
tags: {},
groupBy: {},
areTagsAccepted: false,
rawText: null,
range: null,
},
},
],
queries: [emptyQuery],
colors: DEFAULT_LINE_COLORS,
links: {self: ''},
},
{
...NEW_DEFAULT_DASHBOARD_CELL,
axes: emptyAxes,
i: 'getting-started',
type: CellType.Guide,
isWidget: true,
name: 'Getting Started',
type: 'guide',
x: 9.5,
y: 5,
w: 2.5,
h: 6,
legend: {},
queries: [
{
query: '',
queryConfig: {
database: '',
measurement: '',
retentionPolicy: '',
fields: [],
tags: {},
groupBy: {},
areTagsAccepted: false,
rawText: null,
range: null,
},
},
],
queries: [emptyQuery],
colors: DEFAULT_LINE_COLORS,
links: {self: ''},
},
]

View File

@ -1,19 +1,30 @@
import * as actionTypes from 'src/status/constants/actionTypes'
import {JSONFeedData} from 'src/types'
import {Action, ActionTypes} from 'src/status/actions'
const initialState = {
export interface JSONFeedReducerState {
hasCompletedFetchOnce: boolean
isFetching: boolean
isFailed: boolean
data: JSONFeedData
}
const initialState: JSONFeedReducerState = {
hasCompletedFetchOnce: false,
isFetching: false,
isFailed: false,
data: null,
}
const JSONFeedReducer = (state = initialState, action) => {
const JSONFeedReducer = (
state: JSONFeedReducerState = initialState,
action: Action
): JSONFeedReducerState => {
switch (action.type) {
case actionTypes.FETCH_JSON_FEED_REQUESTED: {
case ActionTypes.FETCH_JSON_FEED_REQUESTED: {
return {...state, isFetching: true, isFailed: false}
}
case actionTypes.FETCH_JSON_FEED_COMPLETED: {
case ActionTypes.FETCH_JSON_FEED_COMPLETED: {
const {data} = action.payload
return {
@ -25,7 +36,7 @@ const JSONFeedReducer = (state = initialState, action) => {
}
}
case actionTypes.FETCH_JSON_FEED_FAILED: {
case ActionTypes.FETCH_JSON_FEED_FAILED: {
return {
...state,
isFetching: false,

View File

@ -1,7 +0,0 @@
import statusUI from './ui'
import JSONFeed from './JSONFeed'
export default {
statusUI,
JSONFeed,
}

View File

@ -0,0 +1,4 @@
import JSONFeed from 'src/status/reducers/JSONFeed'
export default {
JSONFeed,
}

View File

@ -1,36 +0,0 @@
import {AUTOREFRESH_DEFAULT} from 'shared/constants'
import {timeRanges} from 'shared/data/timeRanges'
import * as actionTypes from 'src/status/constants/actionTypes'
const {lower, upper} = timeRanges.find(tr => tr.lower === 'now() - 30d')
const initialState = {
autoRefresh: AUTOREFRESH_DEFAULT,
timeRange: {lower, upper},
}
const statusUI = (state = initialState, action) => {
switch (action.type) {
case actionTypes.SET_STATUS_PAGE_AUTOREFRESH: {
const {milliseconds} = action.payload
return {
...state,
autoRefresh: milliseconds,
}
}
case actionTypes.SET_STATUS_PAGE_TIME_RANGE: {
const {timeRange} = action.payload
return {...state, timeRange}
}
default: {
return state
}
}
}
export default statusUI

View File

@ -86,7 +86,7 @@
position: absolute;
top: 50%;
right: 6px;
transform: translateY(-50%);
transform: translateY(-50%) rotate(180deg);
font-size: 13px;
opacity: 0;
transition: opacity 0.25s ease, color 0.25s ease, transform 0.25s ease;
@ -107,10 +107,10 @@
}
}
&__sort-asc:before {
transform: translateY(-50%) rotate(0deg);
transform: translateY(-50%) rotate(180deg);
}
&__sort-desc:before {
transform: translateY(-50%) rotate(180deg);
transform: translateY(-50%) rotate(0deg);
}
}

View File

@ -0,0 +1,169 @@
import React, {PureComponent, ChangeEvent} from 'react'
import _ from 'lodash'
import {proxy} from 'src/utils/queryUrlGenerator'
import {ErrorHandling} from 'src/shared/decorators/errors'
import TemplateMetaQueryPreview from 'src/tempVars/components/TemplateMetaQueryPreview'
import {parseMetaQuery, isInvalidMetaQuery} from 'src/tempVars/utils/parsing'
import {getDeep} from 'src/utils/wrappers'
import {
TemplateBuilderProps,
RemoteDataState,
TemplateValueType,
} from 'src/types'
const DEBOUNCE_DELAY = 750
interface State {
metaQueryInput: string // bound to input
metaQuery: string // debounced view of metaQueryInput
metaQueryResults: string[]
metaQueryResultsStatus: RemoteDataState
}
@ErrorHandling
class CustomMetaQueryTemplateBuilder extends PureComponent<
TemplateBuilderProps,
State
> {
private handleMetaQueryChange: () => void = _.debounce(() => {
const {metaQuery, metaQueryInput} = this.state
if (metaQuery === metaQueryInput) {
return
}
this.setState({metaQuery: metaQueryInput}, this.executeQuery)
}, DEBOUNCE_DELAY)
constructor(props: TemplateBuilderProps) {
super(props)
const metaQuery = getDeep<string>(props.template, 'query.influxql', '')
this.state = {
metaQuery,
metaQueryInput: metaQuery,
metaQueryResults: [],
metaQueryResultsStatus: RemoteDataState.NotStarted,
}
}
public componentDidMount() {
this.executeQuery()
}
public render() {
const {metaQueryInput} = this.state
return (
<div className="temp-builder csv-temp-builder">
<div className="form-group">
<label>Meta Query</label>
<div className="temp-builder--mq-controls">
<textarea
className="form-control"
value={metaQueryInput}
onChange={this.handleMetaQueryInputChange}
onBlur={this.handleMetaQueryChange}
/>
</div>
</div>
{this.renderResults()}
</div>
)
}
private renderResults() {
const {metaQueryResults, metaQueryResultsStatus} = this.state
if (this.showInvalidMetaQueryMessage) {
return (
<div className="temp-builder-results">
<p className="error">Meta Query is not valid.</p>
</div>
)
}
return (
<TemplateMetaQueryPreview
items={metaQueryResults}
loadingStatus={metaQueryResultsStatus}
/>
)
}
private get showInvalidMetaQueryMessage(): boolean {
const {metaQuery} = this.state
return this.isInvalidMetaQuery && metaQuery !== ''
}
private get isInvalidMetaQuery(): boolean {
const {metaQuery} = this.state
return isInvalidMetaQuery(metaQuery)
}
private handleMetaQueryInputChange = (
e: ChangeEvent<HTMLTextAreaElement>
) => {
this.setState({metaQueryInput: e.target.value})
this.handleMetaQueryChange()
}
private executeQuery = async (): Promise<void> => {
const {template, source, onUpdateTemplate} = this.props
const {metaQuery} = this.state
if (this.isInvalidMetaQuery) {
return
}
this.setState({metaQueryResultsStatus: RemoteDataState.Loading})
try {
const {data} = await proxy({
source: source.links.proxy,
query: metaQuery,
})
const metaQueryResults = parseMetaQuery(metaQuery, data)
this.setState({
metaQueryResults,
metaQueryResultsStatus: RemoteDataState.Done,
})
const nextValues = metaQueryResults.map(result => {
return {
type: TemplateValueType.MetaQuery,
value: result,
selected: false,
}
})
if (nextValues[0]) {
nextValues[0].selected = true
}
const nextTemplate = {
...template,
values: nextValues,
query: {
influxql: metaQuery,
},
}
onUpdateTemplate(nextTemplate)
} catch {
this.setState({
metaQueryResults: [],
metaQueryResultsStatus: RemoteDataState.Error,
})
}
}
}
export default CustomMetaQueryTemplateBuilder

View File

@ -18,6 +18,7 @@ import MeasurementsTemplateBuilder from 'src/tempVars/components/MeasurementsTem
import FieldKeysTemplateBuilder from 'src/tempVars/components/FieldKeysTemplateBuilder'
import TagKeysTemplateBuilder from 'src/tempVars/components/TagKeysTemplateBuilder'
import TagValuesTemplateBuilder from 'src/tempVars/components/TagValuesTemplateBuilder'
import MetaQueryTemplateBuilder from 'src/tempVars/components/MetaQueryTemplateBuilder'
import {
Template,
@ -59,6 +60,7 @@ const TEMPLATE_BUILDERS = {
[TemplateType.FieldKeys]: FieldKeysTemplateBuilder,
[TemplateType.TagKeys]: TagKeysTemplateBuilder,
[TemplateType.TagValues]: TagValuesTemplateBuilder,
[TemplateType.MetaQuery]: MetaQueryTemplateBuilder,
}
const formatName = name => `:${name.replace(/:/g, '')}:`
@ -100,7 +102,9 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
return (
<div className="edit-temp-var">
<div className="edit-temp-var--header">
<h1 className="page-header__title">Edit Template Variable</h1>
<h1 className="page-header__title">
{isNew ? 'Create' : 'Edit'} Template Variable
</h1>
<div className="edit-temp-var--header-controls">
<button
className="btn btn-default"
@ -115,7 +119,7 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
onClick={this.handleSave}
disabled={!this.canSave}
>
{this.isSaving ? 'Saving...' : 'Save'}
{this.saveButtonText}
</button>
</div>
</div>
@ -182,10 +186,10 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
private handleChooseType = ({type}) => {
const {
nextTemplate: {id},
nextTemplate: {id, tempVar},
} = this.state
const nextNextTemplate = {...DEFAULT_TEMPLATES[type](), id}
const nextNextTemplate = {...DEFAULT_TEMPLATES[type](), id, tempVar}
this.setState({nextTemplate: nextNextTemplate})
}
@ -204,7 +208,12 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
private formatName = (): void => {
const {nextTemplate} = this.state
const tempVar = formatName(nextTemplate.tempVar)
let tempVar = formatName(nextTemplate.tempVar)
if (tempVar === '::') {
tempVar = ''
}
this.setState({nextTemplate: {...nextTemplate, tempVar}})
}
@ -272,6 +281,24 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
)
}
private get saveButtonText(): string {
const {isNew} = this.state
if (this.isSaving && isNew) {
return 'Creating...'
}
if (this.isSaving && !isNew) {
return 'Saving...'
}
if (!this.isSaving && isNew) {
return 'Create'
}
return 'Save'
}
private handleDelete = (): void => {
const {onDelete} = this.props

View File

@ -34,6 +34,10 @@ export const TEMPLATE_TYPES_LIST: TemplateTypesListItem[] = [
text: 'CSV',
type: TemplateType.CSV,
},
{
text: 'Custom Meta Query',
type: TemplateType.MetaQuery,
},
]
export const TEMPLATE_VARIABLE_TYPES = {
@ -43,6 +47,7 @@ export const TEMPLATE_VARIABLE_TYPES = {
[TemplateType.FieldKeys]: TemplateValueType.FieldKey,
[TemplateType.TagKeys]: TemplateValueType.TagKey,
[TemplateType.TagValues]: TemplateValueType.TagValue,
[TemplateType.MetaQuery]: TemplateValueType.MetaQuery,
}
export const TEMPLATE_VARIABLE_QUERIES = {
@ -62,7 +67,7 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
[TemplateType.Databases]: () => {
return {
id: uuid.v4(),
tempVar: ':my-databases:',
tempVar: '',
values: [
{
value: '_internal',
@ -80,7 +85,7 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
[TemplateType.Measurements]: () => {
return {
id: uuid.v4(),
tempVar: ':my-measurements:',
tempVar: '',
values: [],
type: TemplateType.Measurements,
label: '',
@ -93,7 +98,7 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
[TemplateType.CSV]: () => {
return {
id: uuid.v4(),
tempVar: ':my-values:',
tempVar: '',
values: [],
type: TemplateType.CSV,
label: '',
@ -103,7 +108,7 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
[TemplateType.TagKeys]: () => {
return {
id: uuid.v4(),
tempVar: ':my-tag-keys:',
tempVar: '',
values: [],
type: TemplateType.TagKeys,
label: '',
@ -115,7 +120,7 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
[TemplateType.FieldKeys]: () => {
return {
id: uuid.v4(),
tempVar: ':my-field-keys:',
tempVar: '',
values: [],
type: TemplateType.FieldKeys,
label: '',
@ -127,7 +132,7 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
[TemplateType.TagValues]: () => {
return {
id: uuid.v4(),
tempVar: ':my-tag-values:',
tempVar: '',
values: [],
type: TemplateType.TagValues,
label: '',
@ -136,6 +141,18 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
},
}
},
[TemplateType.MetaQuery]: () => {
return {
id: uuid.v4(),
tempVar: ':my-meta-query:',
values: [],
type: TemplateType.MetaQuery,
label: '',
query: {
influxql: '',
},
}
},
}
export const RESERVED_TEMPLATE_NAMES = [

View File

@ -0,0 +1,84 @@
import parseShowDatabases from 'src/shared/parsing/showDatabases'
import parseShowFieldKeys from 'src/shared/parsing/showFieldKeys'
import parseShowTagKeys from 'src/shared/parsing/showTagKeys'
import parseShowTagValues from 'src/shared/parsing/showTagValues'
import parseShowMeasurements from 'src/shared/parsing/showMeasurements'
import parseShowSeries from 'src/shared/parsing/showSeries'
export const parseMetaQuery = (metaQuery: string, response): string[] => {
const metaQueryStart = getMetaQueryPrefix(metaQuery)
if (!metaQueryStart) {
throw new Error('Could not find parser for meta query')
}
const parser = PARSERS[metaQueryStart]
const extractor = EXTRACTORS[metaQueryStart]
const parsed = parser(response)
if (parsed.errors.length) {
throw new Error(parsed.errors)
}
return extractor(parsed)
}
export const isInvalidMetaQuery = (metaQuery: string): boolean =>
!getMetaQueryPrefix(metaQuery)
const getMetaQueryPrefix = (metaQuery: string): string | null => {
const words = metaQuery
.trim()
.toUpperCase()
.split(' ')
const firstTwoWords = words.slice(0, 2).join(' ')
const firstThreeWords = words.slice(0, 3).join(' ')
return VALID_META_QUERY_PREFIXES.find(
q => q === firstTwoWords || q === firstThreeWords
)
}
const VALID_META_QUERY_PREFIXES = [
'SHOW DATABASES',
'SHOW MEASUREMENTS',
'SHOW SERIES',
'SHOW TAG VALUES',
'SHOW FIELD KEYS',
'SHOW TAG KEYS',
]
const PARSERS = {
'SHOW DATABASES': parseShowDatabases,
'SHOW FIELD KEYS': parseShowFieldKeys,
'SHOW MEASUREMENTS': parseShowMeasurements,
'SHOW SERIES': parseShowSeries,
'SHOW TAG VALUES': parseShowTagValues,
'SHOW TAG KEYS': parseShowTagKeys,
}
const EXTRACTORS = {
'SHOW DATABASES': parsed => parsed.databases,
'SHOW FIELD KEYS': parsed => {
const {fieldSets} = parsed
const fieldSetsValues = Object.values(fieldSets) as string[]
return fieldSetsValues.reduce((acc, current) => [...acc, ...current], [])
},
'SHOW MEASUREMENTS': parsed => {
const {measurementSets} = parsed
return measurementSets.reduce(
(acc, current) => [...acc, ...current.measurements],
[]
)
},
'SHOW TAG KEYS': parsed => parsed.tagKeys,
'SHOW TAG VALUES': parsed => {
const {tags} = parsed
const tagsValues = Object.values(tags) as string[]
return tagsValues.reduce((acc, current) => [...acc, ...current], [])
},
'SHOW SERIES': parsed => parsed.series,
}

View File

@ -1,4 +1,8 @@
import * as kapacitorQueryConfigActions from 'src/kapacitor/actions/queryConfigs'
import * as kapacitorRuleActionCreators from 'src/kapacitor/actions/view'
type KapacitorQueryConfigActions = typeof kapacitorQueryConfigActions
type KapacitorRuleActions = typeof kapacitorRuleActionCreators
export {KapacitorQueryConfigActions}
export {KapacitorRuleActions}

View File

@ -3,12 +3,12 @@ import {ColorString} from 'src/types/colors'
import {Template} from 'src/types'
export interface Axis {
bounds: [string, string]
label: string
prefix: string
suffix: string
base: string
scale: string
bounds?: [string, string]
}
export type TimeSeriesValue = string | number | null | undefined
@ -76,6 +76,7 @@ export interface Cell {
decimalPlaces: DecimalPlaces
links: CellLinks
legend: Legend
isWidget?: boolean
}
export enum CellType {

View File

@ -44,6 +44,7 @@ import {
DygraphClass,
DygraphData,
} from './dygraphs'
import {JSONFeedData} from './status'
import {AnnotationInterface} from './annotations'
export {
@ -102,6 +103,7 @@ export {
SchemaFilter,
RemoteDataState,
URLQueryParams,
JSONFeedData,
AnnotationInterface,
TemplateType,
TemplateValueType,

View File

@ -32,6 +32,7 @@ export interface AlertRule {
error: string
created: string
modified: string
queryID?: string
'last-enabled'?: string
}
@ -186,7 +187,7 @@ interface OpsGenie {
}
// Talk sends alerts to Jane Talk (https://jianliao.com/site)
interface Talk {} // tslint:disable-line
interface Talk { } // tslint:disable-line
// TriggerValues specifies the alerting logic for a specific trigger type
interface TriggerValues {
@ -224,7 +225,7 @@ export interface RuleMessageTemplate {
time: RuleMessage
}
interface RuleMessage {
export interface RuleMessage {
label: string
text: string
}
@ -411,3 +412,20 @@ export interface RuleValues {
rangeValue?: string | null
operator?: string
}
export interface LogItem {
key: string
service: string
lvl: string
ts: string
msg: string
id: string
tags: string
method?: string
username?: string
host?: string
duration?: string
tag?: object
field?: object
cluster?: string
}

View File

@ -6,6 +6,6 @@ export interface Notification {
message: string
}
export type NotificationFunc = (message: any) => Notification
export type NotificationFunc = (message: string) => Notification
export type NotificationAction = (message: Notification) => void

View File

@ -77,7 +77,7 @@ export interface Namespace {
}
export interface Status {
loading?: string
loading?: boolean
error?: string
warn?: string
success?: string

22
ui/src/types/status.ts Normal file
View File

@ -0,0 +1,22 @@
interface JSONFeedDataItem {
id: string
url: string
title: string
content_text: string
date_published: string
date_modified: string
image: string
author: {
name: string
}
}
export interface JSONFeedData {
version: string
user_comment: string
home_page_url: string
feed_url: string
title: string
description: string
items: JSONFeedDataItem[]
}

View File

@ -9,6 +9,7 @@ export enum TemplateValueType {
CSV = 'csv',
Points = 'points',
Constant = 'constant',
MetaQuery = 'influxql',
}
export interface TemplateValue {
@ -34,8 +35,8 @@ export enum TemplateType {
TagKeys = 'tagKeys',
TagValues = 'tagValues',
CSV = 'csv',
Query = 'query',
Databases = 'databases',
MetaQuery = 'influxql',
}
export interface Template {