Merge pull request #1996 from influxdata/feature/source-on-cell

FEATURE: Switch InfluxDB sources per chart in Dashboards
pull/2070/head
Deniz Kusefoglu 2017-10-03 16:07:57 -07:00 committed by GitHub
commit 29f9d3d024
48 changed files with 584 additions and 292 deletions

View File

@ -5,6 +5,7 @@
1.[#2015](https://github.com/influxdata/chronograf/pull/2015): Chronograf shows real status for windows hosts when metrics are saved in non-default db - thank you, @ar7z1!
1.[#2019](https://github.com/influxdata/chronograf/pull/2006): Fix false error warning for duplicate kapacitor name
1.[#2018](https://github.com/influxdata/chronograf/pull/2018): Fix unresponsive display options and query builder in dashboards
1.[#1996](https://github.com/influxdata/chronograf/pull/1996): Able to switch InfluxDB sources on a per graph basis
### Features
1. [#1885](https://github.com/influxdata/chronograf/pull/1885): Add `fill` options to data explorer and dashboard queries

View File

@ -195,6 +195,7 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) {
Command: q.Command,
Label: q.Label,
Range: r,
Source: q.Source,
}
}
@ -274,6 +275,7 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error {
queries[j] = chronograf.DashboardQuery{
Command: q.Command,
Label: q.Label,
Source: q.Source,
}
if q.Range.Upper != q.Range.Lower {
queries[j].Range = &chronograf.Range{

View File

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

View File

@ -108,6 +108,7 @@ message Query {
repeated string Wheres = 5; // Wheres define the restrictions on the query
string Label = 6; // Label is the name of the Y-Axis
Range Range = 7; // Range is the upper and lower bound of the Y-Axis
string Source = 8; // Source is the optional URI to the data source
}
message Range {

View File

@ -162,6 +162,7 @@ func Test_MarshalDashboard(t *testing.T) {
Range: &chronograf.Range{
Upper: int64(100),
},
Source: "/chronograf/v1/sources/1",
},
},
Axes: map[string]chronograf.Axis{

View File

@ -395,6 +395,7 @@ type DashboardQuery struct {
Label string `json:"label,omitempty"` // Label is the Y-Axis label for the data
Range *Range `json:"range,omitempty"` // Range is the default Y-Axis range for the data
QueryConfig QueryConfig `json:"queryConfig,omitempty"` // QueryConfig represents the query state that is understood by the data explorer
Source string `json:"source"` // Source is the optional URI to the data source for this queryConfig
}
// TemplateQuery is used to retrieve choices for template replacement

View File

@ -217,6 +217,7 @@ func Test_newDashboardResponse(t *testing.T) {
H: 0,
Queries: []chronograf.DashboardQuery{
{
Source: "/chronograf/v1/sources/1",
Command: "SELECT donors from hill_valley_preservation_society where time > '1985-10-25 08:00:00'",
},
},
@ -236,6 +237,7 @@ func Test_newDashboardResponse(t *testing.T) {
H: 0,
Queries: []chronograf.DashboardQuery{
{
Source: "/chronograf/v1/sources/2",
Command: "SELECT winning_horses from grays_sports_alamanc where time > now() - 15m",
},
},
@ -256,6 +258,7 @@ func Test_newDashboardResponse(t *testing.T) {
Queries: []chronograf.DashboardQuery{
{
Command: "SELECT donors from hill_valley_preservation_society where time > '1985-10-25 08:00:00'",
Source: "/chronograf/v1/sources/1",
QueryConfig: chronograf.QueryConfig{
RawText: &[]string{"SELECT donors from hill_valley_preservation_society where time > '1985-10-25 08:00:00'"}[0],
Fields: []chronograf.Field{},
@ -303,6 +306,7 @@ func Test_newDashboardResponse(t *testing.T) {
Queries: []chronograf.DashboardQuery{
{
Command: "SELECT winning_horses from grays_sports_alamanc where time > now() - 15m",
Source: "/chronograf/v1/sources/2",
QueryConfig: chronograf.QueryConfig{
Measurement: "grays_sports_alamanc",
Fields: []chronograf.Field{

View File

@ -3766,6 +3766,11 @@
"query": {
"type": "string"
},
"source": {
"type": "string",
"format": "url",
"description": "Optional URI for data source for this query"
},
"queryConfig": {
"$ref": "#/definitions/QueryConfig"
}

View File

@ -154,7 +154,7 @@ class AdminPage extends Component {
<h1 className="page-header__title">Admin</h1>
</div>
<div className="page-header__right">
<SourceIndicator sourceName={source.name} />
<SourceIndicator />
</div>
</div>
</div>

View File

@ -156,7 +156,7 @@ class AlertsApp extends Component {
<h1 className="page-header__title">Alert History</h1>
</div>
<div className="page-header__right">
<SourceIndicator sourceName={source.name} />
<SourceIndicator />
<CustomTimeRangeDropdown
onApplyTimeRange={this.handleApplyTime}
timeRange={timeRange}

View File

@ -19,7 +19,7 @@ const AxesOptions = ({
const [min, max] = bounds
return (
<div className="display-options--cell">
<div className="display-options--cell y-axis-controls">
<h5 className="display-options--header">Y Axis Controls</h5>
<form autoComplete="off" style={{margin: '0 -6px'}}>
<div className="form-group col-sm-12">

View File

@ -23,10 +23,17 @@ class CellEditorOverlay extends Component {
constructor(props) {
super(props)
const {cell: {name, type, queries, axes}} = props
const {cell: {name, type, queries, axes}, sources} = props
let source = _.get(queries, ['0', 'source'], null)
source = sources.find(s => s.links.self === source) || props.source
const queriesWorkingDraft = _.cloneDeep(
queries.map(({queryConfig}) => ({...queryConfig, id: uuid.v4()}))
queries.map(({queryConfig}) => ({
...queryConfig,
id: uuid.v4(),
source,
}))
)
this.state = {
@ -141,6 +148,7 @@ class CellEditorOverlay extends Component {
return {
queryConfig: q,
query,
source: _.get(q, ['source', 'links', 'self'], null),
}
})
@ -197,6 +205,15 @@ class CellEditorOverlay extends Component {
})
}
handleSetQuerySource = source => {
const queriesWorkingDraft = this.state.queriesWorkingDraft.map(q => ({
..._.cloneDeep(q),
source,
}))
this.setState({queriesWorkingDraft})
}
getActiveQuery = () => {
const {queriesWorkingDraft, activeQueryIndex} = this.state
const activeQuery = queriesWorkingDraft[activeQueryIndex]
@ -221,9 +238,39 @@ class CellEditorOverlay extends Component {
}
}
formatSources = this.props.sources.map(s => ({
...s,
text: `${s.name} @ ${s.url}`,
}))
findSelectedSource = () => {
const {source} = this.props
const sources = this.formatSources
const query = _.get(this.state.queriesWorkingDraft, 0, false)
if (!query || !query.source) {
const defaultSource = sources.find(s => s.id === source.id)
return (defaultSource && defaultSource.text) || 'No sources'
}
const selected = sources.find(s => s.id === query.source.id)
return (selected && selected.text) || 'No sources'
}
getSource = () => {
const {source, sources} = this.props
const query = _.get(this.state.queriesWorkingDraft, 0, false)
if (!query || !query.source) {
return source
}
const querySource = sources.find(s => s.id === query.source.id)
return querySource || source
}
render() {
const {
source,
onCancel,
templates,
timeRange,
@ -232,12 +279,12 @@ class CellEditorOverlay extends Component {
} = this.props
const {
axes,
activeQueryIndex,
cellWorkingName,
cellWorkingType,
isDisplayOptionsTabActive,
queriesWorkingDraft,
axes,
} = this.state
const queryActions = {
@ -271,11 +318,14 @@ class CellEditorOverlay extends Component {
/>
<CEOBottom>
<OverlayControls
onCancel={onCancel}
sources={this.formatSources}
onSave={this.handleSaveCell}
selected={this.findSelectedSource()}
onSetQuerySource={this.handleSetQuerySource}
isSavable={queriesWorkingDraft.every(isQuerySavable)}
isDisplayOptionsTabActive={isDisplayOptionsTabActive}
onClickDisplayOptions={this.handleClickDisplayOptionsTab}
onCancel={onCancel}
onSave={this.handleSaveCell}
isSavable={queriesWorkingDraft.every(isQuerySavable)}
/>
{isDisplayOptionsTabActive
? <DisplayOptions
@ -291,7 +341,7 @@ class CellEditorOverlay extends Component {
onSetYAxisBoundMax={this.handleSetYAxisBoundMax}
/>
: <QueryMaker
source={source}
source={this.getSource()}
templates={templates}
queries={queriesWorkingDraft}
actions={queryActions}
@ -343,6 +393,7 @@ CellEditorOverlay.propTypes = {
status: shape({}),
}).isRequired,
dashboardID: string.isRequired,
sources: arrayOf(shape()),
}
CEOBottom.propTypes = {

View File

@ -7,6 +7,7 @@ import FancyScrollbar from 'shared/components/FancyScrollbar'
const Dashboard = ({
source,
sources,
onZoom,
dashboard,
onAddCell,
@ -24,16 +25,11 @@ const Dashboard = ({
}) => {
const cells = dashboard.cells.map(cell => {
const dashboardCell = {...cell}
dashboardCell.queries = dashboardCell.queries.map(
({label, query, queryConfig, db}) => ({
label,
query,
queryConfig,
db,
database: db,
text: query,
})
)
dashboardCell.queries = dashboardCell.queries.map(q => ({
...q,
database: q.db,
text: q.query,
}))
return dashboardCell
})
@ -54,17 +50,18 @@ const Dashboard = ({
/>}
{cells.length
? <LayoutRenderer
templates={templatesIncludingDashTime}
isEditable={true}
cells={cells}
onZoom={onZoom}
source={source}
sources={sources}
isEditable={true}
timeRange={timeRange}
autoRefresh={autoRefresh}
source={source}
onPositionChange={onPositionChange}
onDeleteCell={onDeleteCell}
onSummonOverlayTechnologies={onSummonOverlayTechnologies}
synchronizer={synchronizer}
onZoom={onZoom}
onDeleteCell={onDeleteCell}
onPositionChange={onPositionChange}
templates={templatesIncludingDashTime}
onSummonOverlayTechnologies={onSummonOverlayTechnologies}
/>
: <div className="dashboard__empty">
<p>This Dashboard has no Cells</p>
@ -112,6 +109,7 @@ Dashboard.propTypes = {
proxy: string,
}).isRequired,
}).isRequired,
sources: arrayOf(shape({})).isRequired,
autoRefresh: number.isRequired,
timeRange: shape({}).isRequired,
onOpenTemplateManager: func.isRequired,

View File

@ -18,7 +18,6 @@ const DashboardHeader = ({
handleChooseTimeRange,
handleChooseAutoRefresh,
handleClickPresentationButton,
source,
onAddCell,
onEditDashboard,
onToggleTempVarControls,
@ -49,7 +48,7 @@ const DashboardHeader = ({
</div>
<div className="page-header__right">
<GraphTips />
<SourceIndicator sourceName={source.name} />
<SourceIndicator />
{dashboard
? <button className="btn btn-primary btn-sm" onClick={onAddCell}>
<span className="icon plus" />
@ -107,7 +106,6 @@ DashboardHeader.defaultProps = {
}
DashboardHeader.propTypes = {
sourceID: string,
children: array,
buttonText: string,
dashboard: shape({}),
@ -121,7 +119,6 @@ DashboardHeader.propTypes = {
handleChooseTimeRange: func.isRequired,
handleChooseAutoRefresh: func.isRequired,
handleClickPresentationButton: func.isRequired,
source: shape({}),
onAddCell: func,
onEditDashboard: func,
onToggleTempVarControls: func,

View File

@ -1,26 +1,17 @@
import React, {PropTypes} from 'react'
import React from 'react'
import SourceIndicator from 'shared/components/SourceIndicator'
const DashboardsHeader = ({sourceName}) => {
return (
const DashboardsHeader = () =>
<div className="page-header">
<div className="page-header__container">
<div className="page-header__left">
<h1 className="page-header__title">Dashboards</h1>
</div>
<div className="page-header__right">
<SourceIndicator sourceName={sourceName} />
<SourceIndicator />
</div>
</div>
</div>
)
}
const {string} = PropTypes
DashboardsHeader.propTypes = {
sourceName: string.isRequired,
}
export default DashboardsHeader

View File

@ -2,16 +2,24 @@ import React, {PropTypes} from 'react'
import classnames from 'classnames'
import ConfirmButtons from 'shared/components/ConfirmButtons'
import SourceSelector from 'src/dashboards/components/SourceSelector'
const OverlayControls = ({
onCancel,
onSave,
sources,
selected,
onCancel,
isSavable,
onSetQuerySource,
isDisplayOptionsTabActive,
onClickDisplayOptions,
isSavable,
}) =>
<div className="overlay-controls">
<h3 className="overlay--graph-name">Cell Editor</h3>
<SourceSelector
sources={sources}
selected={selected}
onSetQuerySource={onSetQuerySource}
/>
<ul className="nav nav-tablist nav-tablist-sm">
<li
key="queries"
@ -29,7 +37,7 @@ const OverlayControls = ({
})}
onClick={onClickDisplayOptions(true)}
>
Display Options
Options
</li>
</ul>
<div className="overlay-controls--right">
@ -41,7 +49,7 @@ const OverlayControls = ({
</div>
</div>
const {func, bool} = PropTypes
const {arrayOf, bool, func, shape, string} = PropTypes
OverlayControls.propTypes = {
onCancel: func.isRequired,
@ -49,6 +57,9 @@ OverlayControls.propTypes = {
isDisplayOptionsTabActive: bool.isRequired,
onClickDisplayOptions: func.isRequired,
isSavable: bool,
sources: arrayOf(shape()),
onSetQuerySource: func,
selected: string,
}
export default OverlayControls

View File

@ -13,7 +13,7 @@ const buildText = q =>
q.rawText || buildInfluxQLQuery(q.range || TEMPLATE_RANGE, q) || ''
const QueryMaker = ({
source: {links},
source,
actions,
queries,
timeRange,
@ -39,7 +39,7 @@ const QueryMaker = ({
query={buildText(activeQuery)}
config={activeQuery}
onUpdate={rawTextBinder(
links,
source.links,
activeQuery.id,
actions.editRawTextAsync
)}
@ -49,6 +49,7 @@ const QueryMaker = ({
query={activeQuery}
actions={actions}
onAddQuery={onAddQuery}
source={source}
/>
</div>
: <EmptyQuery onAddQuery={onAddQuery} />}

View File

@ -0,0 +1,28 @@
import React, {PropTypes} from 'react'
import Dropdown from 'shared/components/Dropdown'
const SourceSelector = ({sources = [], selected, onSetQuerySource}) =>
sources.length > 1
? <div className="source-selector">
<h3>Source:</h3>
<Dropdown
items={sources}
buttonSize="btn-sm"
menuClass="dropdown-astronaut"
useAutoComplete={true}
selected={selected}
onChoose={onSetQuerySource}
className="dropdown-240"
/>
</div>
: null
const {arrayOf, func, shape, string} = PropTypes
SourceSelector.propTypes = {
sources: arrayOf(shape()).isRequired,
onSetQuerySource: func.isRequired,
selected: string,
}
export default SourceSelector

View File

@ -190,6 +190,7 @@ class DashboardPage extends Component {
const {
source,
sources,
timeRange,
timeRange: {lower, upper},
showTemplateControlBar,
@ -277,6 +278,7 @@ class DashboardPage extends Component {
{selectedCell
? <CellEditorOverlay
source={source}
sources={sources}
cell={selectedCell}
timeRange={timeRange}
autoRefresh={autoRefresh}
@ -324,6 +326,7 @@ class DashboardPage extends Component {
{dashboard
? <Dashboard
source={source}
sources={sources}
dashboard={dashboard}
timeRange={timeRange}
autoRefresh={autoRefresh}
@ -354,6 +357,7 @@ DashboardPage.propTypes = {
self: string,
}),
}).isRequired,
sources: arrayOf(shape({})).isRequired,
params: shape({
sourceID: string.isRequired,
dashboardID: string.isRequired,
@ -415,6 +419,7 @@ const mapStateToProps = state => {
persisted: {autoRefresh, showTemplateControlBar},
},
dashboardUI: {dashboards, timeRange, cellQueryStatus},
sources,
} = state
return {
@ -424,6 +429,7 @@ const mapStateToProps = state => {
showTemplateControlBar,
inPresentationMode,
cellQueryStatus,
sources,
}
}

View File

@ -22,12 +22,6 @@ const Header = React.createClass({
}).isRequired,
},
contextTypes: {
source: shape({
name: string,
}),
},
handleChooseTimeRange(bounds) {
this.props.actions.setTimeRange(bounds)
},
@ -48,7 +42,7 @@ const Header = React.createClass({
</div>
<div className="page-header__right">
<GraphTips />
<SourceIndicator sourceName={this.context.source.name} />
<SourceIndicator />
<div
className="btn btn-sm btn-default"
onClick={showWriteForm}

View File

@ -84,7 +84,7 @@ export const HostsPage = React.createClass({
<h1 className="page-header__title">Host List</h1>
</div>
<div className="page-header__right">
<SourceIndicator sourceName={source.name} />
<SourceIndicator />
</div>
</div>
</div>

View File

@ -99,7 +99,7 @@ const KapacitorRules = ({
)
}
const PageContents = ({children, source}) =>
const PageContents = ({children}) =>
<div className="page">
<div className="page-header">
<div className="page-header__container">
@ -109,7 +109,7 @@ const PageContents = ({children, source}) =>
</h1>
</div>
<div className="page-header__right">
<SourceIndicator sourceName={source && source.name} />
<SourceIndicator />
</div>
</div>
</div>
@ -139,7 +139,6 @@ KapacitorRules.propTypes = {
PageContents.propTypes = {
children: node,
source: shape(),
onCloseTickscript: func,
}

View File

@ -4,14 +4,13 @@ import TimeRangeDropdown from 'shared/components/TimeRangeDropdown'
import SourceIndicator from 'shared/components/SourceIndicator'
const RuleHeaderSave = ({
source,
onSave,
timeRange,
validationError,
onChooseTimeRange,
}) =>
<div className="page-header__right">
<SourceIndicator sourceName={source.name} />
<SourceIndicator />
<TimeRangeDropdown
onChooseTimeRange={onChooseTimeRange}
selected={timeRange}
@ -41,7 +40,6 @@ const RuleHeaderSave = ({
const {func, shape, string} = PropTypes
RuleHeaderSave.propTypes = {
source: shape({}).isRequired,
onSave: func.isRequired,
validationError: string.isRequired,
onChooseTimeRange: func.isRequired,

View File

@ -11,7 +11,6 @@ const addName = list => list.map(l => ({...l, name: `${l.db}.${l.rp}`}))
const TickscriptHeader = ({
task: {id, type, dbrps},
task,
source: {name},
onSave,
onChangeType,
onChangeID,
@ -26,7 +25,7 @@ const TickscriptHeader = ({
: <TickscriptStaticID id={task.name} />}
</div>
<div className="page-header__right">
<SourceIndicator sourceName={name} />
<SourceIndicator />
<TickscriptType type={type} onChangeType={onChangeType} />
<MultiSelectDBDropdown
selectedItems={addName(dbrps)}
@ -48,7 +47,6 @@ const {arrayOf, bool, func, shape, string} = PropTypes
TickscriptHeader.propTypes = {
onSave: func,
source: shape(),
onSelectDbrps: func.isRequired,
task: shape({
dbrps: arrayOf(

View File

@ -221,8 +221,8 @@ const AutoRefresh = ComposedComponent => {
return true
}
return data.every(datum => {
return datum.response.results.every(result => {
return data.every(({response}) => {
return _.get(response, 'results', []).every(result => {
return (
Object.keys(result).filter(k => k !== 'statement_id').length === 0
)

View File

@ -15,7 +15,7 @@ const CustomTimeIndicator = ({queries}) => {
: customLower
return (
<span className="dash-graph--custom-time">
<span className="custom-indicator">
{customTimeRange}
</span>
)

View File

@ -14,6 +14,17 @@ const DatabaseList = React.createClass({
propTypes: {
query: shape({}).isRequired,
onChooseNamespace: func.isRequired,
querySource: shape({
links: shape({
proxy: string.isRequired,
}).isRequired,
}),
},
getDefaultProps() {
return {
source: null,
}
},
contextTypes: {
@ -31,8 +42,23 @@ const DatabaseList = React.createClass({
},
componentDidMount() {
this.getDbRp()
},
componentDidUpdate(prevProps) {
if (_.isEqual(prevProps.querySource, this.props.querySource)) {
return
}
this.getDbRp()
},
getDbRp() {
const {source} = this.context
const proxy = source.links.proxy
const {querySource} = this.props
const proxy =
_.get(querySource, ['links', 'proxy'], null) || source.links.proxy
showDatabases(proxy).then(resp => {
const {errors, databases} = showDatabasesParser(resp.data)
if (errors.length) {

View File

@ -206,6 +206,7 @@ class Dropdown extends Component {
iconName,
buttonSize,
buttonColor,
toggleStyle,
useAutoComplete,
} = this.props
const {isOpen, searchTerm, filteredItems} = this.state
@ -222,6 +223,7 @@ class Dropdown extends Component {
{useAutoComplete && isOpen
? <div
className={`dropdown-autocomplete dropdown-toggle ${buttonSize} ${buttonColor}`}
style={toggleStyle}
>
<input
ref="dropdownAutoComplete"
@ -236,7 +238,10 @@ class Dropdown extends Component {
/>
<span className="caret" />
</div>
: <div className={`btn dropdown-toggle ${buttonSize} ${buttonColor}`}>
: <div
className={`btn dropdown-toggle ${buttonSize} ${buttonColor}`}
style={toggleStyle}
>
{iconName
? <span className={classnames('icon', {[iconName]: true})} />
: null}
@ -291,6 +296,7 @@ Dropdown.propTypes = {
menuLabel: string,
menuClass: string,
useAutoComplete: bool,
toggleStyle: shape(),
}
export default OnClickOutside(Dropdown)

View File

@ -1,4 +1,5 @@
import React, {PropTypes, Component} from 'react'
import _ from 'lodash'
import FieldListItem from 'src/data_explorer/components/FieldListItem'
import GroupByTimeDropdown from 'src/data_explorer/components/GroupByTimeDropdown'
@ -26,7 +27,8 @@ class FieldList extends Component {
}
componentDidUpdate(prevProps) {
const {database, measurement, retentionPolicy} = this.props.query
const {querySource, query} = this.props
const {database, measurement, retentionPolicy} = query
const {
database: prevDB,
measurement: prevMeas,
@ -39,7 +41,8 @@ class FieldList extends Component {
if (
database === prevDB &&
measurement === prevMeas &&
retentionPolicy === prevRP
retentionPolicy === prevRP &&
_.isEqual(prevProps.querySource, querySource)
) {
return
}
@ -58,14 +61,12 @@ class FieldList extends Component {
_getFields = () => {
const {database, measurement, retentionPolicy} = this.props.query
const {source} = this.context
const proxySource = source.links.proxy
const {querySource} = this.props
showFieldKeys(
proxySource,
database,
measurement,
retentionPolicy
).then(resp => {
const proxy =
_.get(querySource, ['links', 'proxy'], null) || source.links.proxy
showFieldKeys(proxy, database, measurement, retentionPolicy).then(resp => {
const {errors, fieldSets} = showFieldKeysParser(resp.data)
if (errors.length) {
console.error('Error parsing fields keys: ', errors)
@ -171,6 +172,11 @@ FieldList.propTypes = {
applyFuncsToField: func.isRequired,
isKapacitorRule: bool,
isInDataExplorer: bool,
querySource: shape({
links: shape({
proxy: string.isRequired,
}).isRequired,
}),
}
export default FieldList

View File

@ -4,7 +4,7 @@ import ReactTooltip from 'react-tooltip'
const GraphTips = React.createClass({
render() {
const graphTipsText =
'<p><b>Graph Tips:</b><br/><br/><code>Click + Drag</code> Zoom in (X or Y)</p><p><code>Shift + Click</code> Pan Graph Window</p><p><code>Double Click</code> Reset Graph Window</p>'
'<h1>Graph Tips:</h1><p><code>Click + Drag</code> Zoom in (X or Y)</p><p><code>Shift + Click</code> Pan Graph Window</p><p><code>Double Click</code> Reset Graph Window</p>'
return (
<div
className="graph-tips"

View File

@ -4,11 +4,24 @@ import LayoutCell from 'shared/components/LayoutCell'
import RefreshingGraph from 'shared/components/RefreshingGraph'
import {buildQueriesForLayouts} from 'utils/influxql'
const Layout = ({
import _ from 'lodash'
const getSource = (cell, source, sources, defaultSource) => {
const s = _.get(cell, ['queries', '0', 'source'], null)
if (!s) {
return source
}
return sources.find(src => src.links.self === s) || defaultSource
}
const Layout = (
{
host,
cell,
cell: {h, axes, type},
source,
sources,
onZoom,
templates,
timeRange,
@ -20,14 +33,16 @@ const Layout = ({
resizeCoords,
onCancelEditCell,
onSummonOverlayTechnologies,
}) =>
},
{source: defaultSource}
) =>
<LayoutCell
onCancelEditCell={onCancelEditCell}
cell={cell}
isEditable={isEditable}
onEditCell={onEditCell}
onDeleteCell={onDeleteCell}
onCancelEditCell={onCancelEditCell}
onSummonOverlayTechnologies={onSummonOverlayTechnologies}
cell={cell}
>
{cell.isWidget
? <WidgetCell cell={cell} timeRange={timeRange} source={source} />
@ -36,17 +51,27 @@ const Layout = ({
type={type}
cellHeight={h}
onZoom={onZoom}
sources={sources}
timeRange={timeRange}
templates={templates}
autoRefresh={autoRefresh}
synchronizer={synchronizer}
resizeCoords={resizeCoords}
queries={buildQueriesForLayouts(cell, source, timeRange, host)}
queries={buildQueriesForLayouts(
cell,
getSource(cell, source, sources, defaultSource),
timeRange,
host
)}
/>}
</LayoutCell>
const {arrayOf, bool, func, number, shape, string} = PropTypes
Layout.contextTypes = {
source: shape(),
}
Layout.propTypes = {
autoRefresh: number.isRequired,
timeRange: shape({
@ -87,6 +112,7 @@ Layout.propTypes = {
onCancelEditCell: func,
resizeCoords: shape(),
onZoom: func,
sources: arrayOf(shape()),
}
export default Layout

View File

@ -40,16 +40,16 @@ class LayoutCell extends Component {
<div className="dash-graph">
<LayoutCellMenu
cell={cell}
onDeleteClick={this.handleDeleteClick}
onDelete={this.handleDeleteCell}
isDeleting={isDeleting}
isEditable={isEditable}
handleClickOutside={this.closeMenu}
onDelete={this.handleDeleteCell}
onEdit={this.handleSummonOverlay}
handleClickOutside={this.closeMenu}
onDeleteClick={this.handleDeleteClick}
/>
<LayoutCellHeader
cellName={cell.name}
queries={queries}
cellName={cell.name}
isEditable={isEditable}
/>
<div className="dash-graph--container">
@ -69,7 +69,7 @@ class LayoutCell extends Component {
}
}
const {array, bool, func, node, number, shape, string} = PropTypes
const {arrayOf, bool, func, node, number, shape, string} = PropTypes
LayoutCell.propTypes = {
cell: shape({
@ -77,7 +77,7 @@ LayoutCell.propTypes = {
isEditing: bool,
x: number.isRequired,
y: number.isRequired,
queries: array,
queries: arrayOf(shape()),
}).isRequired,
children: node.isRequired,
onDeleteCell: func,

View File

@ -1,5 +1,4 @@
import React, {PropTypes} from 'react'
import classnames from 'classnames'
import CustomTimeIndicator from 'shared/components/CustomTimeIndicator'
@ -8,12 +7,12 @@ import {NEW_DEFAULT_DASHBOARD_CELL} from 'src/dashboards/constants/index'
const LayoutCellHeader = ({queries, isEditable, cellName}) => {
const cellNameIsDefault = cellName === NEW_DEFAULT_DASHBOARD_CELL.name
const headingClass = `dash-graph--heading ${isEditable
? 'dash-graph--heading-draggable'
: ''}`
return (
<div
className={classnames('dash-graph--heading', {
'dash-graph--heading-draggable': isEditable,
})}
>
<div className={headingClass}>
<span
className={
cellNameIsDefault
@ -22,18 +21,20 @@ const LayoutCellHeader = ({queries, isEditable, cellName}) => {
}
>
{cellName}
<div className="dash-graph--custom-indicators">
{queries && queries.length
? <CustomTimeIndicator queries={queries} />
: null}
</div>
</span>
</div>
)
}
const {array, bool, string} = PropTypes
const {arrayOf, bool, shape, string} = PropTypes
LayoutCellHeader.propTypes = {
queries: array,
queries: arrayOf(shape()),
isEditable: bool,
cellName: string,
}

View File

@ -1,6 +1,7 @@
import React, {Component, PropTypes} from 'react'
import ReactGridLayout, {WidthProvider} from 'react-grid-layout'
import Resizeable from 'react-component-resizable'
import _ from 'lodash'
import Layout from 'src/shared/components/Layout'
@ -26,11 +27,6 @@ class LayoutRenderer extends Component {
}
}
// idea adopted from https://stackoverflow.com/questions/36862334/get-viewport-window-height-in-reactjs
updateWindowDimensions = () => {
this.setState({rowHeight: this.calculateRowHeight()})
}
handleLayoutChange = layout => {
if (!this.props.onPositionChange) {
return
@ -72,6 +68,7 @@ class LayoutRenderer extends Component {
host,
cells,
source,
sources,
onZoom,
templates,
timeRange,
@ -88,7 +85,7 @@ class LayoutRenderer extends Component {
const isDashboard = !!this.props.onPositionChange
return (
<Resizeable onResize={this.updateWindowDimensions}>
<Resizeable onResize={this.handleCellResize}>
<GridLayout
layout={cells}
cols={12}
@ -110,6 +107,7 @@ class LayoutRenderer extends Component {
host={host}
source={source}
onZoom={onZoom}
sources={sources}
templates={templates}
timeRange={timeRange}
isEditable={isEditable}
@ -172,6 +170,7 @@ LayoutRenderer.propTypes = {
isEditable: bool,
onCancelEditCell: func,
onZoom: func,
sources: arrayOf(shape({})),
}
export default LayoutRenderer

View File

@ -1,5 +1,6 @@
import React, {PropTypes} from 'react'
import classnames from 'classnames'
import _ from 'lodash'
import {showMeasurements} from 'shared/apis/metaQuery'
import showMeasurementsParser from 'shared/parsing/showMeasurements'
@ -19,6 +20,11 @@ const MeasurementList = React.createClass({
onChooseTag: func.isRequired,
onToggleTagAcceptance: func.isRequired,
onGroupByTag: func.isRequired,
querySource: shape({
links: shape({
proxy: string.isRequired,
}).isRequired,
}),
},
contextTypes: {
@ -36,6 +42,12 @@ const MeasurementList = React.createClass({
}
},
getDefaultProps() {
return {
querySource: null,
}
},
componentDidMount() {
if (!this.props.query.database) {
return
@ -45,13 +57,16 @@ const MeasurementList = React.createClass({
},
componentDidUpdate(prevProps) {
const {query} = this.props
const {query, querySource} = this.props
if (!query.database) {
return
}
if (prevProps.query.database === query.database) {
if (
prevProps.query.database === query.database &&
_.isEqual(prevProps.querySource, querySource)
) {
return
}
@ -181,7 +196,11 @@ const MeasurementList = React.createClass({
_getMeasurements() {
const {source} = this.context
const proxy = source.links.proxy
const {querySource} = this.props
const proxy =
_.get(querySource, ['links', 'proxy'], null) || source.links.proxy
showMeasurements(proxy, this.props.query.database).then(resp => {
const {errors, measurementSets} = showMeasurementsParser(resp.data)
if (errors.length) {

View File

@ -9,6 +9,7 @@ const actionBinder = (id, action) => item => action(id, item)
const SchemaExplorer = ({
query,
query: {id},
source,
actions: {
chooseTag,
groupByTag,
@ -24,17 +25,22 @@ const SchemaExplorer = ({
<div className="query-builder">
<DatabaseList
query={query}
querySource={source}
onChooseNamespace={actionBinder(id, chooseNamespace)}
/>
<MeasurementList
source={source}
query={query}
querySource={source}
onChooseTag={actionBinder(id, chooseTag)}
onGroupByTag={actionBinder(id, groupByTag)}
onChooseMeasurement={actionBinder(id, chooseMeasurement)}
onToggleTagAcceptance={actionBinder(id, toggleTagAcceptance)}
/>
<FieldList
source={source}
query={query}
querySource={source}
onToggleField={actionBinder(id, toggleFieldWithGroupByInterval)}
onFill={actionBinder(id, fill)}
onGroupByTime={actionBinder(id, groupByTime)}
@ -60,6 +66,7 @@ SchemaExplorer.propTypes = {
fill: func.isRequired,
editRawTextAsync: func.isRequired,
}).isRequired,
source: shape({}),
}
export default SchemaExplorer

View File

@ -1,26 +1,32 @@
import React, {PropTypes} from 'react'
import _ from 'lodash'
import uuid from 'node-uuid'
import ReactTooltip from 'react-tooltip'
const SourceIndicator = React.createClass({
propTypes: {
sourceName: PropTypes.string,
},
const SourceIndicator = ({sourceOverride}, {source: {name, url}}) => {
const sourceName = _.get(sourceOverride, 'name', null)
? sourceOverride.name
: name
const sourceUrl = _.get(sourceOverride, 'url', null)
? sourceOverride.url
: url
render() {
const {sourceName} = this.props
if (!sourceName) {
return null
}
const sourceNameTooltip = `Connected to <code>${sourceName}</code>`
const sourceNameTooltip = `<h1>Connected to Source:</h1><p><code>${sourceName} @ ${sourceUrl}</code></p>`
const uuidTooltip = uuid.v4()
return (
<div
className="source-indicator"
data-for="source-indicator-tooltip"
data-for={uuidTooltip}
data-tip={sourceNameTooltip}
>
<span className="icon server2" />
<ReactTooltip
id="source-indicator-tooltip"
id={uuidTooltip}
effect="solid"
html={true}
offset={{top: 2}}
@ -29,7 +35,22 @@ const SourceIndicator = React.createClass({
/>
</div>
)
},
})
}
const {shape, string} = PropTypes
SourceIndicator.propTypes = {
sourceOverride: shape({
name: string,
url: string,
}),
}
SourceIndicator.contextTypes = {
source: shape({
name: string,
url: string,
}),
}
export default SourceIndicator

View File

@ -62,7 +62,7 @@ class ManageSources extends Component {
<h1 className="page-header__title">Configuration</h1>
</div>
<div className="page-header__right">
<SourceIndicator sourceName={source.name} />
<SourceIndicator />
</div>
</div>
</div>

View File

@ -125,7 +125,7 @@ export const SourcePage = React.createClass({
</h1>
</div>
<div className="page-header__right">
<SourceIndicator sourceName={source.name} />
<SourceIndicator />
</div>
</div>
</div>

View File

@ -56,7 +56,7 @@ class StatusPage extends Component {
<h1 className="page-header__title">Status</h1>
</div>
<div className="page-header__right">
<SourceIndicator sourceName={source.name} />
<SourceIndicator />
</div>
</div>
</div>

View File

@ -50,6 +50,7 @@
@import 'components/resizer';
@import 'components/search-widget';
@import 'components/source-indicator';
@import 'components/source-selector';
@import 'components/tables';
// Pages

View File

@ -17,8 +17,6 @@
padding: 30px;
flex: 1 0 0;
margin-right: 8px;
&:last-child { margin-right: 0; }
}
.display-options--cellx2 {
flex: 2 0 0;
@ -29,8 +27,6 @@
color: $g11-sidewalk;
@include no-user-select();
}
.viz-type-selector {
display: flex;
flex-wrap: wrap;

View File

@ -33,6 +33,15 @@ $tooltip-code-color: $c-potassium;
text-transform: none !important;
cursor: default;
h1 {
font-size: 14px;
font-weight: 600;
margin: 0 0 6px 0;
line-height: 1.125em;
letter-spacing: 0;
font-family: $default-font;
}
p {
margin: 0;
width: 100%;
@ -70,7 +79,6 @@ $tooltip-code-color: $c-potassium;
border-color: transparent transparent $tooltip-accent transparent !important;
}
/* Kapacitor Style Tooltip */
&.kapacitor-tooltip {
border-color: $c-rainforest !important;
@ -90,14 +98,13 @@ $tooltip-code-color: $c-potassium;
.influx-tooltip__hover {
@extend .influx-tooltip;
pointer-events: auto!important;
pointer-events: auto !important;
&:hover {
visibility: visible!important;
opacity: 1!important;
visibility: visible !important;
opacity: 1 !important;
}
}
/*
Question Mark Tooltip
----------------------------------------------
@ -121,8 +128,7 @@ $qmark-tooltip-size: 15px;
background-color: $g10-wolf;
color: $g0-obsidian;
margin: 0 5px;
transition:
background-color 0.25s ease;
transition: background-color 0.25s ease;
}
.question-mark-tooltip:hover {
cursor: default;

View File

@ -0,0 +1,18 @@
/*
Source Selector component styles
----------------------------------------------------------------
*/
.source-selector {
display: flex;
align-items: center;
flex-wrap: nowrap;
flex: 1 0 0;
h3 {
margin: 0 4px 0 0;
font-size: 17px;
color: $g13-mist;
@include no-user-select();
}
}

View File

@ -11,22 +11,49 @@ $dash-graph-options-arrow: 8px;
------------------------------------------------------
*/
@keyframes refreshingSpinnerA {
0% { transform: translate(-50%,-50%) scale(1.75); background-color: $g7-graphite; }
33% { transform: translate(-50%,-50%) scale(1,1); }
66% { transform: translate(-50%,-50%) scale(1,1); }
100% { transform: translate(-50%,-50%) scale(1,1); }
0% {
transform: translate(-50%, -50%) scale(1.75);
background-color: $g7-graphite;
}
33% {
transform: translate(-50%, -50%) scale(1, 1);
}
66% {
transform: translate(-50%, -50%) scale(1, 1);
}
100% {
transform: translate(-50%, -50%) scale(1, 1);
}
}
@keyframes refreshingSpinnerB {
0% { transform: translate(-50%,-50%) scale(1,1); }
33% { transform: translate(-50%,-50%) scale(1.75); background-color: $g7-graphite; }
66% { transform: translate(-50%,-50%) scale(1,1); }
100% { transform: translate(-50%,-50%) scale(1,1); }
0% {
transform: translate(-50%, -50%) scale(1, 1);
}
33% {
transform: translate(-50%, -50%) scale(1.75);
background-color: $g7-graphite;
}
66% {
transform: translate(-50%, -50%) scale(1, 1);
}
100% {
transform: translate(-50%, -50%) scale(1, 1);
}
}
@keyframes refreshingSpinnerC {
0% { transform: translate(-50%,-50%) scale(1,1); }
33% { transform: translate(-50%,-50%) scale(1,1); }
66% { transform: translate(-50%,-50%) scale(1.75); background-color: $g7-graphite; }
100% { transform: translate(-50%,-50%) scale(1,1); }
0% {
transform: translate(-50%, -50%) scale(1, 1);
}
33% {
transform: translate(-50%, -50%) scale(1, 1);
}
66% {
transform: translate(-50%, -50%) scale(1.75);
background-color: $g7-graphite;
}
100% {
transform: translate(-50%, -50%) scale(1, 1);
}
}
/*
@ -93,7 +120,6 @@ $dash-graph-options-arrow: 8px;
top: 0;
width: 100%;
height: 100%;
}
.dygraph .dygraph-child {
position: absolute;
@ -124,9 +150,7 @@ $dash-graph-options-arrow: 8px;
font-weight: 600;
font-size: 13px;
color: $g14-chromium;
transition:
color 0.25s ease,
background-color 0.25s ease;
transition: color 0.25s ease, background-color 0.25s ease;
&.dash-graph--heading-draggable:hover {
cursor: move;
background-color: $g5-pepper;
@ -146,30 +170,41 @@ $dash-graph-options-arrow: 8px;
line-height: $dash-graph-heading;
width: calc(100% - 53px);
padding-left: 10px;
transition:
color 0.25s ease,
background-color 0.25s ease,
transition: color 0.25s ease, background-color 0.25s ease,
border-color 0.25s ease;
}
.dash-graph--name.dash-graph--name__default {
font-style: italic;
}
.dash-graph--custom-indicators {
height: 24px;
border-radius: 3px;
position: absolute;
top: 3px;
right: 0;
display: flex;
.dash-graph--custom-time {
> .custom-indicator,
> .source-indicator {
font-size: 10px;
line-height: 24px;
padding: 0 7px;
font-style: normal;
font-family: $code-font;
color: $c-pool;
background-color: $g2-kevlar;
height: 24px;
font-size: 10px;
line-height: 24px;
margin-right: 2px;
border-radius: 3px;
padding: 0 7px;
position: absolute;
top: 3px;
right: 2px;
}
}
> .source-indicator {
height: 24px;
> .icon {
font-size: 12px;
margin: 0;
}
}
}
.dash-graph-context {
z-index: 2;
position: absolute;
@ -187,9 +222,7 @@ $dash-graph-options-arrow: 8px;
font-size: 12px;
position: relative;
color: $g11-sidewalk;
transition:
color 0.25s ease,
background-color 0.25s ease;
transition: color 0.25s ease, background-color 0.25s ease;
&:hover,
&.active {
@ -197,13 +230,15 @@ $dash-graph-options-arrow: 8px;
color: $g20-white;
background-color: $g5-pepper;
}
&:first-child {margin-right: 2px;}
&:first-child {
margin-right: 2px;
}
> .icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
transform: translate(-50%, -50%);
}
}
.dash-graph-context--confirm {
@ -230,7 +265,7 @@ $dash-graph-options-arrow: 8px;
border-bottom-color: $c-curacao;
left: 50%;
top: 0;
transform: translate(-50%,-100%);
transform: translate(-50%, -100%);
transition: border-color 0.25s ease;
}
@ -241,7 +276,6 @@ $dash-graph-options-arrow: 8px;
&:hover:before {
border-bottom-color: $c-dreamsicle;
}
}
/* Presentation Mode */
@ -257,7 +291,7 @@ $dash-graph-options-arrow: 8px;
.graph-panel__refreshing {
position: absolute;
top: -18px !important;
transform: translate(0,0);
transform: translate(0, 0);
right: 50%;
transform: translateX(50%);
width: 16px;
@ -270,12 +304,24 @@ $dash-graph-options-arrow: 8px;
border-radius: 50%;
position: absolute;
top: 50%;
transform: translate(-50%,-50%);
transform: translate(-50%, -50%);
}
div:nth-child(1) {left: 0; animation: refreshingSpinnerA 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) infinite; }
div:nth-child(2) {left: 50%; animation: refreshingSpinnerB 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) infinite; }
div:nth-child(3) {left: 100%; animation: refreshingSpinnerC 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) infinite;}
div:nth-child(1) {
left: 0;
animation: refreshingSpinnerA 0.8s cubic-bezier(0.645, 0.045, 0.355, 1)
infinite;
}
div:nth-child(2) {
left: 50%;
animation: refreshingSpinnerB 0.8s cubic-bezier(0.645, 0.045, 0.355, 1)
infinite;
}
div:nth-child(3) {
left: 100%;
animation: refreshingSpinnerC 0.8s cubic-bezier(0.645, 0.045, 0.355, 1)
infinite;
}
}
/*
@ -291,7 +337,7 @@ $dash-graph-options-arrow: 8px;
}
.react-grid-item {
&.resizing {
background-color: fade-out($g3-castle,0.09);
background-color: fade-out($g3-castle, 0.09);
border-color: $c-pool;
border-image-slice: 3%;
border-image-repeat: initial;
@ -301,13 +347,14 @@ $dash-graph-options-arrow: 8px;
z-index: 3;
& > .react-resizable-handle {
&:before, &:after {
&:before,
&:after {
background-color: $c-comet;
}
}
}
&.react-draggable-dragging {
background-color: fade-out($g3-castle,0.09);
background-color: fade-out($g3-castle, 0.09);
border-color: $c-pool;
border-image-slice: 3%;
border-image-repeat: initial;
@ -341,14 +388,15 @@ $dash-graph-options-arrow: 8px;
}
&:before {
width: 20px;
transform: translate(-50%,-50%) rotate(-45deg);
transform: translate(-50%, -50%) rotate(-45deg);
}
&:after {
width: 12px;
transform: translate(-3px,2px) rotate(-45deg);
transform: translate(-3px, 2px) rotate(-45deg);
}
&:hover {
&:before, &:after {
&:before,
&:after {
background-color: $c-comet;
}
}

View File

@ -44,10 +44,21 @@ $overlay-z: 100;
border: 0;
background-color: $g2-kevlar;
}
.overlay-controls .nav-tablist {
width: 200px;
li {
white-space: nowrap;
justify-content: center;
flex: 1 0 50%;
}
}
.overlay-controls--right {
display: flex;
align-items: center;
flex-wrap: nowrap;
justify-content: flex-end;
flex: 1 0 0;
.toggle {
margin: 0 0 0 5px;

View File

@ -8,7 +8,17 @@ const buildQueries = (proxy, queryConfigs, timeRange) => {
})
const queries = statements.filter(s => s.text !== null).map(s => {
return {host: [proxy], text: s.text, id: s.id, queryConfig: s.queryConfig}
let queryProxy = ''
if (s.queryConfig.source) {
queryProxy = `${s.queryConfig.source.links.proxy}`
}
return {
host: [queryProxy || proxy],
text: s.text,
id: s.id,
queryConfig: s.queryConfig,
}
})
return queries