Merge pull request #3331 from influxdata/table/field-options

Table/field options
pull/10616/head
Deniz Kusefoglu 2018-04-30 18:00:03 -07:00 committed by GitHub
commit 9cf0ff1b3e
30 changed files with 1095 additions and 678 deletions

View File

@ -344,7 +344,6 @@ func (m *DashboardCell) GetTableOptions() *TableOptions {
}
type TableOptions struct {
TimeFormat string `protobuf:"bytes,1,opt,name=timeFormat,proto3" json:"timeFormat,omitempty"`
VerticalTimeAxis bool `protobuf:"varint,2,opt,name=verticalTimeAxis,proto3" json:"verticalTimeAxis,omitempty"`
SortBy *RenamableField `protobuf:"bytes,3,opt,name=sortBy" json:"sortBy,omitempty"`
Wrapping string `protobuf:"bytes,4,opt,name=wrapping,proto3" json:"wrapping,omitempty"`
@ -357,13 +356,6 @@ func (m *TableOptions) String() string { return proto.CompactTextStri
func (*TableOptions) ProtoMessage() {}
func (*TableOptions) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{3} }
func (m *TableOptions) GetTimeFormat() string {
if m != nil {
return m.TimeFormat
}
return ""
}
func (m *TableOptions) GetVerticalTimeAxis() bool {
if m != nil {
return m.VerticalTimeAxis

View File

@ -273,24 +273,27 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) {
Visible: c.TableOptions.SortBy.Visible,
}
fieldNames := make([]*RenamableField, len(c.TableOptions.FieldNames))
for i, field := range c.TableOptions.FieldNames {
fieldNames[i] = &RenamableField{
tableOptions := &TableOptions{
VerticalTimeAxis: c.TableOptions.VerticalTimeAxis,
SortBy: sortBy,
Wrapping: c.TableOptions.Wrapping,
FixFirstColumn: c.TableOptions.FixFirstColumn,
}
decimalPlaces := &DecimalPlaces{
IsEnforced: c.DecimalPlaces.IsEnforced,
Digits: c.DecimalPlaces.Digits,
}
fieldOptions := make([]*RenamableField, len(c.FieldOptions))
for i, field := range c.FieldOptions {
fieldOptions[i] = &RenamableField{
InternalName: field.InternalName,
DisplayName: field.DisplayName,
Visible: field.Visible,
}
}
tableOptions := &TableOptions{
TimeFormat: c.TableOptions.TimeFormat,
VerticalTimeAxis: c.TableOptions.VerticalTimeAxis,
SortBy: sortBy,
Wrapping: c.TableOptions.Wrapping,
FieldNames: fieldNames,
FixFirstColumn: c.TableOptions.FixFirstColumn,
}
cells[i] = &DashboardCell{
ID: c.ID,
X: c.X,
@ -306,7 +309,10 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) {
Type: c.Legend.Type,
Orientation: c.Legend.Orientation,
},
TableOptions: tableOptions,
TableOptions: tableOptions,
FieldOptions: fieldOptions,
TimeFormat: c.TimeFormat,
DecimalPlaces: decimalPlaces,
}
}
templates := make([]*Template, len(d.Templates))
@ -442,20 +448,23 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error {
sortBy.Visible = c.TableOptions.SortBy.Visible
}
tableOptions.SortBy = sortBy
fieldNames := make([]chronograf.RenamableField, len(c.TableOptions.FieldNames))
for i, field := range c.TableOptions.FieldNames {
fieldNames[i] = chronograf.RenamableField{}
fieldNames[i].InternalName = field.InternalName
fieldNames[i].DisplayName = field.DisplayName
fieldNames[i].Visible = field.Visible
}
tableOptions.FieldNames = fieldNames
tableOptions.TimeFormat = c.TableOptions.TimeFormat
tableOptions.VerticalTimeAxis = c.TableOptions.VerticalTimeAxis
tableOptions.Wrapping = c.TableOptions.Wrapping
tableOptions.FixFirstColumn = c.TableOptions.FixFirstColumn
}
fieldOptions := make([]chronograf.RenamableField, len(c.FieldOptions))
for i, field := range c.FieldOptions {
fieldOptions[i] = chronograf.RenamableField{}
fieldOptions[i].InternalName = field.InternalName
fieldOptions[i].DisplayName = field.DisplayName
fieldOptions[i].Visible = field.Visible
}
decimalPlaces := chronograf.DecimalPlaces{}
if c.DecimalPlaces != nil {
decimalPlaces.IsEnforced = c.DecimalPlaces.IsEnforced
decimalPlaces.Digits = c.DecimalPlaces.Digits
}
// FIXME: this is merely for legacy cells and
@ -466,18 +475,21 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error {
}
cells[i] = chronograf.DashboardCell{
ID: c.ID,
X: c.X,
Y: c.Y,
W: c.W,
H: c.H,
Name: c.Name,
Queries: queries,
Type: cellType,
Axes: axes,
CellColors: colors,
Legend: legend,
TableOptions: tableOptions,
ID: c.ID,
X: c.X,
Y: c.Y,
W: c.W,
H: c.H,
Name: c.Name,
Queries: queries,
Type: cellType,
Axes: axes,
CellColors: colors,
Legend: legend,
TableOptions: tableOptions,
FieldOptions: fieldOptions,
TimeFormat: c.TimeFormat,
DecimalPlaces: decimalPlaces,
}
}

View File

@ -11,6 +11,7 @@ It has these top-level messages:
Source
Dashboard
DashboardCell
DecimalPlaces
TableOptions
RenamableField
Color
@ -220,18 +221,21 @@ func (m *Dashboard) GetOrganization() string {
}
type DashboardCell struct {
X int32 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"`
Y int32 `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"`
W int32 `protobuf:"varint,3,opt,name=w,proto3" json:"w,omitempty"`
H int32 `protobuf:"varint,4,opt,name=h,proto3" json:"h,omitempty"`
Queries []*Query `protobuf:"bytes,5,rep,name=queries" json:"queries,omitempty"`
Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
Type string `protobuf:"bytes,7,opt,name=type,proto3" json:"type,omitempty"`
ID string `protobuf:"bytes,8,opt,name=ID,proto3" json:"ID,omitempty"`
Axes map[string]*Axis `protobuf:"bytes,9,rep,name=axes" json:"axes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
Colors []*Color `protobuf:"bytes,10,rep,name=colors" json:"colors,omitempty"`
Legend *Legend `protobuf:"bytes,11,opt,name=legend" json:"legend,omitempty"`
TableOptions *TableOptions `protobuf:"bytes,12,opt,name=tableOptions" json:"tableOptions,omitempty"`
X int32 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"`
Y int32 `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"`
W int32 `protobuf:"varint,3,opt,name=w,proto3" json:"w,omitempty"`
H int32 `protobuf:"varint,4,opt,name=h,proto3" json:"h,omitempty"`
Queries []*Query `protobuf:"bytes,5,rep,name=queries" json:"queries,omitempty"`
Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
Type string `protobuf:"bytes,7,opt,name=type,proto3" json:"type,omitempty"`
ID string `protobuf:"bytes,8,opt,name=ID,proto3" json:"ID,omitempty"`
Axes map[string]*Axis `protobuf:"bytes,9,rep,name=axes" json:"axes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
Colors []*Color `protobuf:"bytes,10,rep,name=colors" json:"colors,omitempty"`
Legend *Legend `protobuf:"bytes,11,opt,name=legend" json:"legend,omitempty"`
TableOptions *TableOptions `protobuf:"bytes,12,opt,name=tableOptions" json:"tableOptions,omitempty"`
FieldOptions []*RenamableField `protobuf:"bytes,13,rep,name=fieldOptions" json:"fieldOptions,omitempty"`
TimeFormat string `protobuf:"bytes,14,opt,name=timeFormat,proto3" json:"timeFormat,omitempty"`
DecimalPlaces *DecimalPlaces `protobuf:"bytes,15,opt,name=decimalPlaces" json:"decimalPlaces,omitempty"`
}
func (m *DashboardCell) Reset() { *m = DashboardCell{} }
@ -323,27 +327,63 @@ func (m *DashboardCell) GetTableOptions() *TableOptions {
return nil
}
type TableOptions struct {
TimeFormat string `protobuf:"bytes,1,opt,name=timeFormat,proto3" json:"timeFormat,omitempty"`
VerticalTimeAxis bool `protobuf:"varint,2,opt,name=verticalTimeAxis,proto3" json:"verticalTimeAxis,omitempty"`
SortBy *RenamableField `protobuf:"bytes,3,opt,name=sortBy" json:"sortBy,omitempty"`
Wrapping string `protobuf:"bytes,4,opt,name=wrapping,proto3" json:"wrapping,omitempty"`
FieldNames []*RenamableField `protobuf:"bytes,5,rep,name=fieldNames" json:"fieldNames,omitempty"`
FixFirstColumn bool `protobuf:"varint,6,opt,name=fixFirstColumn,proto3" json:"fixFirstColumn,omitempty"`
func (m *DashboardCell) GetFieldOptions() []*RenamableField {
if m != nil {
return m.FieldOptions
}
return nil
}
func (m *TableOptions) Reset() { *m = TableOptions{} }
func (m *TableOptions) String() string { return proto.CompactTextString(m) }
func (*TableOptions) ProtoMessage() {}
func (*TableOptions) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{3} }
func (m *TableOptions) GetTimeFormat() string {
func (m *DashboardCell) GetTimeFormat() string {
if m != nil {
return m.TimeFormat
}
return ""
}
func (m *DashboardCell) GetDecimalPlaces() *DecimalPlaces {
if m != nil {
return m.DecimalPlaces
}
return nil
}
type DecimalPlaces struct {
IsEnforced bool `protobuf:"varint,1,opt,name=isEnforced,proto3" json:"isEnforced,omitempty"`
Digits int32 `protobuf:"varint,2,opt,name=digits,proto3" json:"digits,omitempty"`
}
func (m *DecimalPlaces) Reset() { *m = DecimalPlaces{} }
func (m *DecimalPlaces) String() string { return proto.CompactTextString(m) }
func (*DecimalPlaces) ProtoMessage() {}
func (*DecimalPlaces) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{3} }
func (m *DecimalPlaces) GetIsEnforced() bool {
if m != nil {
return m.IsEnforced
}
return false
}
func (m *DecimalPlaces) GetDigits() int32 {
if m != nil {
return m.Digits
}
return 0
}
type TableOptions struct {
VerticalTimeAxis bool `protobuf:"varint,2,opt,name=verticalTimeAxis,proto3" json:"verticalTimeAxis,omitempty"`
SortBy *RenamableField `protobuf:"bytes,3,opt,name=sortBy" json:"sortBy,omitempty"`
Wrapping string `protobuf:"bytes,4,opt,name=wrapping,proto3" json:"wrapping,omitempty"`
FixFirstColumn bool `protobuf:"varint,6,opt,name=fixFirstColumn,proto3" json:"fixFirstColumn,omitempty"`
}
func (m *TableOptions) Reset() { *m = TableOptions{} }
func (m *TableOptions) String() string { return proto.CompactTextString(m) }
func (*TableOptions) ProtoMessage() {}
func (*TableOptions) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{4} }
func (m *TableOptions) GetVerticalTimeAxis() bool {
if m != nil {
return m.VerticalTimeAxis
@ -365,13 +405,6 @@ func (m *TableOptions) GetWrapping() string {
return ""
}
func (m *TableOptions) GetFieldNames() []*RenamableField {
if m != nil {
return m.FieldNames
}
return nil
}
func (m *TableOptions) GetFixFirstColumn() bool {
if m != nil {
return m.FixFirstColumn
@ -388,7 +421,7 @@ type RenamableField struct {
func (m *RenamableField) Reset() { *m = RenamableField{} }
func (m *RenamableField) String() string { return proto.CompactTextString(m) }
func (*RenamableField) ProtoMessage() {}
func (*RenamableField) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{4} }
func (*RenamableField) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{5} }
func (m *RenamableField) GetInternalName() string {
if m != nil {
@ -422,7 +455,7 @@ type Color struct {
func (m *Color) Reset() { *m = Color{} }
func (m *Color) String() string { return proto.CompactTextString(m) }
func (*Color) ProtoMessage() {}
func (*Color) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{5} }
func (*Color) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{6} }
func (m *Color) GetID() string {
if m != nil {
@ -467,7 +500,7 @@ type Legend struct {
func (m *Legend) Reset() { *m = Legend{} }
func (m *Legend) String() string { return proto.CompactTextString(m) }
func (*Legend) ProtoMessage() {}
func (*Legend) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{6} }
func (*Legend) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{7} }
func (m *Legend) GetType() string {
if m != nil {
@ -496,7 +529,7 @@ type Axis struct {
func (m *Axis) Reset() { *m = Axis{} }
func (m *Axis) String() string { return proto.CompactTextString(m) }
func (*Axis) ProtoMessage() {}
func (*Axis) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{7} }
func (*Axis) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{8} }
func (m *Axis) GetLegacyBounds() []int64 {
if m != nil {
@ -559,7 +592,7 @@ type Template struct {
func (m *Template) Reset() { *m = Template{} }
func (m *Template) String() string { return proto.CompactTextString(m) }
func (*Template) ProtoMessage() {}
func (*Template) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{8} }
func (*Template) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{9} }
func (m *Template) GetID() string {
if m != nil {
@ -612,7 +645,7 @@ type TemplateValue struct {
func (m *TemplateValue) Reset() { *m = TemplateValue{} }
func (m *TemplateValue) String() string { return proto.CompactTextString(m) }
func (*TemplateValue) ProtoMessage() {}
func (*TemplateValue) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{9} }
func (*TemplateValue) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{10} }
func (m *TemplateValue) GetType() string {
if m != nil {
@ -647,7 +680,7 @@ type TemplateQuery struct {
func (m *TemplateQuery) Reset() { *m = TemplateQuery{} }
func (m *TemplateQuery) String() string { return proto.CompactTextString(m) }
func (*TemplateQuery) ProtoMessage() {}
func (*TemplateQuery) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{10} }
func (*TemplateQuery) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{11} }
func (m *TemplateQuery) GetCommand() string {
if m != nil {
@ -706,7 +739,7 @@ type Server struct {
func (m *Server) Reset() { *m = Server{} }
func (m *Server) String() string { return proto.CompactTextString(m) }
func (*Server) ProtoMessage() {}
func (*Server) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{11} }
func (*Server) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{12} }
func (m *Server) GetID() int64 {
if m != nil {
@ -782,7 +815,7 @@ type Layout struct {
func (m *Layout) Reset() { *m = Layout{} }
func (m *Layout) String() string { return proto.CompactTextString(m) }
func (*Layout) ProtoMessage() {}
func (*Layout) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{12} }
func (*Layout) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{13} }
func (m *Layout) GetID() string {
if m != nil {
@ -836,7 +869,7 @@ type Cell struct {
func (m *Cell) Reset() { *m = Cell{} }
func (m *Cell) String() string { return proto.CompactTextString(m) }
func (*Cell) ProtoMessage() {}
func (*Cell) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{13} }
func (*Cell) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{14} }
func (m *Cell) GetX() int32 {
if m != nil {
@ -930,7 +963,7 @@ type Query struct {
func (m *Query) Reset() { *m = Query{} }
func (m *Query) String() string { return proto.CompactTextString(m) }
func (*Query) ProtoMessage() {}
func (*Query) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{14} }
func (*Query) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{15} }
func (m *Query) GetCommand() string {
if m != nil {
@ -1004,7 +1037,7 @@ type TimeShift struct {
func (m *TimeShift) Reset() { *m = TimeShift{} }
func (m *TimeShift) String() string { return proto.CompactTextString(m) }
func (*TimeShift) ProtoMessage() {}
func (*TimeShift) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{15} }
func (*TimeShift) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{16} }
func (m *TimeShift) GetLabel() string {
if m != nil {
@ -1035,7 +1068,7 @@ type Range struct {
func (m *Range) Reset() { *m = Range{} }
func (m *Range) String() string { return proto.CompactTextString(m) }
func (*Range) ProtoMessage() {}
func (*Range) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{16} }
func (*Range) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{17} }
func (m *Range) GetUpper() int64 {
if m != nil {
@ -1061,7 +1094,7 @@ type AlertRule struct {
func (m *AlertRule) Reset() { *m = AlertRule{} }
func (m *AlertRule) String() string { return proto.CompactTextString(m) }
func (*AlertRule) ProtoMessage() {}
func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{17} }
func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{18} }
func (m *AlertRule) GetID() string {
if m != nil {
@ -1103,7 +1136,7 @@ type User struct {
func (m *User) Reset() { *m = User{} }
func (m *User) String() string { return proto.CompactTextString(m) }
func (*User) ProtoMessage() {}
func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{18} }
func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{19} }
func (m *User) GetID() uint64 {
if m != nil {
@ -1155,7 +1188,7 @@ type Role struct {
func (m *Role) Reset() { *m = Role{} }
func (m *Role) String() string { return proto.CompactTextString(m) }
func (*Role) ProtoMessage() {}
func (*Role) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{19} }
func (*Role) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{20} }
func (m *Role) GetOrganization() string {
if m != nil {
@ -1182,7 +1215,7 @@ type Mapping struct {
func (m *Mapping) Reset() { *m = Mapping{} }
func (m *Mapping) String() string { return proto.CompactTextString(m) }
func (*Mapping) ProtoMessage() {}
func (*Mapping) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{20} }
func (*Mapping) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{21} }
func (m *Mapping) GetProvider() string {
if m != nil {
@ -1228,7 +1261,7 @@ type Organization struct {
func (m *Organization) Reset() { *m = Organization{} }
func (m *Organization) String() string { return proto.CompactTextString(m) }
func (*Organization) ProtoMessage() {}
func (*Organization) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{21} }
func (*Organization) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{22} }
func (m *Organization) GetID() string {
if m != nil {
@ -1258,7 +1291,7 @@ type Config struct {
func (m *Config) Reset() { *m = Config{} }
func (m *Config) String() string { return proto.CompactTextString(m) }
func (*Config) ProtoMessage() {}
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{22} }
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{23} }
func (m *Config) GetAuth() *AuthConfig {
if m != nil {
@ -1274,7 +1307,7 @@ type AuthConfig struct {
func (m *AuthConfig) Reset() { *m = AuthConfig{} }
func (m *AuthConfig) String() string { return proto.CompactTextString(m) }
func (*AuthConfig) ProtoMessage() {}
func (*AuthConfig) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{23} }
func (*AuthConfig) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{24} }
func (m *AuthConfig) GetSuperAdminNewUsers() bool {
if m != nil {
@ -1291,7 +1324,7 @@ type BuildInfo struct {
func (m *BuildInfo) Reset() { *m = BuildInfo{} }
func (m *BuildInfo) String() string { return proto.CompactTextString(m) }
func (*BuildInfo) ProtoMessage() {}
func (*BuildInfo) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{24} }
func (*BuildInfo) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{25} }
func (m *BuildInfo) GetVersion() string {
if m != nil {
@ -1311,6 +1344,7 @@ func init() {
proto.RegisterType((*Source)(nil), "internal.Source")
proto.RegisterType((*Dashboard)(nil), "internal.Dashboard")
proto.RegisterType((*DashboardCell)(nil), "internal.DashboardCell")
proto.RegisterType((*DecimalPlaces)(nil), "internal.DecimalPlaces")
proto.RegisterType((*TableOptions)(nil), "internal.TableOptions")
proto.RegisterType((*RenamableField)(nil), "internal.RenamableField")
proto.RegisterType((*Color)(nil), "internal.Color")
@ -1338,105 +1372,110 @@ func init() {
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
var fileDescriptorInternal = []byte{
// 1599 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcd, 0x6f, 0xdb, 0xc8,
0x15, 0x07, 0x45, 0x51, 0x12, 0x9f, 0x1c, 0xd7, 0x98, 0x1a, 0x09, 0x9b, 0x16, 0x85, 0x4a, 0xf4,
0x43, 0xfd, 0x88, 0x1b, 0x28, 0x28, 0x10, 0x04, 0x6d, 0x00, 0xd9, 0x6a, 0x52, 0x37, 0x4e, 0xec,
0x8c, 0x6c, 0xf7, 0x54, 0x04, 0x23, 0x69, 0x24, 0x11, 0xa1, 0x48, 0x76, 0x48, 0xda, 0x62, 0xcf,
0x3d, 0xf5, 0x8f, 0x28, 0x50, 0xa0, 0xfd, 0x07, 0x8a, 0x5e, 0xf6, 0xb4, 0xf7, 0xfd, 0x87, 0x76,
0x8f, 0x8b, 0x37, 0x1f, 0x24, 0x65, 0x29, 0x41, 0x16, 0x58, 0xec, 0x6d, 0x7e, 0xef, 0x3d, 0xbd,
0x99, 0xf7, 0xfd, 0x28, 0xd8, 0x0f, 0xa2, 0x8c, 0x8b, 0x88, 0x85, 0x47, 0x89, 0x88, 0xb3, 0x98,
0x74, 0x0c, 0xf6, 0xff, 0x61, 0x43, 0x6b, 0x1c, 0xe7, 0x62, 0xca, 0xc9, 0x3e, 0x34, 0x4e, 0x47,
0x9e, 0xd5, 0xb3, 0xfa, 0x36, 0x6d, 0x9c, 0x8e, 0x08, 0x81, 0xe6, 0x1b, 0xb6, 0xe2, 0x5e, 0xa3,
0x67, 0xf5, 0x5d, 0x2a, 0xcf, 0x48, 0xbb, 0x2c, 0x12, 0xee, 0xd9, 0x8a, 0x86, 0x67, 0xf2, 0x10,
0x3a, 0x57, 0x29, 0x6a, 0x5b, 0x71, 0xaf, 0x29, 0xe9, 0x25, 0x46, 0xde, 0x05, 0x4b, 0xd3, 0xdb,
0x58, 0xcc, 0x3c, 0x47, 0xf1, 0x0c, 0x26, 0x07, 0x60, 0x5f, 0xd1, 0x33, 0xaf, 0x25, 0xc9, 0x78,
0x24, 0x1e, 0xb4, 0x47, 0x7c, 0xce, 0xf2, 0x30, 0xf3, 0xda, 0x3d, 0xab, 0xdf, 0xa1, 0x06, 0xa2,
0x9e, 0x4b, 0x1e, 0xf2, 0x85, 0x60, 0x73, 0xaf, 0xa3, 0xf4, 0x18, 0x4c, 0x8e, 0x80, 0x9c, 0x46,
0x29, 0x9f, 0xe6, 0x82, 0x8f, 0xdf, 0x07, 0xc9, 0x35, 0x17, 0xc1, 0xbc, 0xf0, 0x5c, 0xa9, 0x60,
0x07, 0x07, 0x6f, 0x79, 0xcd, 0x33, 0x86, 0x77, 0x83, 0x54, 0x65, 0x20, 0xf1, 0x61, 0x6f, 0xbc,
0x64, 0x82, 0xcf, 0xc6, 0x7c, 0x2a, 0x78, 0xe6, 0x75, 0x25, 0x7b, 0x83, 0x86, 0x32, 0xe7, 0x62,
0xc1, 0xa2, 0xe0, 0xef, 0x2c, 0x0b, 0xe2, 0xc8, 0xdb, 0x53, 0x32, 0x75, 0x1a, 0x7a, 0x89, 0xc6,
0x21, 0xf7, 0xee, 0x29, 0x2f, 0xe1, 0x99, 0xfc, 0x08, 0x5c, 0x6d, 0x0c, 0xbd, 0xf0, 0xf6, 0x25,
0xa3, 0x22, 0xf8, 0xff, 0xb7, 0xc0, 0x1d, 0xb1, 0x74, 0x39, 0x89, 0x99, 0x98, 0x7d, 0x52, 0x24,
0x1e, 0x81, 0x33, 0xe5, 0x61, 0x98, 0x7a, 0x76, 0xcf, 0xee, 0x77, 0x07, 0x0f, 0x8e, 0xca, 0x10,
0x97, 0x7a, 0x4e, 0x78, 0x18, 0x52, 0x25, 0x45, 0x1e, 0x83, 0x9b, 0xf1, 0x55, 0x12, 0xb2, 0x8c,
0xa7, 0x5e, 0x53, 0xfe, 0x84, 0x54, 0x3f, 0xb9, 0xd4, 0x2c, 0x5a, 0x09, 0x6d, 0x19, 0xea, 0x6c,
0x1b, 0xea, 0x7f, 0x66, 0xc3, 0xbd, 0x8d, 0xeb, 0xc8, 0x1e, 0x58, 0x6b, 0xf9, 0x72, 0x87, 0x5a,
0x6b, 0x44, 0x85, 0x7c, 0xb5, 0x43, 0xad, 0x02, 0xd1, 0xad, 0xcc, 0x1c, 0x87, 0x5a, 0xb7, 0x88,
0x96, 0x32, 0x5f, 0x1c, 0x6a, 0x2d, 0xc9, 0x2f, 0xa1, 0xfd, 0xb7, 0x9c, 0x8b, 0x80, 0xa7, 0x9e,
0x23, 0x5f, 0xf7, 0xbd, 0xea, 0x75, 0x6f, 0x73, 0x2e, 0x0a, 0x6a, 0xf8, 0xe8, 0x0d, 0x99, 0x6b,
0x2a, 0x71, 0xe4, 0x19, 0x69, 0x19, 0xe6, 0x65, 0x5b, 0xd1, 0xf0, 0xac, 0xbd, 0xa8, 0xb2, 0x05,
0xbd, 0xf8, 0x3b, 0x68, 0xb2, 0x35, 0x4f, 0x3d, 0x57, 0xea, 0xff, 0xc9, 0x07, 0x1c, 0x76, 0x34,
0x5c, 0xf3, 0xf4, 0x8f, 0x51, 0x26, 0x0a, 0x2a, 0xc5, 0xc9, 0x2f, 0xa0, 0x35, 0x8d, 0xc3, 0x58,
0xa4, 0x1e, 0xdc, 0x7d, 0xd8, 0x09, 0xd2, 0xa9, 0x66, 0x93, 0x3e, 0xb4, 0x42, 0xbe, 0xe0, 0xd1,
0x4c, 0xe6, 0x4d, 0x77, 0x70, 0x50, 0x09, 0x9e, 0x49, 0x3a, 0xd5, 0x7c, 0xf2, 0x0c, 0xf6, 0x32,
0x36, 0x09, 0xf9, 0x79, 0x82, 0x5e, 0x4c, 0x65, 0x0e, 0x75, 0x07, 0xf7, 0x6b, 0xf1, 0xa8, 0x71,
0xe9, 0x86, 0xec, 0xc3, 0x97, 0xe0, 0x96, 0x2f, 0xc4, 0x12, 0x7a, 0xcf, 0x0b, 0xe9, 0x6f, 0x97,
0xe2, 0x91, 0xfc, 0x14, 0x9c, 0x1b, 0x16, 0xe6, 0x2a, 0x57, 0xba, 0x83, 0xfd, 0x4a, 0xe7, 0x70,
0x1d, 0xa4, 0x54, 0x31, 0x9f, 0x35, 0x9e, 0x5a, 0xfe, 0x3f, 0x1b, 0xb0, 0x57, 0xbf, 0x87, 0xfc,
0x18, 0x20, 0x0b, 0x56, 0xfc, 0x45, 0x2c, 0x56, 0x2c, 0xd3, 0x3a, 0x6b, 0x14, 0xf2, 0x2b, 0x38,
0xb8, 0xe1, 0x22, 0x0b, 0xa6, 0x2c, 0xbc, 0x0c, 0x56, 0x1c, 0xf5, 0xc9, 0x5b, 0x3a, 0x74, 0x8b,
0x4e, 0x1e, 0x43, 0x2b, 0x8d, 0x45, 0x76, 0x5c, 0xc8, 0x78, 0x77, 0x07, 0x5e, 0xf5, 0x0e, 0xca,
0x23, 0xb6, 0xc2, 0x7b, 0x5f, 0x04, 0x3c, 0x9c, 0x51, 0x2d, 0x87, 0x15, 0x7e, 0x2b, 0x58, 0x92,
0x04, 0xd1, 0xc2, 0x74, 0x11, 0x83, 0xc9, 0x53, 0x80, 0x39, 0x0a, 0x63, 0xe2, 0x9b, 0xfc, 0xf8,
0xb0, 0xc6, 0x9a, 0x2c, 0xf9, 0x39, 0xec, 0xcf, 0x83, 0xf5, 0x8b, 0x40, 0xa4, 0xd9, 0x49, 0x1c,
0xe6, 0xab, 0x48, 0x66, 0x4d, 0x87, 0xde, 0xa1, 0xfa, 0x09, 0xec, 0x6f, 0x6a, 0xc1, 0xf4, 0x37,
0x17, 0xc8, 0xda, 0x53, 0xfe, 0xd8, 0xa0, 0x91, 0x1e, 0x74, 0x67, 0x41, 0x9a, 0x84, 0xac, 0xa8,
0x95, 0x67, 0x9d, 0x84, 0xbd, 0xe6, 0x26, 0x48, 0x83, 0x49, 0xa8, 0x5a, 0x66, 0x87, 0x1a, 0xe8,
0x2f, 0xc0, 0x91, 0xe9, 0x53, 0x2b, 0x76, 0xd7, 0x14, 0xbb, 0x6c, 0xb1, 0x8d, 0x5a, 0x8b, 0x3d,
0x00, 0xfb, 0x4f, 0x7c, 0xad, 0xbb, 0x2e, 0x1e, 0xcb, 0x96, 0xd0, 0xac, 0xb5, 0x84, 0x43, 0x70,
0xae, 0x65, 0xec, 0x55, 0xa9, 0x2a, 0xe0, 0x3f, 0x87, 0x96, 0x4a, 0xbf, 0x52, 0xb3, 0x55, 0xd3,
0xdc, 0x83, 0xee, 0xb9, 0x08, 0x78, 0x94, 0xa9, 0x22, 0xd7, 0x26, 0xd4, 0x48, 0xfe, 0xff, 0x2c,
0x68, 0xca, 0x98, 0xfa, 0xb0, 0x17, 0xf2, 0x05, 0x9b, 0x16, 0xc7, 0x71, 0x1e, 0xcd, 0x52, 0xcf,
0xea, 0xd9, 0x7d, 0x9b, 0x6e, 0xd0, 0xc8, 0x7d, 0x68, 0x4d, 0x14, 0xb7, 0xd1, 0xb3, 0xfb, 0x2e,
0xd5, 0x08, 0x9f, 0x16, 0xb2, 0x09, 0x0f, 0xb5, 0x09, 0x0a, 0xa0, 0x74, 0x22, 0xf8, 0x3c, 0x58,
0x6b, 0x33, 0x34, 0x42, 0x7a, 0x9a, 0xcf, 0x91, 0xae, 0x2c, 0xd1, 0x08, 0x0d, 0x98, 0xb0, 0xb4,
0xac, 0x7c, 0x3c, 0xa3, 0xe6, 0x74, 0xca, 0x42, 0x53, 0xfa, 0x0a, 0xf8, 0x9f, 0x5b, 0x38, 0x30,
0x54, 0x2b, 0xdb, 0xf2, 0xf0, 0x0f, 0xa0, 0x83, 0x6d, 0xee, 0xdd, 0x0d, 0x13, 0xda, 0xe0, 0x36,
0xe2, 0x6b, 0x26, 0xc8, 0x6f, 0xa1, 0x25, 0x2b, 0x64, 0x47, 0x5b, 0x35, 0xea, 0xa4, 0x57, 0xa9,
0x16, 0x2b, 0x1b, 0x4f, 0xb3, 0xd6, 0x78, 0x4a, 0x63, 0x9d, 0xba, 0xb1, 0x8f, 0xc0, 0xc1, 0x0e,
0x56, 0xc8, 0xd7, 0xef, 0xd4, 0xac, 0xfa, 0x9c, 0x92, 0xf2, 0xaf, 0xe0, 0xde, 0xc6, 0x8d, 0xe5,
0x4d, 0xd6, 0xe6, 0x4d, 0x55, 0xb5, 0xbb, 0xba, 0xba, 0xb1, 0x94, 0x52, 0x1e, 0xf2, 0x69, 0xc6,
0x67, 0x3a, 0xeb, 0x4a, 0xec, 0xff, 0xdb, 0xaa, 0xf4, 0xca, 0xfb, 0x30, 0x45, 0xa7, 0xf1, 0x6a,
0xc5, 0xa2, 0x99, 0x56, 0x6d, 0x20, 0xfa, 0x6d, 0x36, 0xd1, 0xaa, 0x1b, 0xb3, 0x09, 0x62, 0x91,
0xe8, 0x08, 0x36, 0x44, 0x82, 0xb9, 0xb3, 0xe2, 0x2c, 0xcd, 0x05, 0x5f, 0xf1, 0x28, 0xd3, 0x2e,
0xa8, 0x93, 0xc8, 0x03, 0x68, 0x67, 0x6c, 0xf1, 0x0e, 0x7b, 0x94, 0x8e, 0x64, 0xc6, 0x16, 0xaf,
0x78, 0x41, 0x7e, 0x08, 0xae, 0xac, 0x52, 0xc9, 0x52, 0xe1, 0xec, 0x48, 0xc2, 0x2b, 0x5e, 0xf8,
0x5f, 0x59, 0xd0, 0x1a, 0x73, 0x71, 0xc3, 0xc5, 0x27, 0x4d, 0xc2, 0xfa, 0xfe, 0x61, 0x7f, 0x64,
0xff, 0x68, 0xee, 0xde, 0x3f, 0x9c, 0x6a, 0xff, 0x38, 0x04, 0x67, 0x2c, 0xa6, 0xa7, 0x23, 0xf9,
0x22, 0x9b, 0x2a, 0x80, 0xd9, 0x38, 0x9c, 0x66, 0xc1, 0x0d, 0xd7, 0x4b, 0x89, 0x46, 0x5b, 0x03,
0xb2, 0xb3, 0x63, 0x13, 0xf8, 0x86, 0xbb, 0x89, 0xff, 0x2f, 0x0b, 0x5a, 0x67, 0xac, 0x88, 0xf3,
0x6c, 0x2b, 0x6b, 0x7b, 0xd0, 0x1d, 0x26, 0x49, 0x18, 0x4c, 0x37, 0x2a, 0xb5, 0x46, 0x42, 0x89,
0xd7, 0xb5, 0x78, 0x28, 0x5f, 0xd4, 0x49, 0x38, 0x1d, 0x4e, 0xe4, 0xd2, 0xa0, 0x36, 0x80, 0xda,
0x74, 0x50, 0xbb, 0x82, 0x64, 0xa2, 0xd3, 0x86, 0x79, 0x16, 0xcf, 0xc3, 0xf8, 0x56, 0x7a, 0xa7,
0x43, 0x4b, 0xec, 0x7f, 0xd1, 0x80, 0xe6, 0x77, 0x35, 0xe8, 0xf7, 0xc0, 0x0a, 0x74, 0x72, 0x58,
0x41, 0x39, 0xf6, 0xdb, 0xb5, 0xb1, 0xef, 0x41, 0xbb, 0x10, 0x2c, 0x5a, 0xf0, 0xd4, 0xeb, 0xc8,
0x6e, 0x64, 0xa0, 0xe4, 0xc8, 0xba, 0x53, 0xf3, 0xde, 0xa5, 0x06, 0x96, 0x75, 0x04, 0xb5, 0x3a,
0xfa, 0x8d, 0x5e, 0x0d, 0xba, 0x77, 0x47, 0xcb, 0xae, 0x8d, 0xe0, 0xdb, 0x1b, 0xc1, 0x5f, 0x5a,
0xe0, 0x94, 0x45, 0x78, 0xb2, 0x59, 0x84, 0x27, 0x55, 0x11, 0x8e, 0x8e, 0x4d, 0x11, 0x8e, 0x8e,
0x11, 0xd3, 0x0b, 0x53, 0x84, 0xf4, 0x02, 0x83, 0xf5, 0x52, 0xc4, 0x79, 0x72, 0x5c, 0xa8, 0xa8,
0xba, 0xb4, 0xc4, 0x98, 0xb9, 0x7f, 0x59, 0x72, 0xa1, 0x5d, 0xed, 0x52, 0x8d, 0x30, 0xcf, 0xcf,
0x64, 0x83, 0x52, 0xce, 0x55, 0x80, 0xfc, 0x0c, 0x1c, 0x8a, 0xce, 0x93, 0x1e, 0xde, 0x88, 0x8b,
0x24, 0x53, 0xc5, 0x45, 0xa5, 0xea, 0x83, 0x41, 0x27, 0xbc, 0xf9, 0x7c, 0xf8, 0x35, 0xb4, 0xc6,
0xcb, 0x60, 0x9e, 0x99, 0x05, 0xeb, 0xfb, 0xb5, 0x06, 0x17, 0xac, 0xb8, 0xe4, 0x51, 0x2d, 0xe2,
0xbf, 0x05, 0xb7, 0x24, 0x56, 0xcf, 0xb1, 0xea, 0xcf, 0x21, 0xd0, 0xbc, 0x8a, 0x82, 0xcc, 0x94,
0x3a, 0x9e, 0xd1, 0xd8, 0xb7, 0x39, 0x8b, 0xb2, 0x20, 0x2b, 0x4c, 0xa9, 0x1b, 0xec, 0x3f, 0xd1,
0xcf, 0x47, 0x75, 0x57, 0x49, 0xc2, 0x85, 0x6e, 0x1b, 0x0a, 0xc8, 0x4b, 0xe2, 0x5b, 0xae, 0x3a,
0xbe, 0x4d, 0x15, 0xf0, 0xff, 0x0a, 0xee, 0x30, 0xe4, 0x22, 0xa3, 0x79, 0xc8, 0x77, 0x4d, 0xe2,
0x3f, 0x8f, 0xcf, 0xdf, 0x98, 0x17, 0xe0, 0xb9, 0x6a, 0x11, 0xf6, 0x9d, 0x16, 0xf1, 0x8a, 0x25,
0xec, 0x74, 0x24, 0xf3, 0xdc, 0xa6, 0x1a, 0xf9, 0xff, 0xb1, 0xa0, 0x89, 0xbd, 0xa8, 0xa6, 0xba,
0xf9, 0xb1, 0x3e, 0x76, 0x21, 0xe2, 0x9b, 0x60, 0xc6, 0x85, 0x31, 0xce, 0x60, 0xe9, 0xf4, 0xe9,
0x92, 0x97, 0x03, 0x5f, 0x23, 0xcc, 0x35, 0xfc, 0xba, 0x30, 0xb5, 0x54, 0xcb, 0x35, 0x24, 0x53,
0xc5, 0xc4, 0xcd, 0x6e, 0x9c, 0x27, 0x5c, 0x0c, 0x67, 0xab, 0xc0, 0x6c, 0x40, 0x35, 0x8a, 0xff,
0x5c, 0x7d, 0xaf, 0x6c, 0x75, 0x34, 0x6b, 0xf7, 0xb7, 0xcd, 0xdd, 0x97, 0xfb, 0xff, 0xb5, 0xa0,
0xfd, 0x5a, 0xef, 0x6a, 0x75, 0x2b, 0xac, 0x0f, 0x5a, 0xd1, 0xd8, 0xb0, 0x62, 0x00, 0x87, 0x46,
0x66, 0xe3, 0x7e, 0xe5, 0x85, 0x9d, 0x3c, 0xed, 0xd1, 0x66, 0x19, 0xac, 0x4f, 0xf9, 0x5c, 0xb9,
0xdc, 0x94, 0xd9, 0x15, 0xf0, 0xad, 0xa8, 0xf4, 0xa0, 0x6b, 0x3e, 0xd3, 0xe2, 0xd0, 0x0c, 0x98,
0x3a, 0xc9, 0x1f, 0x40, 0xeb, 0x24, 0x8e, 0xe6, 0xc1, 0x82, 0xf4, 0xa1, 0x39, 0xcc, 0xb3, 0xa5,
0xd4, 0xd8, 0x1d, 0x1c, 0xd6, 0x0a, 0x3f, 0xcf, 0x96, 0x4a, 0x86, 0x4a, 0x09, 0xff, 0xf7, 0x00,
0x15, 0x0d, 0xa7, 0x44, 0x15, 0x8d, 0x37, 0xfc, 0x16, 0x53, 0x26, 0x95, 0x5a, 0x3a, 0x74, 0x07,
0xc7, 0xff, 0x03, 0xb8, 0xc7, 0x79, 0x10, 0xce, 0x4e, 0xa3, 0x79, 0x8c, 0xad, 0xe3, 0x9a, 0x8b,
0xb4, 0x8a, 0x97, 0x81, 0xe8, 0x6e, 0xec, 0x22, 0x65, 0x0d, 0x69, 0x34, 0x69, 0xc9, 0x3f, 0x01,
0x9e, 0x7c, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xb6, 0xb2, 0x98, 0x60, 0x16, 0x10, 0x00, 0x00,
// 1667 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x5b, 0x6f, 0xe4, 0x48,
0x15, 0x96, 0xbb, 0xed, 0x4e, 0xfb, 0x74, 0x92, 0x8d, 0x8a, 0xd1, 0xae, 0x59, 0x10, 0x6a, 0x2c,
0x2e, 0xe1, 0xb2, 0xc3, 0x2a, 0x2b, 0x24, 0xb4, 0xda, 0x5d, 0x29, 0x97, 0x9d, 0x21, 0x73, 0xcd,
0x54, 0x27, 0xc3, 0x13, 0x5a, 0x55, 0xdb, 0xd5, 0xdd, 0xa5, 0x75, 0xdb, 0xa6, 0x6c, 0x27, 0x31,
0xcf, 0xfc, 0x0e, 0x24, 0x24, 0xf8, 0x03, 0x88, 0x47, 0x24, 0xde, 0xf9, 0x01, 0xfc, 0x15, 0x78,
0x44, 0xa7, 0x2e, 0xee, 0x72, 0xd2, 0x33, 0x1a, 0x24, 0xb4, 0x6f, 0xf5, 0x9d, 0x73, 0xfa, 0x54,
0xd5, 0xb9, 0x7c, 0x75, 0xdc, 0xb0, 0x2f, 0xf2, 0x9a, 0xcb, 0x9c, 0x65, 0x0f, 0x4b, 0x59, 0xd4,
0x05, 0x19, 0x5b, 0x1c, 0xff, 0x61, 0x08, 0xa3, 0x59, 0xd1, 0xc8, 0x84, 0x93, 0x7d, 0x18, 0x9c,
0x9f, 0x45, 0xde, 0xd4, 0x3b, 0x1c, 0xd2, 0xc1, 0xf9, 0x19, 0x21, 0xe0, 0xbf, 0x60, 0x6b, 0x1e,
0x0d, 0xa6, 0xde, 0x61, 0x48, 0xd5, 0x1a, 0x65, 0x97, 0x6d, 0xc9, 0xa3, 0xa1, 0x96, 0xe1, 0x9a,
0x7c, 0x08, 0xe3, 0xab, 0x0a, 0xbd, 0xad, 0x79, 0xe4, 0x2b, 0x79, 0x87, 0x51, 0x77, 0xc1, 0xaa,
0xea, 0xa6, 0x90, 0x69, 0x14, 0x68, 0x9d, 0xc5, 0xe4, 0x00, 0x86, 0x57, 0xf4, 0x59, 0x34, 0x52,
0x62, 0x5c, 0x92, 0x08, 0x76, 0xce, 0xf8, 0x82, 0x35, 0x59, 0x1d, 0xed, 0x4c, 0xbd, 0xc3, 0x31,
0xb5, 0x10, 0xfd, 0x5c, 0xf2, 0x8c, 0x2f, 0x25, 0x5b, 0x44, 0x63, 0xed, 0xc7, 0x62, 0xf2, 0x10,
0xc8, 0x79, 0x5e, 0xf1, 0xa4, 0x91, 0x7c, 0xf6, 0xb5, 0x28, 0x5f, 0x73, 0x29, 0x16, 0x6d, 0x14,
0x2a, 0x07, 0x5b, 0x34, 0xb8, 0xcb, 0x73, 0x5e, 0x33, 0xdc, 0x1b, 0x94, 0x2b, 0x0b, 0x49, 0x0c,
0xbb, 0xb3, 0x15, 0x93, 0x3c, 0x9d, 0xf1, 0x44, 0xf2, 0x3a, 0x9a, 0x28, 0x75, 0x4f, 0x86, 0x36,
0x2f, 0xe5, 0x92, 0xe5, 0xe2, 0xf7, 0xac, 0x16, 0x45, 0x1e, 0xed, 0x6a, 0x1b, 0x57, 0x86, 0x51,
0xa2, 0x45, 0xc6, 0xa3, 0x3d, 0x1d, 0x25, 0x5c, 0x93, 0xef, 0x42, 0x68, 0x2e, 0x43, 0x2f, 0xa2,
0x7d, 0xa5, 0xd8, 0x08, 0xe2, 0xbf, 0x79, 0x10, 0x9e, 0xb1, 0x6a, 0x35, 0x2f, 0x98, 0x4c, 0xdf,
0x29, 0x13, 0x1f, 0x41, 0x90, 0xf0, 0x2c, 0xab, 0xa2, 0xe1, 0x74, 0x78, 0x38, 0x39, 0xfa, 0xe0,
0x61, 0x97, 0xe2, 0xce, 0xcf, 0x29, 0xcf, 0x32, 0xaa, 0xad, 0xc8, 0xc7, 0x10, 0xd6, 0x7c, 0x5d,
0x66, 0xac, 0xe6, 0x55, 0xe4, 0xab, 0x9f, 0x90, 0xcd, 0x4f, 0x2e, 0x8d, 0x8a, 0x6e, 0x8c, 0xee,
0x5d, 0x34, 0xb8, 0x7f, 0xd1, 0xf8, 0x5f, 0x3e, 0xec, 0xf5, 0xb6, 0x23, 0xbb, 0xe0, 0xdd, 0xaa,
0x93, 0x07, 0xd4, 0xbb, 0x45, 0xd4, 0xaa, 0x53, 0x07, 0xd4, 0x6b, 0x11, 0xdd, 0xa8, 0xca, 0x09,
0xa8, 0x77, 0x83, 0x68, 0xa5, 0xea, 0x25, 0xa0, 0xde, 0x8a, 0xfc, 0x04, 0x76, 0x7e, 0xd7, 0x70,
0x29, 0x78, 0x15, 0x05, 0xea, 0x74, 0xef, 0x6d, 0x4e, 0xf7, 0xaa, 0xe1, 0xb2, 0xa5, 0x56, 0x8f,
0xd1, 0x50, 0xb5, 0xa6, 0x0b, 0x47, 0xad, 0x51, 0x56, 0x63, 0x5d, 0xee, 0x68, 0x19, 0xae, 0x4d,
0x14, 0x75, 0xb5, 0x60, 0x14, 0x7f, 0x09, 0x3e, 0xbb, 0xe5, 0x55, 0x14, 0x2a, 0xff, 0xdf, 0x7f,
0x43, 0xc0, 0x1e, 0x1e, 0xdf, 0xf2, 0xea, 0xcb, 0xbc, 0x96, 0x2d, 0x55, 0xe6, 0xe4, 0xc7, 0x30,
0x4a, 0x8a, 0xac, 0x90, 0x55, 0x04, 0x77, 0x0f, 0x76, 0x8a, 0x72, 0x6a, 0xd4, 0xe4, 0x10, 0x46,
0x19, 0x5f, 0xf2, 0x3c, 0x55, 0x75, 0x33, 0x39, 0x3a, 0xd8, 0x18, 0x3e, 0x53, 0x72, 0x6a, 0xf4,
0xe4, 0x53, 0xd8, 0xad, 0xd9, 0x3c, 0xe3, 0x2f, 0x4b, 0x8c, 0x62, 0xa5, 0x6a, 0x68, 0x72, 0xf4,
0xbe, 0x93, 0x0f, 0x47, 0x4b, 0x7b, 0xb6, 0xe4, 0x33, 0xd8, 0x5d, 0x08, 0x9e, 0xa5, 0xf6, 0xb7,
0x7b, 0xea, 0x50, 0xd1, 0xe6, 0xb7, 0x94, 0xe7, 0x6c, 0x8d, 0xbf, 0x78, 0x84, 0x66, 0xb4, 0x67,
0x4d, 0xbe, 0x07, 0x50, 0x8b, 0x35, 0x7f, 0x54, 0xc8, 0x35, 0xab, 0x4d, 0x19, 0x3a, 0x12, 0xf2,
0x39, 0xec, 0xa5, 0x3c, 0x11, 0x6b, 0x96, 0x5d, 0x64, 0x2c, 0xe1, 0x55, 0xf4, 0x9e, 0x3a, 0x9a,
0x5b, 0x5d, 0xae, 0x9a, 0xf6, 0xad, 0x3f, 0x7c, 0x0c, 0x61, 0x17, 0x3e, 0xec, 0xef, 0xaf, 0x79,
0xab, 0x8a, 0x21, 0xa4, 0xb8, 0x24, 0x3f, 0x80, 0xe0, 0x9a, 0x65, 0x8d, 0x2e, 0xe4, 0xc9, 0xd1,
0xfe, 0xc6, 0xeb, 0xf1, 0xad, 0xa8, 0xa8, 0x56, 0x7e, 0x3a, 0xf8, 0x95, 0x17, 0x3f, 0x86, 0xbd,
0xde, 0x46, 0x78, 0x70, 0x51, 0x7d, 0x99, 0x2f, 0x0a, 0x99, 0xf0, 0x54, 0xf9, 0x1c, 0x53, 0x47,
0x42, 0xde, 0x87, 0x51, 0x2a, 0x96, 0xa2, 0xae, 0x4c, 0xb9, 0x19, 0x14, 0xff, 0xdd, 0x83, 0x5d,
0x37, 0x9a, 0xe4, 0xa7, 0x70, 0x70, 0xcd, 0x65, 0x2d, 0x12, 0x96, 0x5d, 0x8a, 0x35, 0xc7, 0x8d,
0xd5, 0x4f, 0xc6, 0xf4, 0x9e, 0x9c, 0x7c, 0x0c, 0xa3, 0xaa, 0x90, 0xf5, 0x49, 0xab, 0xaa, 0xf6,
0x6d, 0x51, 0x36, 0x76, 0xc8, 0x53, 0x37, 0x92, 0x95, 0xa5, 0xc8, 0x97, 0x96, 0x0b, 0x2d, 0x26,
0x3f, 0x82, 0xfd, 0x85, 0xb8, 0x7d, 0x24, 0x64, 0x55, 0x9f, 0x16, 0x59, 0xb3, 0xce, 0x55, 0x05,
0x8f, 0xe9, 0x1d, 0xe9, 0x13, 0x7f, 0xec, 0x1d, 0x0c, 0x9e, 0xf8, 0xe3, 0xe0, 0x60, 0x14, 0x97,
0xb0, 0xdf, 0xdf, 0x09, 0xdb, 0xd2, 0x1e, 0x42, 0x71, 0x82, 0x0e, 0x6f, 0x4f, 0x46, 0xa6, 0x30,
0x49, 0x45, 0x55, 0x66, 0xac, 0x75, 0x68, 0xc3, 0x15, 0x21, 0x07, 0x5e, 0x8b, 0x4a, 0xcc, 0x33,
0x4d, 0xe5, 0x63, 0x6a, 0x61, 0xbc, 0x84, 0x40, 0x95, 0xb5, 0x43, 0x42, 0xa1, 0x25, 0x21, 0x45,
0xfd, 0x03, 0x87, 0xfa, 0x0f, 0x60, 0xf8, 0x6b, 0x7e, 0x6b, 0x5e, 0x03, 0x5c, 0x76, 0x54, 0xe5,
0x3b, 0x54, 0xf5, 0x00, 0x82, 0xd7, 0x2a, 0xed, 0x9a, 0x42, 0x34, 0x88, 0xbf, 0x80, 0x91, 0x6e,
0x8b, 0xce, 0xb3, 0xe7, 0x78, 0x9e, 0xc2, 0xe4, 0xa5, 0x14, 0x3c, 0xaf, 0x35, 0xf9, 0x98, 0x2b,
0x38, 0xa2, 0xf8, 0xaf, 0x1e, 0xf8, 0x2a, 0x4b, 0x31, 0xec, 0x66, 0x7c, 0xc9, 0x92, 0xf6, 0xa4,
0x68, 0xf2, 0xb4, 0x8a, 0xbc, 0xe9, 0xf0, 0x70, 0x48, 0x7b, 0x32, 0x2c, 0x8f, 0xb9, 0xd6, 0x0e,
0xa6, 0xc3, 0xc3, 0x90, 0x1a, 0x84, 0x47, 0xcb, 0xd8, 0x9c, 0x67, 0xe6, 0x0a, 0x1a, 0xa0, 0x75,
0x29, 0xf9, 0x42, 0xdc, 0x9a, 0x6b, 0x18, 0x84, 0xf2, 0xaa, 0x59, 0xa0, 0x5c, 0xdf, 0xc4, 0x20,
0xbc, 0xc0, 0x9c, 0x55, 0x1d, 0x23, 0xe1, 0x1a, 0x3d, 0x57, 0x09, 0xcb, 0x2c, 0x25, 0x69, 0x10,
0xff, 0xc3, 0xc3, 0x87, 0x4c, 0x53, 0xec, 0xbd, 0x08, 0x7f, 0x1b, 0xc6, 0x48, 0xbf, 0x5f, 0x5d,
0x33, 0x69, 0x2e, 0xbc, 0x83, 0xf8, 0x35, 0x93, 0xe4, 0x17, 0x30, 0x52, 0xcd, 0xb1, 0x85, 0xee,
0xad, 0x3b, 0x15, 0x55, 0x6a, 0xcc, 0x3a, 0x42, 0xf4, 0x1d, 0x42, 0xec, 0x2e, 0x1b, 0xb8, 0x97,
0xfd, 0x08, 0x02, 0x64, 0xd6, 0x56, 0x9d, 0x7e, 0xab, 0x67, 0xcd, 0xbf, 0xda, 0x2a, 0xbe, 0x82,
0xbd, 0xde, 0x8e, 0xdd, 0x4e, 0x5e, 0x7f, 0xa7, 0x4d, 0xa3, 0x87, 0xa6, 0xb1, 0xb1, 0x39, 0x2a,
0x9e, 0xf1, 0xa4, 0xe6, 0xa9, 0xa9, 0xba, 0x0e, 0xc7, 0x7f, 0xf2, 0x36, 0x7e, 0xd5, 0x7e, 0x58,
0xa2, 0x49, 0xb1, 0x5e, 0xb3, 0x3c, 0x35, 0xae, 0x2d, 0xc4, 0xb8, 0xa5, 0x73, 0xe3, 0x7a, 0x90,
0xce, 0x11, 0xcb, 0xd2, 0x64, 0x70, 0x20, 0x4b, 0xac, 0x9d, 0x35, 0x67, 0x55, 0x23, 0xf9, 0x9a,
0xe7, 0xb5, 0x09, 0x81, 0x2b, 0x22, 0x1f, 0xc0, 0x4e, 0xcd, 0x96, 0x5f, 0x21, 0x3d, 0x99, 0x4c,
0xd6, 0x6c, 0xf9, 0x94, 0xb7, 0xe4, 0x3b, 0x10, 0x2a, 0xbe, 0x54, 0x2a, 0x9d, 0xce, 0xb1, 0x12,
0x3c, 0xe5, 0x6d, 0xfc, 0x1f, 0x0f, 0x46, 0x33, 0x2e, 0xaf, 0xb9, 0x7c, 0xa7, 0x17, 0xda, 0x9d,
0x8b, 0x86, 0x6f, 0x99, 0x8b, 0xfc, 0xed, 0x73, 0x51, 0xb0, 0x99, 0x8b, 0x1e, 0x40, 0x30, 0x93,
0xc9, 0xf9, 0x99, 0x3a, 0xd1, 0x90, 0x6a, 0x80, 0xd5, 0x78, 0x9c, 0xd4, 0xe2, 0x9a, 0x9b, 0x61,
0xc9, 0xa0, 0x7b, 0x0f, 0xf7, 0x78, 0xcb, 0x84, 0xf2, 0x3f, 0xce, 0x4c, 0xf1, 0x1f, 0x3d, 0x18,
0x3d, 0x63, 0x6d, 0xd1, 0xd4, 0xf7, 0xaa, 0x76, 0x0a, 0x93, 0xe3, 0xb2, 0xcc, 0x44, 0xd2, 0xeb,
0x54, 0x47, 0x84, 0x16, 0xcf, 0x9d, 0x7c, 0xe8, 0x58, 0xb8, 0x22, 0x7c, 0x18, 0x4e, 0xd5, 0x30,
0xa3, 0x27, 0x13, 0xe7, 0x61, 0xd0, 0x33, 0x8c, 0x52, 0x62, 0xd0, 0x8e, 0x9b, 0xba, 0x58, 0x64,
0xc5, 0x8d, 0x8a, 0xce, 0x98, 0x76, 0x38, 0xfe, 0xe7, 0x00, 0xfc, 0x6f, 0x6a, 0x00, 0xd9, 0x05,
0x4f, 0x98, 0xe2, 0xf0, 0x44, 0x37, 0x8e, 0xec, 0x38, 0xe3, 0x48, 0x04, 0x3b, 0xad, 0x64, 0xf9,
0x92, 0x57, 0xd1, 0x58, 0xb1, 0x91, 0x85, 0x4a, 0xa3, 0xfa, 0x4e, 0xcf, 0x21, 0x21, 0xb5, 0xb0,
0xeb, 0x23, 0x70, 0xfa, 0xe8, 0xe7, 0x66, 0x64, 0x99, 0xdc, 0x7d, 0xe4, 0xb7, 0x4d, 0x2a, 0xff,
0xbf, 0xd7, 0xf7, 0xdf, 0x1e, 0x04, 0x5d, 0x13, 0x9e, 0xf6, 0x9b, 0xf0, 0x74, 0xd3, 0x84, 0x67,
0x27, 0xb6, 0x09, 0xcf, 0x4e, 0x10, 0xd3, 0x0b, 0xdb, 0x84, 0xf4, 0x02, 0x93, 0xf5, 0x58, 0x16,
0x4d, 0x79, 0xd2, 0xea, 0xac, 0x86, 0xb4, 0xc3, 0x58, 0xb9, 0xbf, 0x59, 0x71, 0x69, 0x42, 0x1d,
0x52, 0x83, 0xb0, 0xce, 0x9f, 0x29, 0x82, 0xd2, 0xc1, 0xd5, 0x80, 0xfc, 0x10, 0x02, 0x8a, 0xc1,
0x53, 0x11, 0xee, 0xe5, 0x45, 0x89, 0xa9, 0xd6, 0xa2, 0x53, 0xfd, 0x21, 0x63, 0x0a, 0xde, 0x7e,
0xd6, 0xfc, 0x0c, 0x46, 0xb3, 0x95, 0x58, 0xd4, 0x76, 0xf0, 0xfb, 0x96, 0x43, 0x70, 0x62, 0xcd,
0x95, 0x8e, 0x1a, 0x93, 0xf8, 0x15, 0x84, 0x9d, 0x70, 0x73, 0x1c, 0xcf, 0x3d, 0x0e, 0x01, 0xff,
0x2a, 0x17, 0xb5, 0x6d, 0x75, 0x5c, 0xe3, 0x65, 0x5f, 0x35, 0x2c, 0xaf, 0x45, 0xdd, 0xda, 0x56,
0xb7, 0x38, 0xfe, 0xc4, 0x1c, 0x1f, 0xdd, 0x5d, 0x95, 0x25, 0x97, 0x86, 0x36, 0x34, 0x50, 0x9b,
0x14, 0x37, 0x5c, 0x33, 0xfe, 0x90, 0x6a, 0x10, 0xff, 0x16, 0xc2, 0xe3, 0x8c, 0xcb, 0x9a, 0x36,
0x19, 0xdf, 0xf6, 0x12, 0x3f, 0x99, 0xbd, 0x7c, 0x61, 0x4f, 0x80, 0xeb, 0x0d, 0x45, 0x0c, 0xef,
0x50, 0xc4, 0x53, 0x56, 0xb2, 0xf3, 0x33, 0x55, 0xe7, 0x43, 0x6a, 0x50, 0xfc, 0x67, 0x0f, 0x7c,
0xe4, 0x22, 0xc7, 0xb5, 0xff, 0x36, 0x1e, 0xbb, 0x90, 0xc5, 0xb5, 0x48, 0xb9, 0xb4, 0x97, 0xb3,
0x58, 0x05, 0x3d, 0x59, 0xf1, 0xee, 0xc1, 0x37, 0x08, 0x6b, 0x0d, 0xbf, 0x7a, 0x6c, 0x2f, 0x39,
0xb5, 0x86, 0x62, 0xaa, 0x95, 0x38, 0xd4, 0xcd, 0x9a, 0x92, 0xcb, 0xe3, 0x74, 0x2d, 0xec, 0x34,
0xe4, 0x48, 0xe2, 0x2f, 0xf4, 0x77, 0xd4, 0x3d, 0x46, 0xf3, 0xb6, 0x7f, 0x73, 0xdd, 0x3d, 0x79,
0xfc, 0x17, 0x0f, 0x76, 0x9e, 0x9b, 0xe9, 0xcb, 0xbd, 0x85, 0xf7, 0xc6, 0x5b, 0x0c, 0x7a, 0xb7,
0x38, 0x82, 0x07, 0xd6, 0xa6, 0xb7, 0xbf, 0x8e, 0xc2, 0x56, 0x9d, 0x89, 0xa8, 0xdf, 0x25, 0xeb,
0x5d, 0x3e, 0xa3, 0x2e, 0xfb, 0x36, 0xdb, 0x12, 0x7e, 0x2f, 0x2b, 0x53, 0x98, 0xd8, 0xcf, 0xc7,
0x22, 0xb3, 0x0f, 0x8c, 0x2b, 0x8a, 0x8f, 0x60, 0x74, 0x5a, 0xe4, 0x0b, 0xb1, 0x24, 0x87, 0xe0,
0x1f, 0x37, 0xf5, 0x4a, 0x79, 0x9c, 0x1c, 0x3d, 0x70, 0x1a, 0xbf, 0xa9, 0x57, 0xda, 0x86, 0x2a,
0x8b, 0xf8, 0x33, 0x80, 0x8d, 0x0c, 0x5f, 0x89, 0x4d, 0x36, 0x5e, 0xf0, 0x1b, 0x2c, 0x99, 0xca,
0x0c, 0xdf, 0x5b, 0x34, 0xf1, 0xe7, 0x10, 0x9e, 0x34, 0x22, 0x4b, 0xcf, 0xf3, 0x45, 0x81, 0xd4,
0xf1, 0x9a, 0xcb, 0x6a, 0x93, 0x2f, 0x0b, 0x31, 0xdc, 0xc8, 0x22, 0x5d, 0x0f, 0x19, 0x34, 0x1f,
0xa9, 0x3f, 0x27, 0x3e, 0xf9, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x77, 0xc4, 0xaa, 0x16, 0xae,
0x10, 0x00, 0x00,
}

View File

@ -27,33 +27,41 @@ message Dashboard {
}
message DashboardCell {
int32 x = 1; // X-coordinate of Cell in the Dashboard
int32 y = 2; // Y-coordinate of Cell in the Dashboard
int32 w = 3; // Width of Cell in the Dashboard
int32 h = 4; // Height of Cell in the Dashboard
repeated Query queries = 5; // Time-series data queries for Dashboard
string name = 6; // User-facing name for this Dashboard
string type = 7; // Dashboard visualization type
string ID = 8; // id is the unique id of the dashboard. MIGRATED FIELD added in 1.2.0-beta6
map<string, Axis> axes = 9; // Axes represent the graphical viewport for a cell's visualizations
repeated Color colors = 10; // Colors represent encoding data values to color
Legend legend = 11; // Legend is summary information for a cell
TableOptions tableOptions = 12; // TableOptions for visualization of cell with type 'table'
int32 x = 1; // X-coordinate of Cell in the Dashboard
int32 y = 2; // Y-coordinate of Cell in the Dashboard
int32 w = 3; // Width of Cell in the Dashboard
int32 h = 4; // Height of Cell in the Dashboard
repeated Query queries = 5; // Time-series data queries for Dashboard
string name = 6; // User-facing name for this Dashboard
string type = 7; // Dashboard visualization type
string ID = 8; // id is the unique id of the dashboard. MIGRATED FIELD added in 1.2.0-beta6
map<string, Axis> axes = 9; // Axes represent the graphical viewport for a cell's visualizations
repeated Color colors = 10; // Colors represent encoding data values to color
Legend legend = 11; // Legend is summary information for a cell
TableOptions tableOptions = 12; // TableOptions for visualization of cell with type 'table'
repeated RenamableField fieldOptions = 13; // Options for each of the fields returned in a cell
string timeFormat = 14; // format for time
DecimalPlaces decimalPlaces = 15; // Represents how precise the values of this field should be
}
message DecimalPlaces {
bool isEnforced = 1; // whether decimal places should be enforced
int32 digits = 2; // the number of digits to display after decical point
}
message TableOptions {
string timeFormat = 1; // format for time
reserved 1;
bool verticalTimeAxis = 2; // time axis should be a column not row
RenamableField sortBy = 3; // which column should a table be sorted by
string wrapping = 4; // option for text wrapping
repeated RenamableField fieldNames = 5; // names and renames for column/row fields
reserved 5;
bool fixFirstColumn = 6; // first column should be fixed/frozen
}
message RenamableField {
string internalName = 1; // name of column
string displayName = 2; // what column is renamed to
bool visible = 3; // Represents whether RenamableField is visible
bool visible = 3; // Represents whether RenamableField is visible
}
message Color {

View File

@ -194,10 +194,9 @@ func Test_MarshalDashboard(t *testing.T) {
Value: "100",
},
},
TableOptions: chronograf.TableOptions{
TimeFormat: "",
FieldNames: []chronograf.RenamableField{},
},
TableOptions: chronograf.TableOptions{},
FieldOptions: []chronograf.RenamableField{},
TimeFormat: "",
},
},
Templates: []chronograf.Template{},
@ -260,10 +259,10 @@ func Test_MarshalDashboard_WithLegacyBounds(t *testing.T) {
Type: "static",
Orientation: "bottom",
},
TableOptions: chronograf.TableOptions{
TimeFormat: "MM:DD:YYYY",
},
Type: "line",
TableOptions: chronograf.TableOptions{},
TimeFormat: "MM:DD:YYYY",
FieldOptions: []chronograf.RenamableField{},
Type: "line",
},
},
Templates: []chronograf.Template{},
@ -317,11 +316,10 @@ func Test_MarshalDashboard_WithLegacyBounds(t *testing.T) {
Type: "static",
Orientation: "bottom",
},
TableOptions: chronograf.TableOptions{
TimeFormat: "MM:DD:YYYY",
FieldNames: []chronograf.RenamableField{},
},
Type: "line",
TableOptions: chronograf.TableOptions{},
FieldOptions: []chronograf.RenamableField{},
TimeFormat: "MM:DD:YYYY",
Type: "line",
},
},
Templates: []chronograf.Template{},
@ -380,10 +378,10 @@ func Test_MarshalDashboard_WithEmptyLegacyBounds(t *testing.T) {
Value: "100",
},
},
Type: "line",
TableOptions: chronograf.TableOptions{
TimeFormat: "MM:DD:YYYY",
},
Type: "line",
TableOptions: chronograf.TableOptions{},
FieldOptions: []chronograf.RenamableField{},
TimeFormat: "MM:DD:YYYY",
},
},
Templates: []chronograf.Template{},
@ -433,11 +431,10 @@ func Test_MarshalDashboard_WithEmptyLegacyBounds(t *testing.T) {
Value: "100",
},
},
TableOptions: chronograf.TableOptions{
TimeFormat: "MM:DD:YYYY",
FieldNames: []chronograf.RenamableField{},
},
Type: "line",
TableOptions: chronograf.TableOptions{},
FieldOptions: []chronograf.RenamableField{},
TimeFormat: "MM:DD:YYYY",
Type: "line",
},
},
Templates: []chronograf.Template{},
@ -468,14 +465,13 @@ func Test_MarshalDashboard_WithEmptyCellType(t *testing.T) {
ID: 1,
Cells: []chronograf.DashboardCell{
{
ID: "9b5367de-c552-4322-a9e8-7f384cbd235c",
Type: "line",
Queries: []chronograf.DashboardQuery{},
Axes: map[string]chronograf.Axis{},
CellColors: []chronograf.CellColor{},
TableOptions: chronograf.TableOptions{
FieldNames: []chronograf.RenamableField{},
},
ID: "9b5367de-c552-4322-a9e8-7f384cbd235c",
Type: "line",
Queries: []chronograf.DashboardQuery{},
Axes: map[string]chronograf.Axis{},
CellColors: []chronograf.CellColor{},
TableOptions: chronograf.TableOptions{},
FieldOptions: []chronograf.RenamableField{},
},
},
Templates: []chronograf.Template{},

View File

@ -563,18 +563,21 @@ type Legend struct {
// DashboardCell holds visual and query information for a cell
type DashboardCell struct {
ID string `json:"i"`
X int32 `json:"x"`
Y int32 `json:"y"`
W int32 `json:"w"`
H int32 `json:"h"`
Name string `json:"name"`
Queries []DashboardQuery `json:"queries"`
Axes map[string]Axis `json:"axes"`
Type string `json:"type"`
CellColors []CellColor `json:"colors"`
Legend Legend `json:"legend"`
TableOptions TableOptions `json:"tableOptions,omitempty"`
ID string `json:"i"`
X int32 `json:"x"`
Y int32 `json:"y"`
W int32 `json:"w"`
H int32 `json:"h"`
Name string `json:"name"`
Queries []DashboardQuery `json:"queries"`
Axes map[string]Axis `json:"axes"`
Type string `json:"type"`
CellColors []CellColor `json:"colors"`
Legend Legend `json:"legend"`
TableOptions TableOptions `json:"tableOptions,omitempty"`
FieldOptions []RenamableField `json:"fieldOptions"`
TimeFormat string `json:"timeFormat"`
DecimalPlaces DecimalPlaces `json:"decimalPlaces"`
}
// RenamableField is a column/row field in a DashboardCell of type Table
@ -586,12 +589,16 @@ type RenamableField struct {
// TableOptions is a type of options for a DashboardCell with type Table
type TableOptions struct {
TimeFormat string `json:"timeFormat"`
VerticalTimeAxis bool `json:"verticalTimeAxis"`
SortBy RenamableField `json:"sortBy"`
Wrapping string `json:"wrapping"`
FieldNames []RenamableField `json:"fieldNames"`
FixFirstColumn bool `json:"fixFirstColumn"`
VerticalTimeAxis bool `json:"verticalTimeAxis"`
SortBy RenamableField `json:"sortBy"`
Wrapping string `json:"wrapping"`
FixFirstColumn bool `json:"fixFirstColumn"`
}
// DecimalPlaces indicates whether decimal places should be enforced, and how many digits it should show.
type DecimalPlaces struct {
IsEnforced bool `json:"isEnforced"`
Digits int32 `json:"digits"`
}
// DashboardsStore is the storage and retrieval of dashboards

View File

@ -166,8 +166,8 @@ func TestServer(t *testing.T) {
"id": "5000",
"name": "Kapa 1",
"url": "http://localhost:9092",
"active": true,
"insecureSkipVerify": false,
"active": true,
"insecureSkipVerify": false,
"links": {
"proxy": "/chronograf/v1/sources/5000/kapacitors/5000/proxy",
"self": "/chronograf/v1/sources/5000/kapacitors/5000",
@ -225,8 +225,8 @@ func TestServer(t *testing.T) {
"id": "5000",
"name": "Kapa 1",
"url": "http://localhost:9092",
"active": true,
"insecureSkipVerify": false,
"active": true,
"insecureSkipVerify": false,
"links": {
"proxy": "/chronograf/v1/sources/5000/kapacitors/5000/proxy",
"self": "/chronograf/v1/sources/5000/kapacitors/5000",
@ -292,7 +292,7 @@ func TestServer(t *testing.T) {
"default": true,
"telegraf": "telegraf",
"organization": "howdy",
"defaultRP": "",
"defaultRP": "",
"links": {
"self": "/chronograf/v1/sources/5000",
"kapacitors": "/chronograf/v1/sources/5000/kapacitors",
@ -304,7 +304,7 @@ func TestServer(t *testing.T) {
"roles": "/chronograf/v1/sources/5000/roles",
"databases": "/chronograf/v1/sources/5000/dbs",
"annotations": "/chronograf/v1/sources/5000/annotations",
"health": "/chronograf/v1/sources/5000/health"
"health": "/chronograf/v1/sources/5000/health"
}
}
]
@ -546,19 +546,23 @@ func TestServer(t *testing.T) {
"legend":{
"type": "static",
"orientation": "bottom"
},
"tableOptions":{
"timeFormat": "",
"verticalTimeAxis": false,
"sortBy":{
"internalName": "",
"displayName": "",
"visible": false
},
"wrapping": "",
"fieldNames": null,
"fixFirstColumn": false
},
},
"tableOptions":{
"verticalTimeAxis": false,
"sortBy":{
"internalName": "",
"displayName": "",
"visible": false
},
"wrapping": "",
"fixFirstColumn": false
},
"fieldOptions": null,
"timeFormat": "",
"decimalPlaces":{
"isEnforced": false,
"digits": 0
},
"links": {
"self": "/chronograf/v1/dashboards/1000/cells/8f61c619-dd9b-4761-8aa8-577f27247093"
}
@ -797,22 +801,26 @@ func TestServer(t *testing.T) {
"name": "comet",
"value": "100"
}
],
"tableOptions":{
"timeFormat":"",
"verticalTimeAxis":false,
"sortBy":{
"internalName":"",
"displayName":"",
"visible":false
},
"wrapping":"",
"fieldNames":null,
"fixFirstColumn":false
},
"legend":{
"type": "static",
"orientation": "bottom"
],
"legend": {
"type": "static",
"orientation": "bottom"
},
"tableOptions":{
"verticalTimeAxis": false,
"sortBy":{
"internalName": "",
"displayName": "",
"visible": false
},
"wrapping": "",
"fixFirstColumn": false
},
"fieldOptions": null,
"timeFormat": "",
"decimalPlaces":{
"isEnforced": false,
"digits": 0
},
"links": {
"self": "/chronograf/v1/dashboards/1000/cells/8f61c619-dd9b-4761-8aa8-577f27247093"
@ -2143,7 +2151,7 @@ func TestServer(t *testing.T) {
wants: wants{
statusCode: 403,
body: `
{
{
"code": 403,
"message": "user not found"
}`,
@ -2363,61 +2371,61 @@ func TestServer(t *testing.T) {
statusCode: 200,
body: `
{
"links": {
"self": "/chronograf/v1/mappings"
},
"mappings": [
{
"links": {
"self": "/chronograf/v1/mappings/1"
},
"id": "1",
"organizationId": "1",
"provider": "*",
"scheme": "*",
"providerOrganization": "influxdata"
},
{
"links": {
"self": "/chronograf/v1/mappings/2"
},
"id": "2",
"organizationId": "1",
"provider": "*",
"scheme": "*",
"providerOrganization": "*"
},
{
"links": {
"self": "/chronograf/v1/mappings/3"
},
"id": "3",
"organizationId": "2",
"provider": "github",
"scheme": "*",
"providerOrganization": "*"
},
{
"links": {
"self": "/chronograf/v1/mappings/4"
},
"id": "4",
"organizationId": "3",
"provider": "auth0",
"scheme": "ldap",
"providerOrganization": "*"
},
{
"links": {
"self": "/chronograf/v1/mappings/default"
},
"id": "default",
"organizationId": "default",
"provider": "*",
"scheme": "*",
"providerOrganization": "*"
}
]
"links": {
"self": "/chronograf/v1/mappings"
},
"mappings": [
{
"links": {
"self": "/chronograf/v1/mappings/1"
},
"id": "1",
"organizationId": "1",
"provider": "*",
"scheme": "*",
"providerOrganization": "influxdata"
},
{
"links": {
"self": "/chronograf/v1/mappings/2"
},
"id": "2",
"organizationId": "1",
"provider": "*",
"scheme": "*",
"providerOrganization": "*"
},
{
"links": {
"self": "/chronograf/v1/mappings/3"
},
"id": "3",
"organizationId": "2",
"provider": "github",
"scheme": "*",
"providerOrganization": "*"
},
{
"links": {
"self": "/chronograf/v1/mappings/4"
},
"id": "4",
"organizationId": "3",
"provider": "auth0",
"scheme": "ldap",
"providerOrganization": "*"
},
{
"links": {
"self": "/chronograf/v1/mappings/default"
},
"id": "default",
"organizationId": "default",
"provider": "*",
"scheme": "*",
"providerOrganization": "*"
}
]
}
`,
},
@ -2693,40 +2701,40 @@ func TestServer(t *testing.T) {
wants: wants{
statusCode: 200,
body: `
{
"layouts": "/chronograf/v1/layouts",
"users": "/chronograf/v1/organizations/default/users",
"allUsers": "/chronograf/v1/users",
"organizations": "/chronograf/v1/organizations",
"mappings": "/chronograf/v1/mappings",
"sources": "/chronograf/v1/sources",
"me": "/chronograf/v1/me",
"environment": "/chronograf/v1/env",
"dashboards": "/chronograf/v1/dashboards",
"config": {
"self": "/chronograf/v1/config",
"auth": "/chronograf/v1/config/auth"
},
"auth": [
{
"name": "github",
"label": "Github",
"login": "/oauth/github/login",
"logout": "/oauth/github/logout",
"callback": "/oauth/github/callback"
}
],
"logout": "/oauth/logout",
"external": {
"statusFeed": ""
},
"ifql": {
"ast": "/chronograf/v1/ifql/ast",
"self": "/chronograf/v1/ifql",
"suggestions": "/chronograf/v1/ifql/suggestions"
}
}
`,
{
"layouts": "/chronograf/v1/layouts",
"users": "/chronograf/v1/organizations/default/users",
"allUsers": "/chronograf/v1/users",
"organizations": "/chronograf/v1/organizations",
"mappings": "/chronograf/v1/mappings",
"sources": "/chronograf/v1/sources",
"me": "/chronograf/v1/me",
"environment": "/chronograf/v1/env",
"dashboards": "/chronograf/v1/dashboards",
"config": {
"self": "/chronograf/v1/config",
"auth": "/chronograf/v1/config/auth"
},
"auth": [
{
"name": "github",
"label": "Github",
"login": "/oauth/github/login",
"logout": "/oauth/github/logout",
"callback": "/oauth/github/callback"
}
],
"logout": "/oauth/logout",
"external": {
"statusFeed": ""
},
"ifql": {
"ast": "/chronograf/v1/ifql/ast",
"self": "/chronograf/v1/ifql",
"suggestions": "/chronograf/v1/ifql/suggestions"
}
}
`,
},
},
{
@ -2781,40 +2789,40 @@ func TestServer(t *testing.T) {
wants: wants{
statusCode: 200,
body: `
{
"layouts": "/chronograf/v1/layouts",
"users": "/chronograf/v1/organizations/1/users",
"allUsers": "/chronograf/v1/users",
"organizations": "/chronograf/v1/organizations",
"mappings": "/chronograf/v1/mappings",
"sources": "/chronograf/v1/sources",
"me": "/chronograf/v1/me",
"environment": "/chronograf/v1/env",
"dashboards": "/chronograf/v1/dashboards",
"config": {
"self": "/chronograf/v1/config",
"auth": "/chronograf/v1/config/auth"
},
"auth": [
{
"name": "github",
"label": "Github",
"login": "/oauth/github/login",
"logout": "/oauth/github/logout",
"callback": "/oauth/github/callback"
}
],
"logout": "/oauth/logout",
"external": {
"statusFeed": ""
},
"ifql": {
"ast": "/chronograf/v1/ifql/ast",
"self": "/chronograf/v1/ifql",
"suggestions": "/chronograf/v1/ifql/suggestions"
}
}
`,
{
"layouts": "/chronograf/v1/layouts",
"users": "/chronograf/v1/organizations/1/users",
"allUsers": "/chronograf/v1/users",
"organizations": "/chronograf/v1/organizations",
"mappings": "/chronograf/v1/mappings",
"sources": "/chronograf/v1/sources",
"me": "/chronograf/v1/me",
"environment": "/chronograf/v1/env",
"dashboards": "/chronograf/v1/dashboards",
"config": {
"self": "/chronograf/v1/config",
"auth": "/chronograf/v1/config/auth"
},
"auth": [
{
"name": "github",
"label": "Github",
"login": "/oauth/github/login",
"logout": "/oauth/github/logout",
"callback": "/oauth/github/callback"
}
],
"logout": "/oauth/logout",
"external": {
"statusFeed": ""
},
"ifql": {
"ast": "/chronograf/v1/ifql/ast",
"self": "/chronograf/v1/ifql",
"suggestions": "/chronograf/v1/ifql/suggestions"
}
}
`,
},
},
}

View File

@ -532,7 +532,7 @@ func TestService_ReplaceDashboardCell(t *testing.T) {
}
}
`))),
want: `{"i":"3c5c4102-fa40-4585-a8f9-917c77e37192","x":0,"y":0,"w":4,"h":4,"name":"Untitled Cell","queries":[{"query":"SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time \u003e :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)","queryConfig":{"id":"3cd3eaa4-a4b8-44b3-b69e-0c7bf6b91d9e","database":"telegraf","measurement":"cpu","retentionPolicy":"autogen","fields":[{"value":"mean","type":"func","alias":"mean_usage_user","args":[{"value":"usage_user","type":"field","alias":""}]}],"tags":{"cpu":["ChristohersMBP2.lan"]},"groupBy":{"time":"2s","tags":[]},"areTagsAccepted":true,"fill":"null","rawText":"SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time \u003e :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)","range":{"upper":"","lower":"now() - 15m"},"shifts":[]},"source":""}],"axes":{"x":{"bounds":["",""],"label":"","prefix":"","suffix":"","base":"","scale":""},"y":{"bounds":["",""],"label":"","prefix":"","suffix":"","base":"","scale":""},"y2":{"bounds":["",""],"label":"","prefix":"","suffix":"","base":"","scale":""}},"type":"line","colors":[{"id":"0","type":"min","hex":"#00C9FF","name":"laser","value":"0"},{"id":"1","type":"max","hex":"#9394FF","name":"comet","value":"100"}],"legend":{},"tableOptions":{"timeFormat":"","verticalTimeAxis":false,"sortBy":{"internalName":"","displayName":"","visible":false},"wrapping":"","fieldNames":null,"fixFirstColumn":false},"links":{"self":"/chronograf/v1/dashboards/1/cells/3c5c4102-fa40-4585-a8f9-917c77e37192"}}
want: `{"i":"3c5c4102-fa40-4585-a8f9-917c77e37192","x":0,"y":0,"w":4,"h":4,"name":"Untitled Cell","queries":[{"query":"SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time \u003e :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)","queryConfig":{"id":"3cd3eaa4-a4b8-44b3-b69e-0c7bf6b91d9e","database":"telegraf","measurement":"cpu","retentionPolicy":"autogen","fields":[{"value":"mean","type":"func","alias":"mean_usage_user","args":[{"value":"usage_user","type":"field","alias":""}]}],"tags":{"cpu":["ChristohersMBP2.lan"]},"groupBy":{"time":"2s","tags":[]},"areTagsAccepted":true,"fill":"null","rawText":"SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time \u003e :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)","range":{"upper":"","lower":"now() - 15m"},"shifts":[]},"source":""}],"axes":{"x":{"bounds":["",""],"label":"","prefix":"","suffix":"","base":"","scale":""},"y":{"bounds":["",""],"label":"","prefix":"","suffix":"","base":"","scale":""},"y2":{"bounds":["",""],"label":"","prefix":"","suffix":"","base":"","scale":""}},"type":"line","colors":[{"id":"0","type":"min","hex":"#00C9FF","name":"laser","value":"0"},{"id":"1","type":"max","hex":"#9394FF","name":"comet","value":"100"}],"legend":{},"tableOptions":{"verticalTimeAxis":false,"sortBy":{"internalName":"","displayName":"","visible":false},"wrapping":"","fixFirstColumn":false},"fieldOptions":null,"timeFormat":"","decimalPlaces":{"isEnforced":false,"digits":0},"links":{"self":"/chronograf/v1/dashboards/1/cells/3c5c4102-fa40-4585-a8f9-917c77e37192"}}
`,
},
{

View File

@ -4653,11 +4653,6 @@
}
},
"tableOptions": {
"timeFormat": {
"description":
"timeFormat describes the display format for time values according to moment.js date formatting",
"type": "string"
},
"verticalTimeAxis": {
"description":
"verticalTimeAxis describes the orientation of the table by indicating whether the time axis will be displayed vertically",
@ -4675,20 +4670,41 @@
"type": "string",
"enum": ["truncate", "wrap", "single-line"]
},
"fieldNames": {
"description":
"fieldNames represent the fields retrieved by the query with customization options",
"type": "array",
"items": {
"$ref": "#/definitions/RenamableField"
}
},
"fixFirstColumn": {
"description":
"fixFirstColumn indicates whether the first column of the table should be locked",
"type": "boolean"
}
},
"fieldOptions": {
"description":
"fieldOptions represent the fields retrieved by the query with customization options",
"type": "array",
"items": {
"$ref": "#/definitions/RenamableField"
}
},
"timeFormat": {
"description":
"timeFormat describes the display format for time values according to moment.js date formatting",
"type": "string"
},
"decimalPoints": {
"description":
"decimal points indicates whether and how many digits to show after decimal point",
"type": "object",
"properties": {
"isEnforced": {
"description":
"Indicates whether decimal point setting should be enforced",
"type": "bool"
},
"digits": {
"description": "The number of digits after decimal to display",
"type": "integer"
}
}
},
"links": {
"type": "object",
"properties": {

View File

@ -64,3 +64,24 @@ export const updateLineColors = lineColors => ({
lineColors,
},
})
export const changeTimeFormat = timeFormat => ({
type: 'CHANGE_TIME_FORMAT',
payload: {
timeFormat,
},
})
export const changeDecimalPlaces = decimalPlaces => ({
type: 'CHANGE_DECIMAL_PLACES',
payload: {
decimalPlaces,
},
})
export const updateFieldOptions = fieldOptions => ({
type: 'UPDATE_FIELD_OPTIONS',
payload: {
fieldOptions,
},
})

View File

@ -21,7 +21,7 @@ interface RenamableField {
visible: boolean
}
interface GraphOptionsCustomizableFieldProps {
interface Props {
internalName: string
displayName: string
visible: boolean
@ -36,7 +36,7 @@ interface GraphOptionsCustomizableFieldProps {
moveField: (dragIndex: number, hoverIndex: number) => void
}
const fieldSource: DragSourceSpec<GraphOptionsCustomizableFieldProps> = {
const fieldSource: DragSourceSpec<Props> = {
beginDrag(props) {
return {
id: props.id,
@ -112,9 +112,7 @@ function MyDragSource(dragv1, dragv2, dragfunc1) {
isDragging: monitor.isDragging(),
})
)
export default class GraphOptionsCustomizableField extends Component<
GraphOptionsCustomizableFieldProps
> {
export default class GraphOptionsCustomizableField extends Component<Props> {
constructor(props) {
super(props)

View File

@ -0,0 +1,60 @@
import React, {PureComponent} from 'react'
import {ErrorHandling} from 'src/shared/decorators/errors'
import OptIn from 'src/shared/components/OptIn'
interface DecimalPlaces {
isEnforced: boolean
digits: number
}
interface Props extends DecimalPlaces {
onDecimalPlacesChange: (decimalPlaces: DecimalPlaces) => void
}
const fixedValueString = 'fixed'
@ErrorHandling
class GraphOptionsDecimalPlaces extends PureComponent<Props> {
constructor(props: Props) {
super(props)
}
public onSetValue = (valueFromSelector: string): void => {
let digits
let isEnforced
if (valueFromSelector === fixedValueString) {
digits = this.props.digits
isEnforced = false
} else if (valueFromSelector === '') {
digits = this.props.digits
isEnforced = true
} else {
digits = Number(valueFromSelector)
if (digits < 0) {
digits = 0
}
isEnforced = true
}
this.props.onDecimalPlacesChange({digits, isEnforced})
}
public render() {
const {digits, isEnforced} = this.props
return (
<div className="form-group col-xs-6">
<label> Decimal Places </label>
<OptIn
customPlaceholder={isEnforced ? digits.toString() : 'unlimited'}
customValue={isEnforced ? digits.toString() : ''}
onSetValue={this.onSetValue}
fixedPlaceholder={''}
fixedValue={fixedValueString}
type="number"
min={'0'}
/>
</div>
)
}
}
export default GraphOptionsDecimalPlaces

View File

@ -25,7 +25,7 @@ const GraphOptionsSortBy = ({
const selectedValue = selected.displayName || selected.internalName
return (
<div className="form-group col-xs-6">
<label>Default Sort By</label>
<label>Default Sort Field</label>
<Dropdown
items={sortByOptions}
selected={selectedValue}

View File

@ -6,7 +6,7 @@ import {
TIME_FORMAT_CUSTOM,
DEFAULT_TIME_FORMAT,
TIME_FORMAT_TOOLTIP_LINK,
} from 'src/shared/constants/tableGraph'
} from 'src/dashboards/constants'
import {ErrorHandling} from 'src/shared/decorators/errors'
interface TimeFormatOptions {
@ -60,7 +60,7 @@ class GraphOptionsTimeFormat extends PureComponent<Props, State> {
const showCustom = !formatOption || customFormat
return (
<div className="form-group col-xs-12">
<div className="form-group col-xs-6">
<label>
Time Format
{showCustom && (

View File

@ -7,6 +7,7 @@ import GraphOptionsFixFirstColumn from 'src/dashboards/components/GraphOptionsFi
import GraphOptionsSortBy from 'src/dashboards/components/GraphOptionsSortBy'
import GraphOptionsTimeAxis from 'src/dashboards/components/GraphOptionsTimeAxis'
import GraphOptionsTimeFormat from 'src/dashboards/components/GraphOptionsTimeFormat'
import GraphOptionsDecimalPlaces from 'src/dashboards/components/GraphOptionsDecimalPlaces'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import _ from 'lodash'
@ -14,12 +15,17 @@ import _ from 'lodash'
import ThresholdsList from 'src/shared/components/ThresholdsList'
import ThresholdsListTypeToggle from 'src/shared/components/ThresholdsListTypeToggle'
import {updateTableOptions} from 'src/dashboards/actions/cellEditorOverlay'
import {TIME_FIELD_DEFAULT} from 'src/shared/constants/tableGraph'
import {
updateTableOptions,
updateFieldOptions,
changeTimeFormat,
changeDecimalPlaces,
} from 'src/dashboards/actions/cellEditorOverlay'
import {DEFAULT_TIME_FIELD} from 'src/dashboards/constants'
import {QueryConfig} from 'src/types/query'
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Option {
interface DropdownOption {
text: string
key: string
}
@ -30,18 +36,27 @@ interface RenamableField {
visible: boolean
}
interface Options {
timeFormat: string
interface TableOptionsInterface {
verticalTimeAxis: boolean
sortBy: RenamableField
fieldNames: RenamableField[]
fixFirstColumn: boolean
}
interface DecimalPlaces {
isEnforced: boolean
digits: number
}
interface Props {
queryConfigs: QueryConfig[]
handleUpdateTableOptions: (options: Options) => void
tableOptions: Options
handleUpdateTableOptions: (options: TableOptionsInterface) => void
handleUpdateFieldOptions: (fieldOptions: RenamableField[]) => void
handleChangeTimeFormat: (timeFormat: string) => void
handleChangeDecimalPlaces: (decimalPlaces: number) => void
tableOptions: TableOptionsInterface
fieldOptions: RenamableField[]
timeFormat: string
decimalPlaces: DecimalPlaces
onResetFocus: () => void
}
@ -54,12 +69,15 @@ export class TableOptions extends Component<Props, {}> {
public render() {
const {
tableOptions: {timeFormat, fieldNames, verticalTimeAxis, fixFirstColumn},
tableOptions: {verticalTimeAxis, fixFirstColumn},
fieldOptions,
timeFormat,
onResetFocus,
tableOptions,
decimalPlaces,
} = this.props
const tableSortByOptions = fieldNames.map(field => ({
const tableSortByOptions = fieldOptions.map(field => ({
key: field.internalName,
text: field.displayName || field.internalName,
}))
@ -72,18 +90,23 @@ export class TableOptions extends Component<Props, {}> {
<div className="display-options--cell-wrapper">
<h5 className="display-options--header">Table Controls</h5>
<div className="form-group-wrapper">
<GraphOptionsTimeFormat
timeFormat={timeFormat}
onTimeFormatChange={this.handleTimeFormatChange}
<GraphOptionsSortBy
selected={tableOptions.sortBy || DEFAULT_TIME_FIELD}
sortByOptions={tableSortByOptions}
onChooseSortBy={this.handleChooseSortBy}
/>
<GraphOptionsDecimalPlaces
digits={decimalPlaces.digits}
isEnforced={decimalPlaces.isEnforced}
onDecimalPlacesChange={this.handleDecimalPlacesChange}
/>
<GraphOptionsTimeAxis
verticalTimeAxis={verticalTimeAxis}
onToggleVerticalTimeAxis={this.handleToggleVerticalTimeAxis}
/>
<GraphOptionsSortBy
selected={tableOptions.sortBy || TIME_FIELD_DEFAULT}
sortByOptions={tableSortByOptions}
onChooseSortBy={this.handleChooseSortBy}
<GraphOptionsTimeFormat
timeFormat={timeFormat}
onTimeFormatChange={this.handleTimeFormatChange}
/>
<GraphOptionsFixFirstColumn
fixed={fixFirstColumn}
@ -91,7 +114,7 @@ export class TableOptions extends Component<Props, {}> {
/>
</div>
<GraphOptionsCustomizeFields
fields={fieldNames}
fields={fieldOptions}
onFieldUpdate={this.handleFieldUpdate}
moveField={this.moveField}
/>
@ -105,39 +128,38 @@ export class TableOptions extends Component<Props, {}> {
}
private moveField(dragIndex, hoverIndex) {
const {handleUpdateTableOptions, tableOptions} = this.props
const {fieldNames} = tableOptions
const {handleUpdateFieldOptions, fieldOptions} = this.props
const dragField = fieldNames[dragIndex]
const removedFields = _.concat(
_.slice(fieldNames, 0, dragIndex),
_.slice(fieldNames, dragIndex + 1)
const draggedField = fieldOptions[dragIndex]
const fieldOptionsRemoved = _.concat(
_.slice(fieldOptions, 0, dragIndex),
_.slice(fieldOptions, dragIndex + 1)
)
const addedFields = _.concat(
_.slice(removedFields, 0, hoverIndex),
[dragField],
_.slice(removedFields, hoverIndex)
const fieldOptionsAdded = _.concat(
_.slice(fieldOptionsRemoved, 0, hoverIndex),
[draggedField],
_.slice(fieldOptionsRemoved, hoverIndex)
)
handleUpdateTableOptions({
...tableOptions,
fieldNames: addedFields,
})
handleUpdateFieldOptions(fieldOptionsAdded)
}
private handleChooseSortBy = (option: Option) => {
const {tableOptions, handleUpdateTableOptions} = this.props
const sortBy = {
displayName: option.text === option.key ? '' : option.text,
internalName: option.key,
visible: true,
}
private handleChooseSortBy = (option: DropdownOption) => {
const {tableOptions, handleUpdateTableOptions, fieldOptions} = this.props
const sortBy = fieldOptions.find(f => f.internalName === option.key)
handleUpdateTableOptions({...tableOptions, sortBy})
}
private handleTimeFormatChange = timeFormat => {
const {tableOptions, handleUpdateTableOptions} = this.props
handleUpdateTableOptions({...tableOptions, timeFormat})
const {handleChangeTimeFormat} = this.props
handleChangeTimeFormat(timeFormat)
}
private handleDecimalPlacesChange = decimalPlaces => {
const {handleChangeDecimalPlaces} = this.props
handleChangeDecimalPlaces(decimalPlaces)
}
private handleToggleVerticalTimeAxis = verticalTimeAxis => () => {
@ -152,34 +174,46 @@ export class TableOptions extends Component<Props, {}> {
}
private handleFieldUpdate = field => {
const {handleUpdateTableOptions, tableOptions} = this.props
const {sortBy, fieldNames} = tableOptions
const updatedFields = fieldNames.map(
const {
handleUpdateTableOptions,
handleUpdateFieldOptions,
tableOptions,
fieldOptions,
} = this.props
const {sortBy} = tableOptions
const updatedFieldOptions = fieldOptions.map(
f => (f.internalName === field.internalName ? field : f)
)
const updatedSortBy =
sortBy.internalName === field.internalName
? {...sortBy, displayName: field.displayName}
: sortBy
handleUpdateTableOptions({
...tableOptions,
fieldNames: updatedFields,
sortBy: updatedSortBy,
})
if (sortBy.internalName === field.internalName) {
const updatedSortBy = {...sortBy, displayName: field.displayName}
handleUpdateTableOptions({
...tableOptions,
sortBy: updatedSortBy,
})
}
handleUpdateFieldOptions(updatedFieldOptions)
}
}
const mapStateToProps = ({
cellEditorOverlay: {
cell: {tableOptions},
cell: {tableOptions, timeFormat, fieldOptions, decimalPlaces},
},
}) => ({
tableOptions,
timeFormat,
fieldOptions,
decimalPlaces,
})
const mapDispatchToProps = dispatch => ({
handleUpdateTableOptions: bindActionCreators(updateTableOptions, dispatch),
handleUpdateFieldOptions: bindActionCreators(updateFieldOptions, dispatch),
handleChangeTimeFormat: bindActionCreators(changeTimeFormat, dispatch),
handleChangeDecimalPlaces: bindActionCreators(changeDecimalPlaces, dispatch),
})
export default connect(mapStateToProps, mapDispatchToProps)(TableOptions)

View File

@ -24,6 +24,9 @@ const DashVisualization = (
staticLegend,
thresholdsListColors,
tableOptions,
timeFormat,
decimalPlaces,
fieldOptions,
isInCEO,
},
{
@ -54,6 +57,9 @@ const DashVisualization = (
editQueryStatus={editQueryStatus}
resizerTopHeight={resizerTopHeight}
staticLegend={staticLegend}
timeFormat={timeFormat}
decimalPlaces={decimalPlaces}
fieldOptions={fieldOptions}
isInCEO={isInCEO}
/>
</div>
@ -79,6 +85,18 @@ DashVisualization.propTypes = {
}),
}),
tableOptions: shape({}),
timeFormat: string.isRequired,
decimalPlaces: shape({
isEnforced: bool,
digits: number,
}),
fieldOptions: arrayOf(
shape({
internalName: string.isRequired,
displayName: string.isRequired,
visible: bool.isRequired,
})
),
resizerTopHeight: number,
thresholdsListColors: colorsNumberSchema,
gaugeColors: colorsNumberSchema,
@ -100,7 +118,7 @@ const mapStateToProps = ({
thresholdsListColors,
gaugeColors,
lineColors,
cell: {type, axes, tableOptions},
cell: {type, axes, tableOptions, fieldOptions, timeFormat, decimalPlaces},
},
}) => ({
gaugeColors,
@ -109,6 +127,9 @@ const mapStateToProps = ({
type,
axes,
tableOptions,
fieldOptions,
timeFormat,
decimalPlaces,
})
export default connect(mapStateToProps, null)(DashVisualization)

View File

@ -1,4 +1,4 @@
import {DEFAULT_TABLE_OPTIONS} from 'src/shared/constants/tableGraph'
import {DEFAULT_TABLE_OPTIONS} from 'src/dashboards/constants'
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
import {
CELL_TYPE_LINE,

View File

@ -1,4 +1,7 @@
import {DEFAULT_TABLE_OPTIONS} from 'src/shared/constants/tableGraph'
import {
DEFAULT_VERTICAL_TIME_AXIS,
DEFAULT_FIX_FIRST_COLUMN,
} from 'src/shared/constants/tableGraph'
import {CELL_TYPE_LINE} from 'src/dashboards/graphics/graph'
export const UNTITLED_CELL_LINE = 'Untitled Line Graph'
@ -11,6 +14,41 @@ export const UNTITLED_CELL_SINGLE_STAT = 'Untitled Single Stat'
export const UNTITLED_CELL_GAUGE = 'Untitled Gauge'
export const UNTITLED_CELL_TABLE = 'Untitled Table'
export const TIME_FORMAT_TOOLTIP_LINK =
'http://momentjs.com/docs/#/parsing/string-format/'
export const DEFAULT_DECIMAL_PLACES = {
isEnforced: false,
digits: 3,
}
export const DEFAULT_TIME_FIELD = {
internalName: 'time',
displayName: '',
visible: true,
}
export const DEFAULT_TABLE_OPTIONS = {
verticalTimeAxis: DEFAULT_VERTICAL_TIME_AXIS,
sortBy: DEFAULT_TIME_FIELD,
wrapping: 'truncate',
fixFirstColumn: DEFAULT_FIX_FIRST_COLUMN,
}
export const DEFAULT_TIME_FORMAT = 'MM/DD/YYYY HH:mm:ss'
export const TIME_FORMAT_CUSTOM = 'Custom'
export const FORMAT_OPTIONS = [
{text: DEFAULT_TIME_FORMAT},
{text: 'MM/DD/YYYY HH:mm:ss.SSS'},
{text: 'YYYY-MM-DD HH:mm:ss'},
{text: 'HH:mm:ss'},
{text: 'HH:mm:ss.SSS'},
{text: 'MMMM D, YYYY HH:mm:ss'},
{text: 'dddd, MMMM D, YYYY HH:mm:ss'},
{text: TIME_FORMAT_CUSTOM},
]
export const NEW_DEFAULT_DASHBOARD_CELL = {
x: 0,
y: 0,
@ -20,6 +58,9 @@ export const NEW_DEFAULT_DASHBOARD_CELL = {
type: CELL_TYPE_LINE,
queries: [],
tableOptions: DEFAULT_TABLE_OPTIONS,
timeFormat: DEFAULT_TIME_FORMAT,
decimalPlaces: DEFAULT_DECIMAL_PLACES,
fieldOptions: [DEFAULT_TIME_FIELD],
}
export const EMPTY_DASHBOARD = {

View File

@ -114,6 +114,25 @@ export default function cellEditorOverlay(state = initialState, action) {
return {...state, cell}
}
case 'CHANGE_TIME_FORMAT': {
const {timeFormat} = action.payload
const cell = {...state.cell, timeFormat}
return {...state, cell}
}
case 'CHANGE_DECIMAL_PLACES': {
const {decimalPlaces} = action.payload
const cell = {...state.cell, decimalPlaces}
return {...state, cell}
}
case 'UPDATE_FIELD_OPTIONS': {
const {fieldOptions} = action.payload
const cell = {...state.cell, fieldOptions}
return {...state, cell}
}
case 'UPDATE_LINE_COLORS': {
const {lineColors} = action.payload

View File

@ -2,11 +2,8 @@ import calculateSize from 'calculate-size'
import _ from 'lodash'
import {map, reduce, filter} from 'fast.js'
import {
CELL_HORIZONTAL_PADDING,
TIME_FIELD_DEFAULT,
DEFAULT_TIME_FORMAT,
} from 'src/shared/constants/tableGraph'
import {CELL_HORIZONTAL_PADDING} from 'src/shared/constants/tableGraph'
import {DEFAULT_TIME_FIELD, DEFAULT_TIME_FORMAT} from 'src/dashboards/constants'
const calculateTimeColumnWidth = timeFormat => {
// Force usage of longest format names for ideal measurement
@ -30,9 +27,10 @@ const updateMaxWidths = (
maxColumnWidths,
topRow,
isTopRow,
fieldNames,
fieldOptions,
timeFormatWidth,
verticalTimeAxis
verticalTimeAxis,
decimalPlaces
) => {
return reduce(
row,
@ -41,21 +39,27 @@ const updateMaxWidths = (
(verticalTimeAxis && isTopRow) || (!verticalTimeAxis && c === 0)
const foundField = isLabel
? fieldNames.find(field => field.internalName === col)
? fieldOptions.find(field => field.internalName === col)
: undefined
const colValue =
foundField && foundField.displayName ? foundField.displayName : `${col}`
const isNumerical = _.isNumber(col)
let colValue = `${col}`
if (foundField && foundField.displayName) {
colValue = foundField.displayName
} else if (isNumerical && decimalPlaces.isEnforced) {
colValue = col.toFixed(decimalPlaces.digits)
}
const columnLabel = topRow[c]
const useTimeWidth =
(columnLabel === TIME_FIELD_DEFAULT.internalName &&
(columnLabel === DEFAULT_TIME_FIELD.internalName &&
verticalTimeAxis &&
!isTopRow) ||
(!verticalTimeAxis &&
isTopRow &&
topRow[0] === TIME_FIELD_DEFAULT.internalName &&
topRow[0] === DEFAULT_TIME_FIELD.internalName &&
c !== 0)
const currentWidth = useTimeWidth
? timeFormatWidth
: calculateSize(colValue, {
@ -77,23 +81,27 @@ const updateMaxWidths = (
)
}
export const computeFieldNames = (existingFieldNames, sortedLabels) => {
export const computeFieldOptions = (existingFieldOptions, sortedLabels) => {
const timeField =
existingFieldNames.find(f => f.internalName === 'time') ||
TIME_FIELD_DEFAULT
existingFieldOptions.find(f => f.internalName === 'time') ||
DEFAULT_TIME_FIELD
let astNames = [timeField]
sortedLabels.forEach(({label}) => {
const field = {internalName: label, displayName: '', visible: true}
const field = {
internalName: label,
displayName: '',
visible: true,
}
astNames = [...astNames, field]
})
const intersection = existingFieldNames.filter(f => {
const intersection = existingFieldOptions.filter(f => {
return astNames.find(a => a.internalName === f.internalName)
})
const newFields = astNames.filter(a => {
return !existingFieldNames.find(f => f.internalName === a.internalName)
return !existingFieldOptions.find(f => f.internalName === a.internalName)
})
return [...intersection, ...newFields]
@ -101,9 +109,10 @@ export const computeFieldNames = (existingFieldNames, sortedLabels) => {
export const calculateColumnWidths = (
data,
fieldNames,
fieldOptions,
timeFormat,
verticalTimeAxis
verticalTimeAxis,
decimalPlaces
) => {
const timeFormatWidth = calculateTimeColumnWidth(
timeFormat === '' ? DEFAULT_TIME_FORMAT : timeFormat
@ -116,21 +125,24 @@ export const calculateColumnWidths = (
acc,
data[0],
r === 0,
fieldNames,
fieldOptions,
timeFormatWidth,
verticalTimeAxis
verticalTimeAxis,
decimalPlaces
)
},
{widths: {}, totalWidths: 0}
)
}
export const filterTableColumns = (data, fieldNames) => {
export const filterTableColumns = (data, fieldOptions) => {
const visibility = {}
const filteredData = map(data, (row, i) => {
return filter(row, (col, j) => {
if (i === 0) {
const foundField = fieldNames.find(field => field.internalName === col)
const foundField = fieldOptions.find(
field => field.internalName === col
)
visibility[j] = foundField ? foundField.visible : true
}
return visibility[j]
@ -139,10 +151,10 @@ export const filterTableColumns = (data, fieldNames) => {
return filteredData[0].length ? filteredData : [[]]
}
export const orderTableColumns = (data, fieldNames) => {
const fieldsSortOrder = fieldNames.map(fieldName => {
export const orderTableColumns = (data, fieldOptions) => {
const fieldsSortOrder = fieldOptions.map(fieldOption => {
return _.findIndex(data[0], dataLabel => {
return dataLabel === fieldName.internalName
return dataLabel === fieldOption.internalName
})
})
const filteredFieldSortOrder = filter(fieldsSortOrder, f => f !== -1)
@ -152,22 +164,30 @@ export const orderTableColumns = (data, fieldNames) => {
return orderedData[0].length ? orderedData : [[]]
}
export const transformTableData = (data, sort, fieldNames, tableOptions) => {
const {verticalTimeAxis, timeFormat} = tableOptions
export const transformTableData = (
data,
sort,
fieldOptions,
tableOptions,
timeFormat,
decimalPlaces
) => {
const {verticalTimeAxis} = tableOptions
const sortIndex = _.indexOf(data[0], sort.field)
const sortedData = [
data[0],
..._.orderBy(_.drop(data, 1), sortIndex, [sort.direction]),
]
const sortedTimeVals = map(sortedData, r => r[0])
const filteredData = filterTableColumns(sortedData, fieldNames)
const orderedData = orderTableColumns(filteredData, fieldNames)
const filteredData = filterTableColumns(sortedData, fieldOptions)
const orderedData = orderTableColumns(filteredData, fieldOptions)
const transformedData = verticalTimeAxis ? orderedData : _.unzip(orderedData)
const {widths: columnWidths, totalWidths} = calculateColumnWidths(
transformedData,
fieldNames,
fieldOptions,
timeFormat,
verticalTimeAxis
verticalTimeAxis,
decimalPlaces
)
return {transformedData, sortedTimeVals, columnWidths, totalWidths}
}

View File

@ -45,7 +45,17 @@ const Layout = (
{
host,
cell,
cell: {h, axes, type, colors, legend, tableOptions},
cell: {
h,
axes,
type,
colors,
legend,
timeFormat,
fieldOptions,
tableOptions,
decimalPlaces,
},
source,
sources,
onZoom,
@ -87,6 +97,9 @@ const Layout = (
type={type}
isDragging={isDragging}
tableOptions={tableOptions}
fieldOptions={fieldOptions}
timeFormat={timeFormat}
decimalPlaces={decimalPlaces}
staticLegend={IS_STATIC_LEGEND(legend)}
cellHeight={h}
onZoom={onZoom}
@ -140,6 +153,28 @@ const propTypes = {
name: string.isRequired,
type: string.isRequired,
colors: colorsStringSchema,
tableOptions: shape({
verticalTimeAxis: bool.isRequired,
sortBy: shape({
internalName: string.isRequired,
displayName: string.isRequired,
visible: bool.isRequired,
}).isRequired,
wrapping: string.isRequired,
fixFirstColumn: bool.isRequired,
}),
timeFormat: string.isRequired,
decimalPlaces: shape({
isEnforced: bool.isRequired,
digits: number.isRequired,
}).isRequired,
fieldOptions: arrayOf(
shape({
internalName: string.isRequired,
displayName: string.isRequired,
visible: bool.isRequired,
}).isRequired
),
}).isRequired,
templates: arrayOf(shape()),
host: string,

View File

@ -173,6 +173,24 @@ LayoutRenderer.propTypes = {
i: string.isRequired,
name: string.isRequired,
type: string.isRequired,
timeFormat: string.isRequired,
tableOptions: shape({
verticalTimeAxis: bool.isRequired,
sortBy: shape({
internalName: string.isRequired,
displayName: string.isRequired,
visible: bool.isRequired,
}).isRequired,
wrapping: string.isRequired,
fixFirstColumn: bool.isRequired,
}),
fieldOptions: arrayOf(
shape({
internalName: string.isRequired,
displayName: string.isRequired,
visible: bool.isRequired,
}).isRequired
),
}).isRequired
),
templates: arrayOf(shape()),

View File

@ -33,6 +33,9 @@ const RefreshingGraph = ({
timeRange,
cellHeight,
autoRefresh,
fieldOptions,
timeFormat,
decimalPlaces,
resizerTopHeight,
staticLegend,
manualRefresh, // when changed, re-mounts the component
@ -44,7 +47,6 @@ const RefreshingGraph = ({
}) => {
const prefix = (axes && axes.y.prefix) || ''
const suffix = (axes && axes.y.suffix) || ''
if (!queries.length) {
return (
<div className="graph-empty">
@ -107,6 +109,9 @@ const RefreshingGraph = ({
cellHeight={cellHeight}
resizeCoords={resizeCoords}
tableOptions={tableOptions}
fieldOptions={fieldOptions}
timeFormat={timeFormat}
decimalPlaces={decimalPlaces}
editQueryStatus={editQueryStatus}
resizerTopHeight={resizerTopHeight}
handleSetHoverTime={handleSetHoverTime}
@ -167,7 +172,28 @@ RefreshingGraph.propTypes = {
colors: colorsStringSchema,
cellID: string,
inView: bool,
tableOptions: shape({}),
tableOptions: shape({
verticalTimeAxis: bool.isRequired,
sortBy: shape({
internalName: string.isRequired,
displayName: string.isRequired,
visible: bool.isRequired,
}).isRequired,
wrapping: string.isRequired,
fixFirstColumn: bool.isRequired,
}),
fieldOptions: arrayOf(
shape({
internalName: string.isRequired,
displayName: string.isRequired,
visible: bool.isRequired,
}).isRequired
),
timeFormat: string.isRequired,
decimalPlaces: shape({
isEnforced: bool.isRequired,
digits: number.isRequired,
}).isRequired,
hoverTime: string.isRequired,
handleSetHoverTime: func.isRequired,
isInCEO: bool,

View File

@ -9,27 +9,22 @@ import {bindActionCreators} from 'redux'
import moment from 'moment'
import {reduce} from 'fast.js'
const {arrayOf, bool, shape, string, func} = PropTypes
import {timeSeriesToTableGraph} from 'src/utils/timeSeriesTransformers'
import {
computeFieldNames,
computeFieldOptions,
transformTableData,
} from 'src/dashboards/utils/tableGraph'
import {updateTableOptions} from 'src/dashboards/actions/cellEditorOverlay'
import {updateFieldOptions} from 'src/dashboards/actions/cellEditorOverlay'
import {DEFAULT_TIME_FIELD} from 'src/dashboards/constants'
import {
NULL_ARRAY_INDEX,
NULL_HOVER_TIME,
DEFAULT_TIME_FORMAT,
TIME_FIELD_DEFAULT,
ASCENDING,
DESCENDING,
NULL_HOVER_TIME,
NULL_ARRAY_INDEX,
DEFAULT_FIX_FIRST_COLUMN,
DEFAULT_VERTICAL_TIME_AXIS,
DEFAULT_SORT_DIRECTION,
FIX_FIRST_COLUMN_DEFAULT,
VERTICAL_TIME_AXIS_DEFAULT,
} from 'src/shared/constants/tableGraph'
import {generateThresholdsListHexs} from 'shared/constants/colorOperations'
import {colorsStringSchema} from 'shared/schemas'
import {ErrorHandling} from 'src/shared/decorators/errors'
@ -42,7 +37,7 @@ class TableGraph extends Component {
const sortField = _.get(
this.props,
['tableOptions', 'sortBy', 'internalName'],
TIME_FIELD_DEFAULT.internalName
DEFAULT_TIME_FIELD.internalName
)
this.state = {
@ -58,19 +53,19 @@ class TableGraph extends Component {
}
}
handleUpdateTableOptions = (fieldNames, tableOptions) => {
handleUpdateFieldOptions = fieldOptions => {
const {isInCEO} = this.props
if (!isInCEO) {
return
}
this.props.handleUpdateTableOptions({...tableOptions, fieldNames})
this.props.handleUpdateFieldOptions(fieldOptions)
}
componentWillReceiveProps(nextProps) {
const updatedProps = _.keys(nextProps).filter(
k => !_.isEqual(this.props[k], nextProps[k])
)
const {tableOptions} = nextProps
const {tableOptions, fieldOptions, timeFormat, decimalPlaces} = nextProps
let result = {}
@ -80,10 +75,11 @@ class TableGraph extends Component {
const data = _.get(result, 'data', this.state.data)
const sortedLabels = _.get(result, 'sortedLabels', this.state.sortedLabels)
const fieldNames = computeFieldNames(tableOptions.fieldNames, sortedLabels)
const computedFieldOptions = computeFieldOptions(fieldOptions, sortedLabels)
if (_.includes(updatedProps, 'data')) {
this.handleUpdateTableOptions(fieldNames, tableOptions)
this.handleUpdateFieldOptions(computedFieldOptions)
}
if (_.isEmpty(data[0])) {
@ -106,14 +102,23 @@ class TableGraph extends Component {
if (
_.includes(updatedProps, 'data') ||
_.includes(updatedProps, 'tableOptions')
_.includes(updatedProps, 'tableOptions') ||
_.includes(updatedProps, 'fieldOptions') ||
_.includes(updatedProps, 'timeFormat')
) {
const {
transformedData,
sortedTimeVals,
columnWidths,
totalWidths,
} = transformTableData(data, sort, fieldNames, tableOptions)
} = transformTableData(
data,
sort,
computedFieldOptions,
tableOptions,
timeFormat,
decimalPlaces
)
this.setState({
data,
@ -187,9 +192,8 @@ class TableGraph extends Component {
}
handleClickFieldName = clickedFieldName => () => {
const {tableOptions} = this.props
const {tableOptions, fieldOptions, timeFormat, decimalPlaces} = this.props
const {data, sort} = this.state
const fieldNames = _.get(tableOptions, 'fieldNames', [TIME_FIELD_DEFAULT])
if (clickedFieldName === sort.field) {
sort.direction = sort.direction === ASCENDING ? DESCENDING : ASCENDING
@ -201,8 +205,10 @@ class TableGraph extends Component {
const {transformedData, sortedTimeVals} = transformTableData(
data,
sort,
fieldNames,
tableOptions
fieldOptions,
tableOptions,
timeFormat,
decimalPlaces
)
this.setState({
@ -241,6 +247,26 @@ class TableGraph extends Component {
return adjustedColumnSizerWidth
}
createCellContents = (
cellData,
fieldName,
isTimeData,
isFieldName,
isNumerical
) => {
const {timeFormat, decimalPlaces} = this.props
if (isTimeData) {
return `${moment(cellData).format(timeFormat)}`
}
if (isFieldName) {
return fieldName
}
if (isNumerical && decimalPlaces.isEnforced) {
return cellData.toFixed(decimalPlaces.digits)
}
return `${cellData}`
}
cellRenderer = ({columnIndex, rowIndex, key, parent, style}) => {
const {
hoveredColumnIndex,
@ -248,22 +274,25 @@ class TableGraph extends Component {
transformedData,
sort,
} = this.state
const {tableOptions, colors} = parent.props
const {
timeFormat = DEFAULT_TIME_FORMAT,
verticalTimeAxis = VERTICAL_TIME_AXIS_DEFAULT,
fixFirstColumn = FIX_FIRST_COLUMN_DEFAULT,
fieldNames = [TIME_FIELD_DEFAULT],
fieldOptions = [DEFAULT_TIME_FIELD],
tableOptions,
colors,
} = parent.props
const {
verticalTimeAxis = DEFAULT_VERTICAL_TIME_AXIS,
fixFirstColumn = DEFAULT_FIX_FIRST_COLUMN,
} = tableOptions
const cellData = transformedData[rowIndex][columnIndex]
const timeFieldIndex = fieldNames.findIndex(
field => field.internalName === TIME_FIELD_DEFAULT.internalName
const timeFieldIndex = fieldOptions.findIndex(
field => field.internalName === DEFAULT_TIME_FIELD.internalName
)
const visibleTime = _.get(fieldNames, [timeFieldIndex, 'visible'], true)
const visibleTime = _.get(fieldOptions, [timeFieldIndex, 'visible'], true)
const isFixedRow = rowIndex === 0 && columnIndex > 0
const isFixedColumn = fixFirstColumn && rowIndex > 0 && columnIndex === 0
@ -274,7 +303,7 @@ class TableGraph extends Component {
: rowIndex === timeFieldIndex && columnIndex !== 0)
const isFieldName = verticalTimeAxis ? rowIndex === 0 : columnIndex === 0
const isFixedCorner = rowIndex === 0 && columnIndex === 0
const dataIsNumerical = _.isNumber(cellData)
const isNumerical = _.isNumber(cellData)
const isHighlightedRow =
rowIndex === parent.props.scrollToRow ||
(rowIndex === hoveredRowIndex && hoveredRowIndex !== 0)
@ -299,7 +328,7 @@ class TableGraph extends Component {
}
const foundField =
isFieldName && fieldNames.find(field => field.internalName === cellData)
isFieldName && fieldOptions.find(field => field.internalName === cellData)
const fieldName =
foundField && (foundField.displayName || foundField.internalName)
@ -309,7 +338,7 @@ class TableGraph extends Component {
'table-graph-cell__fixed-corner': isFixedCorner,
'table-graph-cell__highlight-row': isHighlightedRow,
'table-graph-cell__highlight-column': isHighlightedColumn,
'table-graph-cell__numerical': dataIsNumerical,
'table-graph-cell__numerical': isNumerical,
'table-graph-cell__field-name': isFieldName,
'table-graph-cell__sort-asc':
isFieldName && sort.field === cellData && sort.direction === ASCENDING,
@ -317,11 +346,13 @@ class TableGraph extends Component {
isFieldName && sort.field === cellData && sort.direction === DESCENDING,
})
const cellContents = isTimeData
? `${moment(cellData).format(
timeFormat === '' ? DEFAULT_TIME_FORMAT : timeFormat
)}`
: fieldName || `${cellData}`
const cellContents = this.createCellContents(
cellData,
fieldName,
isTimeData,
isFieldName,
isNumerical
)
return (
<div
@ -350,8 +381,15 @@ class TableGraph extends Component {
sort,
transformedData,
} = this.state
const {hoverTime, tableOptions, colors} = this.props
const {fixFirstColumn = FIX_FIRST_COLUMN_DEFAULT} = tableOptions
const {
hoverTime,
tableOptions,
colors,
fieldOptions,
timeFormat,
decimalPlaces,
} = this.props
const {fixFirstColumn = DEFAULT_FIX_FIRST_COLUMN} = tableOptions
const columnCount = _.get(transformedData, ['0', 'length'], 0)
const rowCount = columnCount === 0 ? 0 : transformedData.length
@ -398,7 +436,10 @@ class TableGraph extends Component {
hoveredRowIndex={hoveredRowIndex}
hoverTime={hoverTime}
colors={colors}
fieldOptions={fieldOptions}
tableOptions={tableOptions}
timeFormat={timeFormat}
decimalPlaces={decimalPlaces}
timeColumnWidth={timeColumnWidth}
classNameBottomRightGrid="table-graph--scroll-window"
/>
@ -409,11 +450,11 @@ class TableGraph extends Component {
)
}
}
const {arrayOf, bool, number, shape, string, func} = PropTypes
TableGraph.propTypes = {
data: arrayOf(shape()),
tableOptions: shape({
timeFormat: string.isRequired,
verticalTimeAxis: bool.isRequired,
sortBy: shape({
internalName: string.isRequired,
@ -421,17 +462,22 @@ TableGraph.propTypes = {
visible: bool.isRequired,
}).isRequired,
wrapping: string.isRequired,
fieldNames: arrayOf(
shape({
internalName: string.isRequired,
displayName: string.isRequired,
visible: bool.isRequired,
})
).isRequired,
fixFirstColumn: bool,
fixFirstColumn: bool.isRequired,
}),
timeFormat: string.isRequired,
decimalPlaces: shape({
isEnforced: bool.isRequired,
digits: number.isRequired,
}).isRequired,
fieldOptions: arrayOf(
shape({
internalName: string.isRequired,
displayName: string.isRequired,
visible: bool.isRequired,
})
).isRequired,
hoverTime: string,
handleUpdateTableOptions: func,
handleUpdateFieldOptions: func,
handleSetHoverTime: func,
colors: colorsStringSchema,
queryASTs: arrayOf(shape()),
@ -439,7 +485,7 @@ TableGraph.propTypes = {
}
const mapDispatchToProps = dispatch => ({
handleUpdateTableOptions: bindActionCreators(updateTableOptions, dispatch),
handleUpdateFieldOptions: bindActionCreators(updateFieldOptions, dispatch),
})
export default connect(null, mapDispatchToProps)(TableGraph)

View File

@ -2,43 +2,11 @@ export const NULL_ARRAY_INDEX = -1
export const NULL_HOVER_TIME = '0'
export const TIME_FORMAT_TOOLTIP_LINK =
'http://momentjs.com/docs/#/parsing/string-format/'
export const TIME_FIELD_DEFAULT = {
internalName: 'time',
displayName: '',
visible: true,
}
export const ASCENDING = 'asc'
export const DESCENDING = 'desc'
export const DEFAULT_SORT_DIRECTION = ASCENDING
export const FIX_FIRST_COLUMN_DEFAULT = true
export const VERTICAL_TIME_AXIS_DEFAULT = true
export const DEFAULT_FIX_FIRST_COLUMN = true
export const DEFAULT_VERTICAL_TIME_AXIS = true
export const CELL_HORIZONTAL_PADDING = 30
export const DEFAULT_TIME_FORMAT = 'MM/DD/YYYY HH:mm:ss'
export const TIME_FORMAT_CUSTOM = 'Custom'
export const FORMAT_OPTIONS = [
{text: DEFAULT_TIME_FORMAT},
{text: 'MM/DD/YYYY HH:mm:ss.SSS'},
{text: 'YYYY-MM-DD HH:mm:ss'},
{text: 'HH:mm:ss'},
{text: 'HH:mm:ss.SSS'},
{text: 'MMMM D, YYYY HH:mm:ss'},
{text: 'dddd, MMMM D, YYYY HH:mm:ss'},
{text: TIME_FORMAT_CUSTOM},
]
export const DEFAULT_TABLE_OPTIONS = {
verticalTimeAxis: VERTICAL_TIME_AXIS_DEFAULT,
timeFormat: DEFAULT_TIME_FORMAT,
sortBy: TIME_FIELD_DEFAULT,
wrapping: 'truncate',
fieldNames: [TIME_FIELD_DEFAULT],
fixFirstColumn: FIX_FIRST_COLUMN_DEFAULT,
}

View File

@ -21,12 +21,10 @@ interface FieldName {
}
interface TableOptions {
timeFormat: string
verticalTimeAxis: boolean
sortBy: FieldName
wrapping: string
fixFirstColumn: boolean
fieldNames: FieldName[]
}
interface CellLinks {
@ -43,6 +41,11 @@ export interface Legend {
orientation?: string
}
interface DecimalPlaces {
isEnforced: boolean
digits: number
}
export interface Cell {
id: string
x: number
@ -55,6 +58,9 @@ export interface Cell {
axes: Axes
colors: ColorString[]
tableOptions: TableOptions
fieldOptions: FieldName[]
timeFormat: string
decimalPlaces: DecimalPlaces
links: CellLinks
legend: Legend
}

View File

@ -8,7 +8,7 @@ import QuestionMarkTooltip from 'src/shared/components/QuestionMarkTooltip'
import {
TIME_FORMAT_CUSTOM,
TIME_FORMAT_TOOLTIP_LINK,
} from 'src/shared/constants/tableGraph'
} from 'src/dashboards/constants'
const setup = (override = {}) => {
const props = {

View File

@ -12,17 +12,22 @@ import ThresholdsListTypeToggle from 'src/shared/components/ThresholdsListTypeTo
const defaultProps = {
handleUpdateTableOptions: () => {},
handleUpdateFieldOptions: () => {},
handleChangeTimeFormat: () => {},
handleChangeDecimalPlaces: () => {},
onResetFocus: () => {},
queryConfigs: [],
tableOptions: {
columnNames: [],
fieldNames: [],
fixFirstColumn: true,
sortBy: {displayName: '', internalName: '', visible: true},
timeFormat: '',
verticalTimeAxis: true,
},
queryASTs: [],
fieldOptions: [],
timeFormat: '',
decimalPlaces: {
isEnforced: true,
digits: 2,
},
}
const setup = (override = {}) => {

View File

@ -11,7 +11,7 @@ import {
updateLineColors,
updateAxes,
} from 'src/dashboards/actions/cellEditorOverlay'
import {DEFAULT_TABLE_OPTIONS} from 'src/shared/constants/tableGraph'
import {DEFAULT_TABLE_OPTIONS} from 'src/dashboards/constants'
import {
validateGaugeColors,

View File

@ -8,10 +8,11 @@ import {
transformTableData,
} from 'src/dashboards/utils/tableGraph'
import {DEFAULT_SORT_DIRECTION} from 'src/shared/constants/tableGraph'
import {
DEFAULT_SORT_DIRECTION,
DEFAULT_TIME_FORMAT,
} from 'src/shared/constants/tableGraph'
DEFAULT_DECIMAL_PLACES,
} from 'src/dashboards/constants'
describe('timeSeriesToDygraph', () => {
it('parses a raw InfluxDB response into a dygraph friendly data format', () => {
@ -494,18 +495,18 @@ describe('filterTableColumns', () => {
[3000, 2000, 1000],
]
const fieldNames = [
const fieldOptions = [
{internalName: 'time', displayName: 'Time', visible: true},
{internalName: 'f1', displayName: '', visible: false},
{internalName: 'f2', displayName: 'F2', visible: false},
]
const actual = filterTableColumns(data, fieldNames)
const actual = filterTableColumns(data, fieldOptions)
const expected = [['time'], [1000], [2000], [3000]]
expect(actual).toEqual(expected)
})
it('returns an array of an empty array if all fieldNames are not visible', () => {
it('returns an array of an empty array if all fieldOptions are not visible', () => {
const data = [
['time', 'f1', 'f2'],
[1000, 3000, 2000],
@ -513,13 +514,13 @@ describe('filterTableColumns', () => {
[3000, 2000, 1000],
]
const fieldNames = [
const fieldOptions = [
{internalName: 'time', displayName: 'Time', visible: false},
{internalName: 'f1', displayName: '', visible: false},
{internalName: 'f2', displayName: 'F2', visible: false},
]
const actual = filterTableColumns(data, fieldNames)
const actual = filterTableColumns(data, fieldOptions)
const expected = [[]]
expect(actual).toEqual(expected)
})
@ -534,18 +535,23 @@ describe('transformTableData', () => {
[3000, 2000, 1000],
]
const sort = {field: 'f1', direction: DEFAULT_SORT_DIRECTION}
const tableOptions = {
verticalTimeAxis: true,
timeFormat: DEFAULT_TIME_FORMAT,
}
const fieldNames = [
const tableOptions = {verticalTimeAxis: true}
const timeFormat = DEFAULT_TIME_FORMAT
const decimalPlaces = DEFAULT_DECIMAL_PLACES
const fieldOptions = [
{internalName: 'time', displayName: 'Time', visible: true},
{internalName: 'f1', displayName: '', visible: true},
{internalName: 'f2', displayName: 'F2', visible: true},
]
const actual = transformTableData(data, sort, fieldNames, tableOptions)
const actual = transformTableData(
data,
sort,
fieldOptions,
tableOptions,
timeFormat,
decimalPlaces
)
const expected = [
['time', 'f1', 'f2'],
[2000, 1000, 3000],
@ -563,21 +569,24 @@ describe('transformTableData', () => {
[2000, 1000, 3000],
[3000, 2000, 1000],
]
const sort = {field: 'time', direction: DEFAULT_SORT_DIRECTION}
const tableOptions = {
verticalTimeAxis: true,
timeFormat: DEFAULT_TIME_FORMAT,
}
const fieldNames = [
const tableOptions = {verticalTimeAxis: true}
const timeFormat = DEFAULT_TIME_FORMAT
const decimalPlaces = DEFAULT_DECIMAL_PLACES
const fieldOptions = [
{internalName: 'time', displayName: 'Time', visible: true},
{internalName: 'f1', displayName: '', visible: false},
{internalName: 'f2', displayName: 'F2', visible: true},
]
const actual = transformTableData(data, sort, fieldNames, tableOptions)
const actual = transformTableData(
data,
sort,
fieldOptions,
tableOptions,
timeFormat,
decimalPlaces
)
const expected = [['time', 'f2'], [1000, 2000], [2000, 3000], [3000, 1000]]
@ -593,19 +602,23 @@ describe('transformTableData', () => {
]
const sort = {field: 'f1', direction: DEFAULT_SORT_DIRECTION}
const tableOptions = {
verticalTimeAxis: true,
timeFormat: DEFAULT_TIME_FORMAT,
}
const fieldNames = [
const tableOptions = {verticalTimeAxis: true}
const timeFormat = DEFAULT_TIME_FORMAT
const decimalPlaces = DEFAULT_DECIMAL_PLACES
const fieldOptions = [
{internalName: 'time', displayName: 'Time', visible: true},
{internalName: 'f1', displayName: '', visible: false},
{internalName: 'f2', displayName: 'F2', visible: true},
]
const actual = transformTableData(data, sort, fieldNames, tableOptions)
const actual = transformTableData(
data,
sort,
fieldOptions,
tableOptions,
timeFormat,
decimalPlaces
)
const expected = [['time', 'f2'], [2000, 3000], [3000, 1000], [1000, 2000]]
@ -623,19 +636,23 @@ describe('if verticalTimeAxis is false', () => {
]
const sort = {field: 'time', direction: DEFAULT_SORT_DIRECTION}
const tableOptions = {
verticalTimeAxis: false,
timeFormat: DEFAULT_TIME_FORMAT,
}
const fieldNames = [
const tableOptions = {verticalTimeAxis: false}
const timeFormat = DEFAULT_TIME_FORMAT
const decimalPlaces = DEFAULT_DECIMAL_PLACES
const fieldOptions = [
{internalName: 'time', displayName: 'Time', visible: true},
{internalName: 'f1', displayName: '', visible: true},
{internalName: 'f2', displayName: 'F2', visible: true},
]
const actual = transformTableData(data, sort, fieldNames, tableOptions)
const actual = transformTableData(
data,
sort,
fieldOptions,
tableOptions,
timeFormat,
decimalPlaces
)
const expected = [
['time', 1000, 2000, 3000],
@ -655,19 +672,23 @@ describe('if verticalTimeAxis is false', () => {
]
const sort = {field: 'f1', direction: DEFAULT_SORT_DIRECTION}
const tableOptions = {
verticalTimeAxis: false,
timeFormat: DEFAULT_TIME_FORMAT,
}
const fieldNames = [
const tableOptions = {verticalTimeAxis: false}
const timeFormat = DEFAULT_TIME_FORMAT
const decimalPlaces = DEFAULT_DECIMAL_PLACES
const fieldOptions = [
{internalName: 'time', displayName: 'Time', visible: true},
{internalName: 'f1', displayName: '', visible: false},
{internalName: 'f2', displayName: 'F2', visible: true},
]
const actual = transformTableData(data, sort, fieldNames, tableOptions)
const actual = transformTableData(
data,
sort,
fieldOptions,
tableOptions,
timeFormat,
decimalPlaces
)
const expected = [['time', 2000, 3000, 1000], ['f2', 3000, 1000, 2000]]