Merge branch 'master' into bugfix/task_enabled_correct_check
commit
9e9f566996
|
@ -22,3 +22,4 @@ chronograf*.db
|
|||
*_gen.go
|
||||
canned/apps_gen.go
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
|
|
@ -2,8 +2,12 @@
|
|||
|
||||
### Features
|
||||
|
||||
1. [#2973](https://github.com/influxdata/chronograf/pull/2973): Add unsafe SSL to Kapacitor UI configuration
|
||||
|
||||
### UI Improvements
|
||||
|
||||
1. [#2910](https://github.com/influxdata/chronograf/pull/2910): Redesign system notifications
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
1. [#2911](https://github.com/influxdata/chronograf/pull/2911): Fix Heroku OAuth
|
||||
|
@ -11,6 +15,7 @@
|
|||
1. [#2947](https://github.com/influxdata/chronograf/pull/2947): Fix Okta oauth2 provider support
|
||||
1. [#2866](https://github.com/influxdata/chronograf/pull/2866): Change hover text on delete mappings confirmation button to 'Delete'
|
||||
1. [#2919](https://github.com/influxdata/chronograf/pull/2919): Automatically add graph type 'line' to any graph missing a type
|
||||
1. [#2970](https://github.com/influxdata/chronograf/pull/2970): Fix hanging browser on docker host dashboard
|
||||
1. [#3006](https://github.com/influxdata/chronograf/pull/3006): Fix Kapacitor Rules task enabled checkboxes to only toggle exactly as clicked
|
||||
|
||||
## v1.4.2.3 [2018-03-08]
|
||||
|
|
2
Makefile
2
Makefile
|
@ -101,7 +101,7 @@ gotestrace:
|
|||
go test -race ./...
|
||||
|
||||
jstest:
|
||||
cd ui && yarn test
|
||||
cd ui && yarn test --runInBand
|
||||
|
||||
run: ${BINARY}
|
||||
./chronograf
|
||||
|
|
|
@ -75,14 +75,15 @@ func UnmarshalSource(data []byte, s *chronograf.Source) error {
|
|||
// MarshalServer encodes a server to binary protobuf format.
|
||||
func MarshalServer(s chronograf.Server) ([]byte, error) {
|
||||
return proto.Marshal(&Server{
|
||||
ID: int64(s.ID),
|
||||
SrcID: int64(s.SrcID),
|
||||
Name: s.Name,
|
||||
Username: s.Username,
|
||||
Password: s.Password,
|
||||
URL: s.URL,
|
||||
Active: s.Active,
|
||||
Organization: s.Organization,
|
||||
ID: int64(s.ID),
|
||||
SrcID: int64(s.SrcID),
|
||||
Name: s.Name,
|
||||
Username: s.Username,
|
||||
Password: s.Password,
|
||||
URL: s.URL,
|
||||
Active: s.Active,
|
||||
Organization: s.Organization,
|
||||
InsecureSkipVerify: s.InsecureSkipVerify,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -101,6 +102,7 @@ func UnmarshalServer(data []byte, s *chronograf.Server) error {
|
|||
s.URL = pb.URL
|
||||
s.Active = pb.Active
|
||||
s.Organization = pb.Organization
|
||||
s.InsecureSkipVerify = pb.InsecureSkipVerify
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -263,6 +265,27 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) {
|
|||
}
|
||||
}
|
||||
|
||||
sortBy := &TableColumn{
|
||||
InternalName: c.TableOptions.SortBy.InternalName,
|
||||
DisplayName: c.TableOptions.SortBy.DisplayName,
|
||||
}
|
||||
|
||||
columnNames := make([]*TableColumn, len(c.TableOptions.ColumnNames))
|
||||
for i, column := range c.TableOptions.ColumnNames {
|
||||
columnNames[i] = &TableColumn{
|
||||
InternalName: column.InternalName,
|
||||
DisplayName: column.DisplayName,
|
||||
}
|
||||
}
|
||||
|
||||
tableOptions := &TableOptions{
|
||||
TimeFormat: c.TableOptions.TimeFormat,
|
||||
VerticalTimeAxis: c.TableOptions.VerticalTimeAxis,
|
||||
SortBy: sortBy,
|
||||
Wrapping: c.TableOptions.Wrapping,
|
||||
ColumnNames: columnNames,
|
||||
}
|
||||
|
||||
cells[i] = &DashboardCell{
|
||||
ID: c.ID,
|
||||
X: c.X,
|
||||
|
@ -278,6 +301,7 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) {
|
|||
Type: c.Legend.Type,
|
||||
Orientation: c.Legend.Orientation,
|
||||
},
|
||||
TableOptions: tableOptions,
|
||||
}
|
||||
}
|
||||
templates := make([]*Template, len(d.Templates))
|
||||
|
@ -404,6 +428,28 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error {
|
|||
legend.Orientation = c.Legend.Orientation
|
||||
}
|
||||
|
||||
tableOptions := chronograf.TableOptions{}
|
||||
if c.TableOptions != nil {
|
||||
sortBy := chronograf.TableColumn{}
|
||||
if c.TableOptions.SortBy != nil {
|
||||
sortBy.InternalName = c.TableOptions.SortBy.InternalName
|
||||
sortBy.DisplayName = c.TableOptions.SortBy.DisplayName
|
||||
}
|
||||
tableOptions.SortBy = sortBy
|
||||
|
||||
columnNames := make([]chronograf.TableColumn, len(c.TableOptions.ColumnNames))
|
||||
for i, column := range c.TableOptions.ColumnNames {
|
||||
columnNames[i] = chronograf.TableColumn{}
|
||||
columnNames[i].InternalName = column.InternalName
|
||||
columnNames[i].DisplayName = column.DisplayName
|
||||
}
|
||||
tableOptions.ColumnNames = columnNames
|
||||
tableOptions.TimeFormat = c.TableOptions.TimeFormat
|
||||
tableOptions.VerticalTimeAxis = c.TableOptions.VerticalTimeAxis
|
||||
tableOptions.Wrapping = c.TableOptions.Wrapping
|
||||
|
||||
}
|
||||
|
||||
// FIXME: this is merely for legacy cells and
|
||||
// should be removed as soon as possible
|
||||
cellType := c.Type
|
||||
|
@ -412,17 +458,18 @@ 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,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ It has these top-level messages:
|
|||
Source
|
||||
Dashboard
|
||||
DashboardCell
|
||||
TableOptions
|
||||
TableColumn
|
||||
Color
|
||||
Legend
|
||||
Axis
|
||||
|
@ -210,17 +212,18 @@ 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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func (m *DashboardCell) Reset() { *m = DashboardCell{} }
|
||||
|
@ -305,6 +308,85 @@ func (m *DashboardCell) GetLegend() *Legend {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *DashboardCell) GetTableOptions() *TableOptions {
|
||||
if m != nil {
|
||||
return m.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 *TableColumn `protobuf:"bytes,3,opt,name=sortBy" json:"sortBy,omitempty"`
|
||||
Wrapping string `protobuf:"bytes,4,opt,name=wrapping,proto3" json:"wrapping,omitempty"`
|
||||
ColumnNames []*TableColumn `protobuf:"bytes,5,rep,name=columnNames" json:"columnNames,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{3} }
|
||||
|
||||
func (m *TableOptions) GetTimeFormat() string {
|
||||
if m != nil {
|
||||
return m.TimeFormat
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TableOptions) GetVerticalTimeAxis() bool {
|
||||
if m != nil {
|
||||
return m.VerticalTimeAxis
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *TableOptions) GetSortBy() *TableColumn {
|
||||
if m != nil {
|
||||
return m.SortBy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *TableOptions) GetWrapping() string {
|
||||
if m != nil {
|
||||
return m.Wrapping
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TableOptions) GetColumnNames() []*TableColumn {
|
||||
if m != nil {
|
||||
return m.ColumnNames
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TableColumn struct {
|
||||
InternalName string `protobuf:"bytes,1,opt,name=internalName,proto3" json:"internalName,omitempty"`
|
||||
DisplayName string `protobuf:"bytes,2,opt,name=displayName,proto3" json:"displayName,omitempty"`
|
||||
}
|
||||
|
||||
func (m *TableColumn) Reset() { *m = TableColumn{} }
|
||||
func (m *TableColumn) String() string { return proto.CompactTextString(m) }
|
||||
func (*TableColumn) ProtoMessage() {}
|
||||
func (*TableColumn) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{4} }
|
||||
|
||||
func (m *TableColumn) GetInternalName() string {
|
||||
if m != nil {
|
||||
return m.InternalName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *TableColumn) GetDisplayName() string {
|
||||
if m != nil {
|
||||
return m.DisplayName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Color struct {
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Type string `protobuf:"bytes,2,opt,name=Type,proto3" json:"Type,omitempty"`
|
||||
|
@ -316,7 +398,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{3} }
|
||||
func (*Color) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{5} }
|
||||
|
||||
func (m *Color) GetID() string {
|
||||
if m != nil {
|
||||
|
@ -361,7 +443,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{4} }
|
||||
func (*Legend) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{6} }
|
||||
|
||||
func (m *Legend) GetType() string {
|
||||
if m != nil {
|
||||
|
@ -390,7 +472,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{5} }
|
||||
func (*Axis) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{7} }
|
||||
|
||||
func (m *Axis) GetLegacyBounds() []int64 {
|
||||
if m != nil {
|
||||
|
@ -453,7 +535,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{6} }
|
||||
func (*Template) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{8} }
|
||||
|
||||
func (m *Template) GetID() string {
|
||||
if m != nil {
|
||||
|
@ -506,7 +588,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{7} }
|
||||
func (*TemplateValue) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{9} }
|
||||
|
||||
func (m *TemplateValue) GetType() string {
|
||||
if m != nil {
|
||||
|
@ -541,7 +623,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{8} }
|
||||
func (*TemplateQuery) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{10} }
|
||||
|
||||
func (m *TemplateQuery) GetCommand() string {
|
||||
if m != nil {
|
||||
|
@ -586,20 +668,21 @@ func (m *TemplateQuery) GetFieldKey() string {
|
|||
}
|
||||
|
||||
type Server struct {
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||
Username string `protobuf:"bytes,3,opt,name=Username,proto3" json:"Username,omitempty"`
|
||||
Password string `protobuf:"bytes,4,opt,name=Password,proto3" json:"Password,omitempty"`
|
||||
URL string `protobuf:"bytes,5,opt,name=URL,proto3" json:"URL,omitempty"`
|
||||
SrcID int64 `protobuf:"varint,6,opt,name=SrcID,proto3" json:"SrcID,omitempty"`
|
||||
Active bool `protobuf:"varint,7,opt,name=Active,proto3" json:"Active,omitempty"`
|
||||
Organization string `protobuf:"bytes,8,opt,name=Organization,proto3" json:"Organization,omitempty"`
|
||||
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||
Username string `protobuf:"bytes,3,opt,name=Username,proto3" json:"Username,omitempty"`
|
||||
Password string `protobuf:"bytes,4,opt,name=Password,proto3" json:"Password,omitempty"`
|
||||
URL string `protobuf:"bytes,5,opt,name=URL,proto3" json:"URL,omitempty"`
|
||||
SrcID int64 `protobuf:"varint,6,opt,name=SrcID,proto3" json:"SrcID,omitempty"`
|
||||
Active bool `protobuf:"varint,7,opt,name=Active,proto3" json:"Active,omitempty"`
|
||||
Organization string `protobuf:"bytes,8,opt,name=Organization,proto3" json:"Organization,omitempty"`
|
||||
InsecureSkipVerify bool `protobuf:"varint,9,opt,name=InsecureSkipVerify,proto3" json:"InsecureSkipVerify,omitempty"`
|
||||
}
|
||||
|
||||
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{9} }
|
||||
func (*Server) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{11} }
|
||||
|
||||
func (m *Server) GetID() int64 {
|
||||
if m != nil {
|
||||
|
@ -657,6 +740,13 @@ func (m *Server) GetOrganization() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (m *Server) GetInsecureSkipVerify() bool {
|
||||
if m != nil {
|
||||
return m.InsecureSkipVerify
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Layout struct {
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Application string `protobuf:"bytes,2,opt,name=Application,proto3" json:"Application,omitempty"`
|
||||
|
@ -668,7 +758,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{10} }
|
||||
func (*Layout) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{12} }
|
||||
|
||||
func (m *Layout) GetID() string {
|
||||
if m != nil {
|
||||
|
@ -722,7 +812,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{11} }
|
||||
func (*Cell) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{13} }
|
||||
|
||||
func (m *Cell) GetX() int32 {
|
||||
if m != nil {
|
||||
|
@ -816,7 +906,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{12} }
|
||||
func (*Query) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{14} }
|
||||
|
||||
func (m *Query) GetCommand() string {
|
||||
if m != nil {
|
||||
|
@ -890,7 +980,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{13} }
|
||||
func (*TimeShift) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{15} }
|
||||
|
||||
func (m *TimeShift) GetLabel() string {
|
||||
if m != nil {
|
||||
|
@ -921,7 +1011,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{14} }
|
||||
func (*Range) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{16} }
|
||||
|
||||
func (m *Range) GetUpper() int64 {
|
||||
if m != nil {
|
||||
|
@ -947,7 +1037,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{15} }
|
||||
func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{17} }
|
||||
|
||||
func (m *AlertRule) GetID() string {
|
||||
if m != nil {
|
||||
|
@ -989,7 +1079,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{16} }
|
||||
func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{18} }
|
||||
|
||||
func (m *User) GetID() uint64 {
|
||||
if m != nil {
|
||||
|
@ -1041,7 +1131,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{17} }
|
||||
func (*Role) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{19} }
|
||||
|
||||
func (m *Role) GetOrganization() string {
|
||||
if m != nil {
|
||||
|
@ -1068,7 +1158,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{18} }
|
||||
func (*Mapping) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{20} }
|
||||
|
||||
func (m *Mapping) GetProvider() string {
|
||||
if m != nil {
|
||||
|
@ -1114,7 +1204,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{19} }
|
||||
func (*Organization) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{21} }
|
||||
|
||||
func (m *Organization) GetID() string {
|
||||
if m != nil {
|
||||
|
@ -1144,7 +1234,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{20} }
|
||||
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{22} }
|
||||
|
||||
func (m *Config) GetAuth() *AuthConfig {
|
||||
if m != nil {
|
||||
|
@ -1160,7 +1250,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{21} }
|
||||
func (*AuthConfig) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{23} }
|
||||
|
||||
func (m *AuthConfig) GetSuperAdminNewUsers() bool {
|
||||
if m != nil {
|
||||
|
@ -1177,7 +1267,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{22} }
|
||||
func (*BuildInfo) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{24} }
|
||||
|
||||
func (m *BuildInfo) GetVersion() string {
|
||||
if m != nil {
|
||||
|
@ -1197,6 +1287,8 @@ func init() {
|
|||
proto.RegisterType((*Source)(nil), "internal.Source")
|
||||
proto.RegisterType((*Dashboard)(nil), "internal.Dashboard")
|
||||
proto.RegisterType((*DashboardCell)(nil), "internal.DashboardCell")
|
||||
proto.RegisterType((*TableOptions)(nil), "internal.TableOptions")
|
||||
proto.RegisterType((*TableColumn)(nil), "internal.TableColumn")
|
||||
proto.RegisterType((*Color)(nil), "internal.Color")
|
||||
proto.RegisterType((*Legend)(nil), "internal.Legend")
|
||||
proto.RegisterType((*Axis)(nil), "internal.Axis")
|
||||
|
@ -1222,93 +1314,103 @@ func init() {
|
|||
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
|
||||
|
||||
var fileDescriptorInternal = []byte{
|
||||
// 1406 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x5f, 0x8f, 0xdb, 0x44,
|
||||
0x10, 0x97, 0x63, 0x3b, 0x89, 0x27, 0xd7, 0x52, 0x99, 0x13, 0x35, 0x45, 0x42, 0xc1, 0x02, 0x11,
|
||||
0x04, 0x3d, 0xd0, 0x55, 0x48, 0x08, 0x41, 0xa5, 0xdc, 0x05, 0x95, 0xa3, 0xd7, 0xf6, 0xba, 0xb9,
|
||||
0x3b, 0x9e, 0x50, 0xb5, 0x97, 0x4c, 0x12, 0xab, 0x8e, 0x6d, 0xd6, 0xf6, 0x5d, 0xcc, 0x87, 0x41,
|
||||
0x42, 0x82, 0x2f, 0x80, 0x78, 0xe7, 0x15, 0xf1, 0x41, 0xf8, 0x0a, 0x3c, 0x21, 0xa1, 0xd9, 0x5d,
|
||||
0xff, 0xc9, 0x25, 0xad, 0xfa, 0x80, 0x78, 0xdb, 0xdf, 0xcc, 0x66, 0x76, 0xfe, 0xfc, 0x66, 0xc6,
|
||||
0x81, 0x9b, 0x41, 0x94, 0xa1, 0x88, 0x78, 0xb8, 0x97, 0x88, 0x38, 0x8b, 0xdd, 0x6e, 0x89, 0xfd,
|
||||
0xbf, 0x5a, 0xd0, 0x1e, 0xc7, 0xb9, 0x98, 0xa0, 0x7b, 0x13, 0x5a, 0x47, 0x23, 0xcf, 0xe8, 0x1b,
|
||||
0x03, 0x93, 0xb5, 0x8e, 0x46, 0xae, 0x0b, 0xd6, 0x63, 0xbe, 0x44, 0xaf, 0xd5, 0x37, 0x06, 0x0e,
|
||||
0x93, 0x67, 0x92, 0x9d, 0x16, 0x09, 0x7a, 0xa6, 0x92, 0xd1, 0xd9, 0xbd, 0x03, 0xdd, 0xb3, 0x94,
|
||||
0xac, 0x2d, 0xd1, 0xb3, 0xa4, 0xbc, 0xc2, 0xa4, 0x3b, 0xe1, 0x69, 0x7a, 0x15, 0x8b, 0xa9, 0x67,
|
||||
0x2b, 0x5d, 0x89, 0xdd, 0x5b, 0x60, 0x9e, 0xb1, 0x63, 0xaf, 0x2d, 0xc5, 0x74, 0x74, 0x3d, 0xe8,
|
||||
0x8c, 0x70, 0xc6, 0xf3, 0x30, 0xf3, 0x3a, 0x7d, 0x63, 0xd0, 0x65, 0x25, 0x24, 0x3b, 0xa7, 0x18,
|
||||
0xe2, 0x5c, 0xf0, 0x99, 0xd7, 0x55, 0x76, 0x4a, 0xec, 0xee, 0x81, 0x7b, 0x14, 0xa5, 0x38, 0xc9,
|
||||
0x05, 0x8e, 0x9f, 0x07, 0xc9, 0x39, 0x8a, 0x60, 0x56, 0x78, 0x8e, 0x34, 0xb0, 0x45, 0x43, 0xaf,
|
||||
0x3c, 0xc2, 0x8c, 0xd3, 0xdb, 0x20, 0x4d, 0x95, 0xd0, 0xf5, 0x61, 0x67, 0xbc, 0xe0, 0x02, 0xa7,
|
||||
0x63, 0x9c, 0x08, 0xcc, 0xbc, 0x9e, 0x54, 0xaf, 0xc9, 0xe8, 0xce, 0x13, 0x31, 0xe7, 0x51, 0xf0,
|
||||
0x03, 0xcf, 0x82, 0x38, 0xf2, 0x76, 0xd4, 0x9d, 0xa6, 0x8c, 0xb2, 0xc4, 0xe2, 0x10, 0xbd, 0x1b,
|
||||
0x2a, 0x4b, 0x74, 0xf6, 0x7f, 0x33, 0xc0, 0x19, 0xf1, 0x74, 0x71, 0x11, 0x73, 0x31, 0x7d, 0xa5,
|
||||
0x5c, 0xdf, 0x05, 0x7b, 0x82, 0x61, 0x98, 0x7a, 0x66, 0xdf, 0x1c, 0xf4, 0xf6, 0x6f, 0xef, 0x55,
|
||||
0x45, 0xac, 0xec, 0x1c, 0x62, 0x18, 0x32, 0x75, 0xcb, 0xfd, 0x04, 0x9c, 0x0c, 0x97, 0x49, 0xc8,
|
||||
0x33, 0x4c, 0x3d, 0x4b, 0xfe, 0xc4, 0xad, 0x7f, 0x72, 0xaa, 0x55, 0xac, 0xbe, 0xb4, 0x11, 0x8a,
|
||||
0xbd, 0x19, 0x8a, 0xff, 0x4f, 0x0b, 0x6e, 0xac, 0x3d, 0xe7, 0xee, 0x80, 0xb1, 0x92, 0x9e, 0xdb,
|
||||
0xcc, 0x58, 0x11, 0x2a, 0xa4, 0xd7, 0x36, 0x33, 0x0a, 0x42, 0x57, 0x92, 0x1b, 0x36, 0x33, 0xae,
|
||||
0x08, 0x2d, 0x24, 0x23, 0x6c, 0x66, 0x2c, 0xdc, 0x0f, 0xa0, 0xf3, 0x7d, 0x8e, 0x22, 0xc0, 0xd4,
|
||||
0xb3, 0xa5, 0x77, 0xaf, 0xd5, 0xde, 0x3d, 0xcd, 0x51, 0x14, 0xac, 0xd4, 0x53, 0x36, 0x24, 0x9b,
|
||||
0x14, 0x35, 0xe4, 0x99, 0x64, 0x19, 0x31, 0xaf, 0xa3, 0x64, 0x74, 0xd6, 0x59, 0x54, 0x7c, 0xa0,
|
||||
0x2c, 0x7e, 0x0a, 0x16, 0x5f, 0x61, 0xea, 0x39, 0xd2, 0xfe, 0x3b, 0x2f, 0x48, 0xd8, 0xde, 0x70,
|
||||
0x85, 0xe9, 0x57, 0x51, 0x26, 0x0a, 0x26, 0xaf, 0xbb, 0xef, 0x43, 0x7b, 0x12, 0x87, 0xb1, 0x48,
|
||||
0x3d, 0xb8, 0xee, 0xd8, 0x21, 0xc9, 0x99, 0x56, 0xbb, 0x03, 0x68, 0x87, 0x38, 0xc7, 0x68, 0x2a,
|
||||
0x99, 0xd1, 0xdb, 0xbf, 0x55, 0x5f, 0x3c, 0x96, 0x72, 0xa6, 0xf5, 0x77, 0x1e, 0x80, 0x53, 0xbd,
|
||||
0x42, 0x44, 0x7f, 0x8e, 0x85, 0xcc, 0x99, 0xc3, 0xe8, 0xe8, 0xbe, 0x0b, 0xf6, 0x25, 0x0f, 0x73,
|
||||
0x55, 0xef, 0xde, 0xfe, 0xcd, 0xda, 0xce, 0x70, 0x15, 0xa4, 0x4c, 0x29, 0x3f, 0x6f, 0x7d, 0x66,
|
||||
0xf8, 0x73, 0xb0, 0xa5, 0x0f, 0x0d, 0xc6, 0x38, 0x25, 0x63, 0x64, 0x27, 0xb6, 0x1a, 0x9d, 0x78,
|
||||
0x0b, 0xcc, 0xaf, 0x71, 0xa5, 0x9b, 0x93, 0x8e, 0x15, 0xaf, 0xac, 0x06, 0xaf, 0x76, 0xc1, 0x3e,
|
||||
0x97, 0x8f, 0xab, 0x7a, 0x2b, 0xe0, 0xdf, 0x87, 0xb6, 0x8a, 0xa1, 0xb2, 0x6c, 0x34, 0x2c, 0xf7,
|
||||
0xa1, 0xf7, 0x44, 0x04, 0x18, 0x65, 0x8a, 0x29, 0xea, 0xd1, 0xa6, 0xc8, 0xff, 0xd5, 0x00, 0x8b,
|
||||
0x9c, 0x27, 0x56, 0x85, 0x38, 0xe7, 0x93, 0xe2, 0x20, 0xce, 0xa3, 0x69, 0xea, 0x19, 0x7d, 0x73,
|
||||
0x60, 0xb2, 0x35, 0x99, 0xfb, 0x06, 0xb4, 0x2f, 0x94, 0xb6, 0xd5, 0x37, 0x07, 0x0e, 0xd3, 0x88,
|
||||
0x5c, 0x0b, 0xf9, 0x05, 0x86, 0x3a, 0x04, 0x05, 0xe8, 0x76, 0x22, 0x70, 0x16, 0xac, 0x74, 0x18,
|
||||
0x1a, 0x91, 0x3c, 0xcd, 0x67, 0x24, 0x57, 0x91, 0x68, 0x44, 0x01, 0x5c, 0xf0, 0xb4, 0xa2, 0x0f,
|
||||
0x9d, 0xc9, 0x72, 0x3a, 0xe1, 0x61, 0xc9, 0x1f, 0x05, 0xfc, 0xdf, 0x0d, 0x9a, 0x2b, 0xaa, 0x1f,
|
||||
0x36, 0x32, 0xfc, 0x26, 0x74, 0xa9, 0x57, 0x9e, 0x5d, 0x72, 0xa1, 0x03, 0xee, 0x10, 0x3e, 0xe7,
|
||||
0xc2, 0xfd, 0x18, 0xda, 0xb2, 0x44, 0x5b, 0x7a, 0xb3, 0x34, 0x27, 0xb3, 0xca, 0xf4, 0xb5, 0x8a,
|
||||
0xbd, 0x56, 0x83, 0xbd, 0x55, 0xb0, 0x76, 0x33, 0xd8, 0xbb, 0x60, 0x53, 0x1b, 0x14, 0xd2, 0xfb,
|
||||
0xad, 0x96, 0x55, 0xb3, 0xa8, 0x5b, 0xfe, 0x19, 0xdc, 0x58, 0x7b, 0xb1, 0x7a, 0xc9, 0x58, 0x7f,
|
||||
0xa9, 0xa6, 0x9b, 0xa3, 0xe9, 0x45, 0x33, 0x35, 0xc5, 0x10, 0x27, 0x19, 0x4e, 0x65, 0xbe, 0xbb,
|
||||
0xac, 0xc2, 0xfe, 0x4f, 0x46, 0x6d, 0x57, 0xbe, 0x47, 0x53, 0x73, 0x12, 0x2f, 0x97, 0x3c, 0x9a,
|
||||
0x6a, 0xd3, 0x25, 0xa4, 0xbc, 0x4d, 0x2f, 0xb4, 0xe9, 0xd6, 0xf4, 0x82, 0xb0, 0x48, 0x74, 0x05,
|
||||
0x5b, 0x22, 0x21, 0xee, 0x2c, 0x91, 0xa7, 0xb9, 0xc0, 0x25, 0x46, 0x99, 0x4e, 0x41, 0x53, 0xe4,
|
||||
0xde, 0x86, 0x4e, 0xc6, 0xe7, 0xcf, 0xa8, 0x49, 0x74, 0x25, 0x33, 0x3e, 0x7f, 0x88, 0x85, 0xfb,
|
||||
0x16, 0x38, 0xb3, 0x00, 0xc3, 0xa9, 0x54, 0xa9, 0x72, 0x76, 0xa5, 0xe0, 0x21, 0x16, 0xfe, 0x1f,
|
||||
0x06, 0xb4, 0xc7, 0x28, 0x2e, 0x51, 0xbc, 0xd2, 0x38, 0x6d, 0xae, 0x29, 0xf3, 0x25, 0x6b, 0xca,
|
||||
0xda, 0xbe, 0xa6, 0xec, 0x7a, 0x4d, 0xed, 0x82, 0x3d, 0x16, 0x93, 0xa3, 0x91, 0xf4, 0xc8, 0x64,
|
||||
0x0a, 0x10, 0x1b, 0x87, 0x93, 0x2c, 0xb8, 0x44, 0xbd, 0xbb, 0x34, 0xda, 0x98, 0xb2, 0xdd, 0x2d,
|
||||
0x53, 0xf6, 0x47, 0x03, 0xda, 0xc7, 0xbc, 0x88, 0xf3, 0x6c, 0x83, 0x85, 0x7d, 0xe8, 0x0d, 0x93,
|
||||
0x24, 0x0c, 0x26, 0x6b, 0x9d, 0xd7, 0x10, 0xd1, 0x8d, 0x47, 0x8d, 0xfc, 0xaa, 0xd8, 0x9a, 0x22,
|
||||
0x1a, 0x37, 0x87, 0x72, 0x93, 0xa8, 0xb5, 0xd0, 0x18, 0x37, 0x6a, 0x81, 0x48, 0x25, 0x25, 0x61,
|
||||
0x98, 0x67, 0xf1, 0x2c, 0x8c, 0xaf, 0x64, 0xb4, 0x5d, 0x56, 0x61, 0xff, 0xcf, 0x16, 0x58, 0xff,
|
||||
0xd7, 0xf4, 0xdf, 0x01, 0x23, 0xd0, 0xc5, 0x36, 0x82, 0x6a, 0x17, 0x74, 0x1a, 0xbb, 0xc0, 0x83,
|
||||
0x4e, 0x21, 0x78, 0x34, 0xc7, 0xd4, 0xeb, 0xca, 0xe9, 0x52, 0x42, 0xa9, 0x91, 0x7d, 0xa4, 0x96,
|
||||
0x80, 0xc3, 0x4a, 0x58, 0xf5, 0x05, 0x34, 0xfa, 0xe2, 0x23, 0xbd, 0x2f, 0x7a, 0xd2, 0x23, 0x6f,
|
||||
0x3d, 0x2d, 0xd7, 0xd7, 0xc4, 0x7f, 0x37, 0xd3, 0xff, 0x36, 0xc0, 0xae, 0x9a, 0xea, 0x70, 0xbd,
|
||||
0xa9, 0x0e, 0xeb, 0xa6, 0x1a, 0x1d, 0x94, 0x4d, 0x35, 0x3a, 0x20, 0xcc, 0x4e, 0xca, 0xa6, 0x62,
|
||||
0x27, 0x54, 0xac, 0x07, 0x22, 0xce, 0x93, 0x83, 0x42, 0x55, 0xd5, 0x61, 0x15, 0x26, 0x26, 0x7e,
|
||||
0xbb, 0x40, 0xa1, 0x53, 0xed, 0x30, 0x8d, 0x88, 0xb7, 0xc7, 0x72, 0xe0, 0xa8, 0xe4, 0x2a, 0xe0,
|
||||
0xbe, 0x07, 0x36, 0xa3, 0xe4, 0xc9, 0x0c, 0xaf, 0xd5, 0x45, 0x8a, 0x99, 0xd2, 0x92, 0x51, 0xf5,
|
||||
0x9d, 0xa8, 0x09, 0x5c, 0x7e, 0x35, 0x7e, 0x08, 0xed, 0xf1, 0x22, 0x98, 0x65, 0xe5, 0xd6, 0x7d,
|
||||
0xbd, 0x31, 0xb0, 0x82, 0x25, 0x4a, 0x1d, 0xd3, 0x57, 0xfc, 0xa7, 0xe0, 0x54, 0xc2, 0xda, 0x1d,
|
||||
0xa3, 0xe9, 0x8e, 0x0b, 0xd6, 0x59, 0x14, 0x64, 0x65, 0xeb, 0xd2, 0x99, 0x82, 0x7d, 0x9a, 0xf3,
|
||||
0x28, 0x0b, 0xb2, 0xa2, 0x6c, 0xdd, 0x12, 0xfb, 0xf7, 0xb4, 0xfb, 0x64, 0xee, 0x2c, 0x49, 0x50,
|
||||
0xe8, 0x31, 0xa0, 0x80, 0x7c, 0x24, 0xbe, 0x42, 0x35, 0xc1, 0x4d, 0xa6, 0x80, 0xff, 0x1d, 0x38,
|
||||
0xc3, 0x10, 0x45, 0xc6, 0xf2, 0x10, 0xb7, 0x6d, 0xd6, 0x6f, 0xc6, 0x4f, 0x1e, 0x97, 0x1e, 0xd0,
|
||||
0xb9, 0x6e, 0x79, 0xf3, 0x5a, 0xcb, 0x3f, 0xe4, 0x09, 0x3f, 0x1a, 0x49, 0x9e, 0x9b, 0x4c, 0x23,
|
||||
0xff, 0x67, 0x03, 0x2c, 0x9a, 0x2d, 0x0d, 0xd3, 0xd6, 0xcb, 0xe6, 0xd2, 0x89, 0x88, 0x2f, 0x83,
|
||||
0x29, 0x8a, 0x32, 0xb8, 0x12, 0xcb, 0xa4, 0x4f, 0x16, 0x58, 0x2d, 0x70, 0x8d, 0x88, 0x6b, 0xf4,
|
||||
0x51, 0x59, 0xf6, 0x52, 0x83, 0x6b, 0x24, 0x66, 0x4a, 0xe9, 0xbe, 0x0d, 0x30, 0xce, 0x13, 0x14,
|
||||
0xc3, 0xe9, 0x32, 0x88, 0x64, 0xd1, 0xbb, 0xac, 0x21, 0xf1, 0xef, 0xab, 0xcf, 0xd4, 0x8d, 0x09,
|
||||
0x65, 0x6c, 0xff, 0xa4, 0xbd, 0xee, 0xb9, 0xff, 0x8b, 0x01, 0x9d, 0x47, 0x3c, 0x49, 0x82, 0x68,
|
||||
0xbe, 0x16, 0x85, 0xf1, 0xc2, 0x28, 0x5a, 0x6b, 0x51, 0xec, 0xc3, 0x6e, 0x79, 0x67, 0xed, 0x7d,
|
||||
0x95, 0x85, 0xad, 0x3a, 0x9d, 0x51, 0xab, 0x2a, 0xd6, 0xab, 0x7c, 0xc3, 0x9e, 0xae, 0xdf, 0xd9,
|
||||
0x56, 0xf0, 0x8d, 0xaa, 0xf4, 0xa1, 0xa7, 0xff, 0x7b, 0xc8, 0x2f, 0x79, 0x3d, 0x54, 0x1b, 0x22,
|
||||
0x7f, 0x1f, 0xda, 0x87, 0x71, 0x34, 0x0b, 0xe6, 0xee, 0x00, 0xac, 0x61, 0x9e, 0x2d, 0xa4, 0xc5,
|
||||
0xde, 0xfe, 0x6e, 0xa3, 0xf1, 0xf3, 0x6c, 0xa1, 0xee, 0x30, 0x79, 0xc3, 0xff, 0x02, 0xa0, 0x96,
|
||||
0xd1, 0x1f, 0x97, 0xba, 0x1a, 0x8f, 0xf1, 0x8a, 0x28, 0x93, 0x4a, 0x2b, 0x5d, 0xb6, 0x45, 0xe3,
|
||||
0x7f, 0x09, 0xce, 0x41, 0x1e, 0x84, 0xd3, 0xa3, 0x68, 0x16, 0xd3, 0xe8, 0x38, 0x47, 0x91, 0xd6,
|
||||
0xf5, 0x2a, 0x21, 0xa5, 0x9b, 0xa6, 0x48, 0xd5, 0x43, 0x1a, 0x5d, 0xb4, 0xe5, 0x7f, 0xbf, 0x7b,
|
||||
0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xe9, 0xd1, 0x8f, 0x0d, 0x0e, 0x00, 0x00,
|
||||
// 1558 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x5f, 0x6f, 0xdb, 0x46,
|
||||
0x12, 0x07, 0x25, 0x51, 0x12, 0x47, 0x4e, 0xce, 0xe0, 0xf9, 0x12, 0x5e, 0x0e, 0x38, 0xe8, 0x88,
|
||||
0x3b, 0x9c, 0xee, 0x4f, 0x7c, 0x07, 0x05, 0x45, 0x8b, 0xa0, 0x0d, 0x20, 0x5b, 0x6d, 0xea, 0xc6,
|
||||
0x89, 0x9d, 0x95, 0xed, 0x3e, 0x15, 0xc1, 0x4a, 0x1a, 0x49, 0x44, 0x28, 0x92, 0x5d, 0x92, 0xb6,
|
||||
0xd9, 0x0f, 0x53, 0xa0, 0x40, 0xfb, 0x05, 0x8a, 0xbe, 0xf4, 0xa9, 0xef, 0xfd, 0x10, 0x7d, 0xec,
|
||||
0x57, 0x68, 0x1f, 0x8b, 0xd9, 0x5d, 0x52, 0x2b, 0x4b, 0x09, 0x52, 0xa0, 0xe8, 0xdb, 0xfe, 0x66,
|
||||
0x86, 0xb3, 0xf3, 0x7f, 0x96, 0x70, 0x3b, 0x88, 0x32, 0x14, 0x11, 0x0f, 0xf7, 0x13, 0x11, 0x67,
|
||||
0xb1, 0xdb, 0x2e, 0xb1, 0xff, 0x63, 0x0d, 0x9a, 0xa3, 0x38, 0x17, 0x13, 0x74, 0x6f, 0x43, 0xed,
|
||||
0x68, 0xe8, 0x59, 0x5d, 0xab, 0x57, 0x67, 0xb5, 0xa3, 0xa1, 0xeb, 0x42, 0xe3, 0x19, 0x5f, 0xa2,
|
||||
0x57, 0xeb, 0x5a, 0x3d, 0x87, 0xc9, 0x33, 0xd1, 0xce, 0x8a, 0x04, 0xbd, 0xba, 0xa2, 0xd1, 0xd9,
|
||||
0xbd, 0x07, 0xed, 0xf3, 0x94, 0xb4, 0x2d, 0xd1, 0x6b, 0x48, 0x7a, 0x85, 0x89, 0x77, 0xca, 0xd3,
|
||||
0xf4, 0x2a, 0x16, 0x53, 0xcf, 0x56, 0xbc, 0x12, 0xbb, 0xbb, 0x50, 0x3f, 0x67, 0xc7, 0x5e, 0x53,
|
||||
0x92, 0xe9, 0xe8, 0x7a, 0xd0, 0x1a, 0xe2, 0x8c, 0xe7, 0x61, 0xe6, 0xb5, 0xba, 0x56, 0xaf, 0xcd,
|
||||
0x4a, 0x48, 0x7a, 0xce, 0x30, 0xc4, 0xb9, 0xe0, 0x33, 0xaf, 0xad, 0xf4, 0x94, 0xd8, 0xdd, 0x07,
|
||||
0xf7, 0x28, 0x4a, 0x71, 0x92, 0x0b, 0x1c, 0xbd, 0x0c, 0x92, 0x0b, 0x14, 0xc1, 0xac, 0xf0, 0x1c,
|
||||
0xa9, 0x60, 0x0b, 0x87, 0x6e, 0x79, 0x8a, 0x19, 0xa7, 0xbb, 0x41, 0xaa, 0x2a, 0xa1, 0xeb, 0xc3,
|
||||
0xce, 0x68, 0xc1, 0x05, 0x4e, 0x47, 0x38, 0x11, 0x98, 0x79, 0x1d, 0xc9, 0x5e, 0xa3, 0x91, 0xcc,
|
||||
0x89, 0x98, 0xf3, 0x28, 0xf8, 0x8c, 0x67, 0x41, 0x1c, 0x79, 0x3b, 0x4a, 0xc6, 0xa4, 0x51, 0x94,
|
||||
0x58, 0x1c, 0xa2, 0x77, 0x4b, 0x45, 0x89, 0xce, 0xfe, 0x37, 0x16, 0x38, 0x43, 0x9e, 0x2e, 0xc6,
|
||||
0x31, 0x17, 0xd3, 0x37, 0x8a, 0xf5, 0x7d, 0xb0, 0x27, 0x18, 0x86, 0xa9, 0x57, 0xef, 0xd6, 0x7b,
|
||||
0x9d, 0xfe, 0xdd, 0xfd, 0x2a, 0x89, 0x95, 0x9e, 0x43, 0x0c, 0x43, 0xa6, 0xa4, 0xdc, 0xff, 0x83,
|
||||
0x93, 0xe1, 0x32, 0x09, 0x79, 0x86, 0xa9, 0xd7, 0x90, 0x9f, 0xb8, 0xab, 0x4f, 0xce, 0x34, 0x8b,
|
||||
0xad, 0x84, 0x36, 0x5c, 0xb1, 0x37, 0x5d, 0xf1, 0xbf, 0xad, 0xc3, 0xad, 0xb5, 0xeb, 0xdc, 0x1d,
|
||||
0xb0, 0xae, 0xa5, 0xe5, 0x36, 0xb3, 0xae, 0x09, 0x15, 0xd2, 0x6a, 0x9b, 0x59, 0x05, 0xa1, 0x2b,
|
||||
0x59, 0x1b, 0x36, 0xb3, 0xae, 0x08, 0x2d, 0x64, 0x45, 0xd8, 0xcc, 0x5a, 0xb8, 0xff, 0x82, 0xd6,
|
||||
0xa7, 0x39, 0x8a, 0x00, 0x53, 0xcf, 0x96, 0xd6, 0xfd, 0x61, 0x65, 0xdd, 0xf3, 0x1c, 0x45, 0xc1,
|
||||
0x4a, 0x3e, 0x45, 0x43, 0x56, 0x93, 0x2a, 0x0d, 0x79, 0x26, 0x5a, 0x46, 0x95, 0xd7, 0x52, 0x34,
|
||||
0x3a, 0xeb, 0x28, 0xaa, 0x7a, 0xa0, 0x28, 0xbe, 0x05, 0x0d, 0x7e, 0x8d, 0xa9, 0xe7, 0x48, 0xfd,
|
||||
0x7f, 0x7b, 0x45, 0xc0, 0xf6, 0x07, 0xd7, 0x98, 0xbe, 0x1f, 0x65, 0xa2, 0x60, 0x52, 0xdc, 0xfd,
|
||||
0x27, 0x34, 0x27, 0x71, 0x18, 0x8b, 0xd4, 0x83, 0x9b, 0x86, 0x1d, 0x12, 0x9d, 0x69, 0xb6, 0xdb,
|
||||
0x83, 0x66, 0x88, 0x73, 0x8c, 0xa6, 0xb2, 0x32, 0x3a, 0xfd, 0xdd, 0x95, 0xe0, 0xb1, 0xa4, 0x33,
|
||||
0xcd, 0x77, 0x1f, 0xc2, 0x4e, 0xc6, 0xc7, 0x21, 0x9e, 0x24, 0x14, 0xc5, 0x54, 0x56, 0x49, 0xa7,
|
||||
0x7f, 0xc7, 0xc8, 0x87, 0xc1, 0x65, 0x6b, 0xb2, 0xf7, 0x1e, 0x83, 0x53, 0x59, 0x48, 0x4d, 0xf2,
|
||||
0x12, 0x0b, 0x19, 0x6f, 0x87, 0xd1, 0xd1, 0xfd, 0x3b, 0xd8, 0x97, 0x3c, 0xcc, 0x55, 0xad, 0x74,
|
||||
0xfa, 0xb7, 0x57, 0x3a, 0x07, 0xd7, 0x41, 0xca, 0x14, 0xf3, 0x61, 0xed, 0x1d, 0xcb, 0xff, 0xc1,
|
||||
0x82, 0x1d, 0xf3, 0x1e, 0xf7, 0xaf, 0x00, 0x59, 0xb0, 0xc4, 0x0f, 0x62, 0xb1, 0xe4, 0x99, 0xd6,
|
||||
0x69, 0x50, 0xdc, 0x7f, 0xc3, 0xee, 0x25, 0x8a, 0x2c, 0x98, 0xf0, 0xf0, 0x2c, 0x58, 0x22, 0xe9,
|
||||
0x93, 0xb7, 0xb4, 0xd9, 0x06, 0xdd, 0xbd, 0x0f, 0xcd, 0x34, 0x16, 0xd9, 0x41, 0x21, 0xf3, 0xdd,
|
||||
0xe9, 0xff, 0xe9, 0x86, 0x6f, 0x87, 0x71, 0x98, 0x2f, 0x23, 0xa6, 0x85, 0xa8, 0x81, 0xaf, 0x04,
|
||||
0x4f, 0x92, 0x20, 0x9a, 0x97, 0x43, 0xa2, 0xc4, 0xee, 0xdb, 0xd0, 0x99, 0x48, 0x69, 0x2a, 0xfb,
|
||||
0xb2, 0x3a, 0x5e, 0xa1, 0xcf, 0x94, 0xf4, 0x47, 0xd0, 0x31, 0x78, 0x54, 0xcf, 0xe5, 0x37, 0xb2,
|
||||
0x99, 0x94, 0x83, 0x6b, 0x34, 0xb7, 0x0b, 0x9d, 0x69, 0x90, 0x26, 0x21, 0x2f, 0x8c, 0x7e, 0x33,
|
||||
0x49, 0xfe, 0x1c, 0x6c, 0x99, 0x75, 0xa3, 0x47, 0x9d, 0xb2, 0x47, 0xe5, 0xec, 0xab, 0x19, 0xb3,
|
||||
0x6f, 0x17, 0xea, 0x1f, 0xe2, 0xb5, 0x1e, 0x87, 0x74, 0xac, 0x3a, 0xb9, 0x61, 0x74, 0xf2, 0x1e,
|
||||
0xd8, 0x17, 0x32, 0x65, 0xaa, 0xc3, 0x14, 0xf0, 0x1f, 0x41, 0x53, 0x55, 0x4d, 0xa5, 0xd9, 0x32,
|
||||
0x34, 0x77, 0xa1, 0x73, 0x22, 0x02, 0x8c, 0x32, 0xd5, 0x9b, 0xda, 0x50, 0x83, 0xe4, 0x7f, 0x6d,
|
||||
0x41, 0x43, 0xa6, 0xc2, 0x87, 0x9d, 0x10, 0xe7, 0x7c, 0x52, 0x1c, 0xc4, 0x79, 0x34, 0x4d, 0x3d,
|
||||
0xab, 0x5b, 0xef, 0xd5, 0xd9, 0x1a, 0xcd, 0xbd, 0x03, 0xcd, 0xb1, 0xe2, 0xd6, 0xba, 0xf5, 0x9e,
|
||||
0xc3, 0x34, 0x22, 0xd3, 0x42, 0x3e, 0xc6, 0x50, 0xbb, 0xa0, 0x00, 0x49, 0x27, 0x02, 0x67, 0xc1,
|
||||
0xb5, 0x76, 0x43, 0x23, 0xa2, 0xa7, 0xf9, 0x8c, 0xe8, 0xca, 0x13, 0x8d, 0xc8, 0x81, 0x31, 0x4f,
|
||||
0xab, 0x86, 0xa5, 0x33, 0x69, 0x4e, 0x27, 0x3c, 0x2c, 0x3b, 0x56, 0x01, 0xff, 0x3b, 0x8b, 0x26,
|
||||
0xb9, 0x9a, 0x40, 0x1b, 0x11, 0xfe, 0x33, 0xb4, 0x69, 0x3a, 0xbd, 0xb8, 0xe4, 0x42, 0x3b, 0xdc,
|
||||
0x22, 0x7c, 0xc1, 0x85, 0xfb, 0x3f, 0x68, 0xca, 0xc2, 0xde, 0x32, 0x0d, 0x4b, 0x75, 0x32, 0xaa,
|
||||
0x4c, 0x8b, 0x55, 0xf3, 0xa2, 0x61, 0xcc, 0x8b, 0xca, 0x59, 0xdb, 0x74, 0xf6, 0x3e, 0xd8, 0x34,
|
||||
0x78, 0x0a, 0x69, 0xfd, 0x56, 0xcd, 0x6a, 0x3c, 0x29, 0x29, 0xff, 0x1c, 0x6e, 0xad, 0xdd, 0x58,
|
||||
0xdd, 0x64, 0xad, 0xdf, 0xb4, 0x6a, 0x52, 0x47, 0x37, 0x25, 0x35, 0x41, 0x8a, 0x21, 0x4e, 0x32,
|
||||
0x9c, 0xca, 0x78, 0xb7, 0x59, 0x85, 0xfd, 0x2f, 0xac, 0x95, 0x5e, 0x79, 0x1f, 0xed, 0xa9, 0x49,
|
||||
0xbc, 0x5c, 0xf2, 0x68, 0xaa, 0x55, 0x97, 0x90, 0xe2, 0x36, 0x1d, 0x6b, 0xd5, 0xb5, 0xe9, 0x98,
|
||||
0xb0, 0x48, 0x74, 0x06, 0x6b, 0x22, 0xa1, 0xda, 0x59, 0x22, 0x4f, 0x73, 0x81, 0x4b, 0x8c, 0x32,
|
||||
0x1d, 0x02, 0x93, 0xe4, 0xde, 0x85, 0x56, 0xc6, 0xe7, 0x2f, 0x68, 0xb4, 0xe8, 0x4c, 0x66, 0x7c,
|
||||
0xfe, 0x04, 0x0b, 0xf7, 0x2f, 0xe0, 0xcc, 0x02, 0x0c, 0xa7, 0x92, 0xa5, 0xd2, 0xd9, 0x96, 0x84,
|
||||
0x27, 0x58, 0xf8, 0x3f, 0x5b, 0xd0, 0x1c, 0xa1, 0xb8, 0x44, 0xf1, 0x46, 0x0b, 0xcc, 0x7c, 0x18,
|
||||
0xd4, 0x5f, 0xf3, 0x30, 0x68, 0x6c, 0x7f, 0x18, 0xd8, 0xab, 0x87, 0xc1, 0x1e, 0xd8, 0x23, 0x31,
|
||||
0x39, 0x1a, 0x4a, 0x8b, 0xea, 0x4c, 0x01, 0xaa, 0xc6, 0xc1, 0x24, 0x0b, 0x2e, 0x51, 0xbf, 0x16,
|
||||
0x34, 0xda, 0xd8, 0x6b, 0xed, 0x2d, 0x2b, 0xfa, 0x57, 0x3e, 0x1a, 0xfc, 0xcf, 0x2d, 0x68, 0x1e,
|
||||
0xf3, 0x22, 0xce, 0xb3, 0x8d, 0xaa, 0xed, 0x42, 0x67, 0x90, 0x24, 0x61, 0x30, 0x59, 0xeb, 0x54,
|
||||
0x83, 0x44, 0x12, 0x4f, 0x8d, 0x7c, 0xa8, 0x58, 0x98, 0x24, 0x1a, 0xea, 0x87, 0x72, 0xd7, 0xab,
|
||||
0xc5, 0x6d, 0x0c, 0x75, 0xb5, 0xe2, 0x25, 0x93, 0x82, 0x36, 0xc8, 0xb3, 0x78, 0x16, 0xc6, 0x57,
|
||||
0x32, 0x3a, 0x6d, 0x56, 0x61, 0xff, 0xfb, 0x1a, 0x34, 0x7e, 0xaf, 0xfd, 0xbc, 0x03, 0x56, 0xa0,
|
||||
0x8b, 0xc3, 0x0a, 0xaa, 0x6d, 0xdd, 0x32, 0xb6, 0xb5, 0x07, 0xad, 0x42, 0xf0, 0x68, 0x8e, 0xa9,
|
||||
0xd7, 0x96, 0xd3, 0xa8, 0x84, 0x92, 0x23, 0xfb, 0x4e, 0xad, 0x69, 0x87, 0x95, 0xb0, 0xea, 0x23,
|
||||
0x30, 0xfa, 0xe8, 0xbf, 0x7a, 0xa3, 0x77, 0xa4, 0x45, 0xde, 0x7a, 0x58, 0x6e, 0x2e, 0xf2, 0xdf,
|
||||
0x6e, 0x73, 0xfe, 0x64, 0x81, 0x5d, 0x35, 0xe1, 0xe1, 0x7a, 0x13, 0x1e, 0xae, 0x9a, 0x70, 0x78,
|
||||
0x50, 0x36, 0xe1, 0xf0, 0x80, 0x30, 0x3b, 0x2d, 0x9b, 0x90, 0x9d, 0x52, 0xb2, 0x1e, 0x8b, 0x38,
|
||||
0x4f, 0x0e, 0x0a, 0x95, 0x55, 0x87, 0x55, 0x98, 0x2a, 0xf7, 0xe3, 0x05, 0x0a, 0x1d, 0x6a, 0x87,
|
||||
0x69, 0x44, 0x75, 0x7e, 0x2c, 0x07, 0x94, 0x0a, 0xae, 0x02, 0xee, 0x3f, 0xc0, 0x66, 0x14, 0x3c,
|
||||
0x19, 0xe1, 0xb5, 0xbc, 0x48, 0x32, 0x53, 0x5c, 0x52, 0xaa, 0x5e, 0xf2, 0xba, 0xe0, 0xcb, 0x77,
|
||||
0xfd, 0x7f, 0xa0, 0x39, 0x5a, 0x04, 0xb3, 0xac, 0x7c, 0x17, 0xfd, 0xd1, 0x18, 0x70, 0xc1, 0x12,
|
||||
0x25, 0x8f, 0x69, 0x11, 0xff, 0x39, 0x38, 0x15, 0x71, 0x65, 0x8e, 0x65, 0x9a, 0xe3, 0x42, 0xe3,
|
||||
0x3c, 0x0a, 0xb2, 0xb2, 0xd5, 0xe9, 0x4c, 0xce, 0x3e, 0xcf, 0x79, 0x94, 0x05, 0x59, 0x51, 0xb6,
|
||||
0x7a, 0x89, 0xfd, 0x07, 0xda, 0x7c, 0x52, 0x77, 0x9e, 0x24, 0x28, 0xf4, 0xd8, 0x50, 0x40, 0x5e,
|
||||
0x12, 0x5f, 0xa1, 0x9a, 0xf8, 0x75, 0xa6, 0x80, 0xff, 0x09, 0x38, 0x83, 0x10, 0x45, 0xc6, 0xf2,
|
||||
0x10, 0xb7, 0x6d, 0xe2, 0x8f, 0x46, 0x27, 0xcf, 0x4a, 0x0b, 0xe8, 0xbc, 0x1a, 0x11, 0xf5, 0x1b,
|
||||
0x23, 0xe2, 0x09, 0x4f, 0xf8, 0xd1, 0x50, 0xd6, 0x79, 0x9d, 0x69, 0xe4, 0x7f, 0x69, 0x41, 0x83,
|
||||
0x66, 0x91, 0xa1, 0xba, 0xf1, 0xba, 0x39, 0x76, 0x2a, 0xe2, 0xcb, 0x60, 0x8a, 0xa2, 0x74, 0xae,
|
||||
0xc4, 0x32, 0xe8, 0x93, 0x05, 0x56, 0x0b, 0x5f, 0x23, 0xaa, 0x35, 0x7a, 0xf6, 0x97, 0xbd, 0x64,
|
||||
0xd4, 0x1a, 0x91, 0x99, 0x62, 0xd2, 0x83, 0x6c, 0x94, 0x27, 0x28, 0x06, 0xd3, 0x65, 0x10, 0xc9,
|
||||
0xa4, 0xb7, 0x99, 0x41, 0xf1, 0x1f, 0xa9, 0x1f, 0x89, 0x8d, 0x89, 0x66, 0x6d, 0xff, 0xe9, 0xb8,
|
||||
0x69, 0xb9, 0xff, 0x95, 0x05, 0xad, 0xa7, 0xfa, 0x95, 0x65, 0x7a, 0x61, 0xbd, 0xd2, 0x8b, 0xda,
|
||||
0x9a, 0x17, 0x7d, 0xd8, 0x2b, 0x65, 0xd6, 0xee, 0x57, 0x51, 0xd8, 0xca, 0xd3, 0x11, 0x6d, 0x54,
|
||||
0xc9, 0x7a, 0x93, 0xbf, 0x8c, 0xb3, 0x75, 0x99, 0x6d, 0x09, 0xdf, 0xc8, 0x4a, 0x17, 0x3a, 0xfa,
|
||||
0xef, 0x50, 0xfe, 0x6b, 0xe9, 0xa1, 0x6a, 0x90, 0xfc, 0x3e, 0x34, 0x0f, 0xe3, 0x68, 0x16, 0xcc,
|
||||
0xdd, 0x1e, 0x34, 0x06, 0x79, 0xb6, 0x90, 0x1a, 0x3b, 0xfd, 0x3d, 0xa3, 0xf1, 0xf3, 0x6c, 0xa1,
|
||||
0x64, 0x98, 0x94, 0xf0, 0xdf, 0x05, 0x58, 0xd1, 0x68, 0x4b, 0xac, 0xb2, 0xf1, 0x0c, 0xaf, 0xa8,
|
||||
0x64, 0x52, 0xa9, 0xa5, 0xcd, 0xb6, 0x70, 0xfc, 0xf7, 0xc0, 0x39, 0xc8, 0x83, 0x70, 0x7a, 0x14,
|
||||
0xcd, 0x62, 0x1a, 0x1d, 0x17, 0x28, 0xd2, 0x55, 0xbe, 0x4a, 0x48, 0xe1, 0xa6, 0x29, 0x52, 0xf5,
|
||||
0x90, 0x46, 0xe3, 0xa6, 0xfc, 0x3b, 0x7f, 0xf0, 0x4b, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe8, 0x64,
|
||||
0x6b, 0x1b, 0xaf, 0x0f, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -37,6 +37,20 @@ message DashboardCell {
|
|||
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'
|
||||
}
|
||||
|
||||
message TableOptions {
|
||||
string timeFormat = 1; // format for time
|
||||
bool verticalTimeAxis = 2; // time axis should be a column not row
|
||||
TableColumn sortBy = 3; // which column should a table be sorted by
|
||||
string wrapping = 4; // option for text wrapping
|
||||
repeated TableColumn columnNames = 5; // names and renames for columns
|
||||
}
|
||||
|
||||
message TableColumn {
|
||||
string internalName = 1; // name of column
|
||||
string displayName = 2; // what column is renamed to
|
||||
}
|
||||
|
||||
message Color {
|
||||
|
@ -95,6 +109,7 @@ message Server {
|
|||
int64 SrcID = 6; // SrcID is the ID of the data source
|
||||
bool Active = 7; // is this the currently active server for the source
|
||||
string Organization = 8; // Organization is the organization ID that resource belongs to
|
||||
bool InsecureSkipVerify = 9; // InsecureSkipVerify accepts any certificate from the client
|
||||
}
|
||||
|
||||
message Layout {
|
||||
|
|
|
@ -76,12 +76,13 @@ func TestMarshalSourceWithSecret(t *testing.T) {
|
|||
|
||||
func TestMarshalServer(t *testing.T) {
|
||||
v := chronograf.Server{
|
||||
ID: 12,
|
||||
SrcID: 2,
|
||||
Name: "Fountain of Truth",
|
||||
Username: "docbrown",
|
||||
Password: "1 point twenty-one g1g@w@tts",
|
||||
URL: "http://oldmanpeabody.mall.io:9092",
|
||||
ID: 12,
|
||||
SrcID: 2,
|
||||
Name: "Fountain of Truth",
|
||||
Username: "docbrown",
|
||||
Password: "1 point twenty-one g1g@w@tts",
|
||||
URL: "http://oldmanpeabody.mall.io:9092",
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
var vv chronograf.Server
|
||||
|
@ -193,6 +194,10 @@ func Test_MarshalDashboard(t *testing.T) {
|
|||
Value: "100",
|
||||
},
|
||||
},
|
||||
TableOptions: chronograf.TableOptions{
|
||||
TimeFormat: "",
|
||||
ColumnNames: []chronograf.TableColumn{},
|
||||
},
|
||||
},
|
||||
},
|
||||
Templates: []chronograf.Template{},
|
||||
|
@ -255,6 +260,9 @@ func Test_MarshalDashboard_WithLegacyBounds(t *testing.T) {
|
|||
Type: "static",
|
||||
Orientation: "bottom",
|
||||
},
|
||||
TableOptions: chronograf.TableOptions{
|
||||
TimeFormat: "MM:DD:YYYY",
|
||||
},
|
||||
Type: "line",
|
||||
},
|
||||
},
|
||||
|
@ -309,6 +317,10 @@ func Test_MarshalDashboard_WithLegacyBounds(t *testing.T) {
|
|||
Type: "static",
|
||||
Orientation: "bottom",
|
||||
},
|
||||
TableOptions: chronograf.TableOptions{
|
||||
TimeFormat: "MM:DD:YYYY",
|
||||
ColumnNames: []chronograf.TableColumn{},
|
||||
},
|
||||
Type: "line",
|
||||
},
|
||||
},
|
||||
|
@ -369,6 +381,9 @@ func Test_MarshalDashboard_WithEmptyLegacyBounds(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Type: "line",
|
||||
TableOptions: chronograf.TableOptions{
|
||||
TimeFormat: "MM:DD:YYYY",
|
||||
},
|
||||
},
|
||||
},
|
||||
Templates: []chronograf.Template{},
|
||||
|
@ -418,6 +433,10 @@ func Test_MarshalDashboard_WithEmptyLegacyBounds(t *testing.T) {
|
|||
Value: "100",
|
||||
},
|
||||
},
|
||||
TableOptions: chronograf.TableOptions{
|
||||
TimeFormat: "MM:DD:YYYY",
|
||||
ColumnNames: []chronograf.TableColumn{},
|
||||
},
|
||||
Type: "line",
|
||||
},
|
||||
},
|
||||
|
@ -454,6 +473,9 @@ func Test_MarshalDashboard_WithEmptyCellType(t *testing.T) {
|
|||
Queries: []chronograf.DashboardQuery{},
|
||||
Axes: map[string]chronograf.Axis{},
|
||||
CellColors: []chronograf.CellColor{},
|
||||
TableOptions: chronograf.TableOptions{
|
||||
ColumnNames: []chronograf.TableColumn{},
|
||||
},
|
||||
},
|
||||
},
|
||||
Templates: []chronograf.Template{},
|
||||
|
|
|
@ -20,22 +20,24 @@ func TestServerStore(t *testing.T) {
|
|||
|
||||
srcs := []chronograf.Server{
|
||||
chronograf.Server{
|
||||
Name: "Of Truth",
|
||||
SrcID: 10,
|
||||
Username: "marty",
|
||||
Password: "I❤️ jennifer parker",
|
||||
URL: "toyota-hilux.lyon-estates.local",
|
||||
Active: false,
|
||||
Organization: "133",
|
||||
Name: "Of Truth",
|
||||
SrcID: 10,
|
||||
Username: "marty",
|
||||
Password: "I❤️ jennifer parker",
|
||||
URL: "toyota-hilux.lyon-estates.local",
|
||||
Active: false,
|
||||
Organization: "133",
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
chronograf.Server{
|
||||
Name: "HipToBeSquare",
|
||||
SrcID: 12,
|
||||
Username: "calvinklein",
|
||||
Password: "chuck b3rry",
|
||||
URL: "toyota-hilux.lyon-estates.local",
|
||||
Active: false,
|
||||
Organization: "133",
|
||||
Name: "HipToBeSquare",
|
||||
SrcID: 12,
|
||||
Username: "calvinklein",
|
||||
Password: "chuck b3rry",
|
||||
URL: "toyota-hilux.lyon-estates.local",
|
||||
Active: false,
|
||||
Organization: "133",
|
||||
InsecureSkipVerify: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -150,26 +150,26 @@ func (s *UsersStore) Add(ctx context.Context, u *chronograf.User) (*chronograf.U
|
|||
}
|
||||
|
||||
// Delete a user from the UsersStore
|
||||
func (s *UsersStore) Delete(ctx context.Context, usr *chronograf.User) error {
|
||||
_, err := s.get(ctx, usr.ID)
|
||||
func (s *UsersStore) Delete(ctx context.Context, u *chronograf.User) error {
|
||||
_, err := s.get(ctx, u.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.client.db.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(UsersBucket).Delete(u64tob(usr.ID))
|
||||
return tx.Bucket(UsersBucket).Delete(u64tob(u.ID))
|
||||
})
|
||||
}
|
||||
|
||||
// Update a user
|
||||
func (s *UsersStore) Update(ctx context.Context, usr *chronograf.User) error {
|
||||
_, err := s.get(ctx, usr.ID)
|
||||
func (s *UsersStore) Update(ctx context.Context, u *chronograf.User) error {
|
||||
_, err := s.get(ctx, u.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.client.db.Update(func(tx *bolt.Tx) error {
|
||||
if v, err := internal.MarshalUser(usr); err != nil {
|
||||
if v, err := internal.MarshalUser(u); err != nil {
|
||||
return err
|
||||
} else if err := tx.Bucket(UsersBucket).Put(u64tob(usr.ID), v); err != nil {
|
||||
} else if err := tx.Bucket(UsersBucket).Put(u64tob(u.ID), v); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"colors": [],
|
||||
"type": "single-stat"
|
||||
},
|
||||
{
|
||||
|
@ -73,6 +74,7 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"colors": [],
|
||||
"type": "single-stat"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -117,6 +117,7 @@
|
|||
"h": 4,
|
||||
"i": "0fa47984-825b-46f1-9ca5-0366e3220008",
|
||||
"name": "Mesos Master Uptime",
|
||||
"colors": [],
|
||||
"type": "single-stat",
|
||||
"queries": [
|
||||
{
|
||||
|
|
|
@ -35,6 +35,9 @@ const (
|
|||
ErrCannotDeleteDefaultOrganization = Error("cannot delete default organization")
|
||||
ErrConfigNotFound = Error("cannot find configuration")
|
||||
ErrAnnotationNotFound = Error("annotation not found")
|
||||
ErrInvalidCellOptionsText = Error("invalid text wrapping option. Valid wrappings are 'truncate', 'wrap', and 'single line'")
|
||||
ErrInvalidCellOptionsSort = Error("cell options sortby cannot be empty'")
|
||||
ErrInvalidCellOptionsColumns = Error("cell options columns cannot be empty'")
|
||||
)
|
||||
|
||||
// Error is a domain error encountered while processing chronograf requests
|
||||
|
@ -543,17 +546,33 @@ 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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// TableColumn is a column in a DashboardCell of type Table
|
||||
type TableColumn struct {
|
||||
InternalName string `json:"internalName"`
|
||||
DisplayName string `json:"displayName"`
|
||||
}
|
||||
|
||||
// TableOptions is a type of options for a DashboardCell with type Table
|
||||
type TableOptions struct {
|
||||
TimeFormat string `json:"timeFormat"`
|
||||
VerticalTimeAxis bool `json:"verticalTimeAxis"`
|
||||
SortBy TableColumn `json:"sortBy"`
|
||||
Wrapping string `json:"wrapping"`
|
||||
ColumnNames []TableColumn `json:"columnNames"`
|
||||
}
|
||||
|
||||
// DashboardsStore is the storage and retrieval of dashboards
|
||||
|
@ -572,15 +591,16 @@ type DashboardsStore interface {
|
|||
|
||||
// Cell is a rectangle and multiple time series queries to visualize.
|
||||
type Cell struct {
|
||||
X int32 `json:"x"`
|
||||
Y int32 `json:"y"`
|
||||
W int32 `json:"w"`
|
||||
H int32 `json:"h"`
|
||||
I string `json:"i"`
|
||||
Name string `json:"name"`
|
||||
Queries []Query `json:"queries"`
|
||||
Axes map[string]Axis `json:"axes"`
|
||||
Type string `json:"type"`
|
||||
X int32 `json:"x"`
|
||||
Y int32 `json:"y"`
|
||||
W int32 `json:"w"`
|
||||
H int32 `json:"h"`
|
||||
I string `json:"i"`
|
||||
Name string `json:"name"`
|
||||
Queries []Query `json:"queries"`
|
||||
Axes map[string]Axis `json:"axes"`
|
||||
Type string `json:"type"`
|
||||
CellColors []CellColor `json:"colors"`
|
||||
}
|
||||
|
||||
// Layout is a collection of Cells for visualization
|
||||
|
|
|
@ -164,7 +164,8 @@ func TestServer(t *testing.T) {
|
|||
"id": "5000",
|
||||
"name": "Kapa 1",
|
||||
"url": "http://localhost:9092",
|
||||
"active": true,
|
||||
"active": true,
|
||||
"insecureSkipVerify": false,
|
||||
"links": {
|
||||
"proxy": "/chronograf/v1/sources/5000/kapacitors/5000/proxy",
|
||||
"self": "/chronograf/v1/sources/5000/kapacitors/5000",
|
||||
|
@ -222,7 +223,8 @@ func TestServer(t *testing.T) {
|
|||
"id": "5000",
|
||||
"name": "Kapa 1",
|
||||
"url": "http://localhost:9092",
|
||||
"active": true,
|
||||
"active": true,
|
||||
"insecureSkipVerify": false,
|
||||
"links": {
|
||||
"proxy": "/chronograf/v1/sources/5000/kapacitors/5000/proxy",
|
||||
"self": "/chronograf/v1/sources/5000/kapacitors/5000",
|
||||
|
@ -540,7 +542,16 @@ func TestServer(t *testing.T) {
|
|||
"legend":{
|
||||
"type": "static",
|
||||
"orientation": "bottom"
|
||||
},
|
||||
},
|
||||
"tableOptions":{
|
||||
"timeFormat": "",
|
||||
"verticalTimeAxis": false,
|
||||
"sortBy":{
|
||||
"internalName": "",
|
||||
"displayName": ""},
|
||||
"wrapping": "",
|
||||
"columnNames": null
|
||||
},
|
||||
"links": {
|
||||
"self": "/chronograf/v1/dashboards/1000/cells/8f61c619-dd9b-4761-8aa8-577f27247093"
|
||||
}
|
||||
|
@ -779,7 +790,17 @@ func TestServer(t *testing.T) {
|
|||
"name": "comet",
|
||||
"value": "100"
|
||||
}
|
||||
],
|
||||
],
|
||||
"tableOptions":{
|
||||
"timeFormat":"",
|
||||
"verticalTimeAxis":false,
|
||||
"sortBy":{
|
||||
"internalName":"",
|
||||
"displayName":""
|
||||
},
|
||||
"wrapping":"",
|
||||
"columnNames":null
|
||||
},
|
||||
"legend":{
|
||||
"type": "static",
|
||||
"orientation": "bottom"
|
||||
|
|
|
@ -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":{},"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":{"timeFormat":"","verticalTimeAxis":false,"sortBy":{"internalName":"","displayName":""},"wrapping":"","columnNames":null},"links":{"self":"/chronograf/v1/dashboards/1/cells/3c5c4102-fa40-4585-a8f9-917c77e37192"}}
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -16,7 +16,7 @@ type postKapacitorRequest struct {
|
|||
URL *string `json:"url"` // URL for the kapacitor backend (e.g. http://localhost:9092);/ Required: true
|
||||
Username string `json:"username,omitempty"` // Username for authentication to kapacitor
|
||||
Password string `json:"password,omitempty"`
|
||||
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"` // InsecureSkipVerify as true means any certificate presented by the kapacitor is accepted.
|
||||
InsecureSkipVerify bool `json:"insecureSkipVerify"` // InsecureSkipVerify as true means any certificate presented by the kapacitor is accepted.
|
||||
Active bool `json:"active"`
|
||||
Organization string `json:"organization"` // Organization is the organization ID that resource belongs to
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ type kapacitor struct {
|
|||
URL string `json:"url"` // URL for the kapacitor backend (e.g. http://localhost:9092)
|
||||
Username string `json:"username,omitempty"` // Username for authentication to kapacitor
|
||||
Password string `json:"password,omitempty"`
|
||||
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"` // InsecureSkipVerify as true means any certificate presented by the kapacitor is accepted.
|
||||
InsecureSkipVerify bool `json:"insecureSkipVerify"` // InsecureSkipVerify as true means any certificate presented by the kapacitor is accepted.
|
||||
Active bool `json:"active"`
|
||||
Links kapaLinks `json:"links"` // Links are URI locations related to kapacitor
|
||||
}
|
||||
|
@ -225,7 +225,7 @@ type patchKapacitorRequest struct {
|
|||
URL *string `json:"url,omitempty"` // URL for the kapacitor
|
||||
Username *string `json:"username,omitempty"` // Username for kapacitor auth
|
||||
Password *string `json:"password,omitempty"`
|
||||
InsecureSkipVerify *bool `json:"insecureSkipVerify,omitempty"` // InsecureSkipVerify as true means any certificate presented by the kapacitor is accepted.
|
||||
InsecureSkipVerify *bool `json:"insecureSkipVerify"` // InsecureSkipVerify as true means any certificate presented by the kapacitor is accepted.
|
||||
Active *bool `json:"active"`
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,10 @@ func newLayoutResponse(layout chronograf.Layout) layoutResponse {
|
|||
layout.Cells[idx].Axes = make(map[string]chronograf.Axis, len(axes))
|
||||
}
|
||||
|
||||
if cell.CellColors == nil {
|
||||
layout.Cells[idx].CellColors = []chronograf.CellColor{}
|
||||
}
|
||||
|
||||
for _, axis := range axes {
|
||||
if _, found := cell.Axes[axis]; !found {
|
||||
layout.Cells[idx].Axes[axis] = chronograf.Axis{
|
||||
|
|
|
@ -76,12 +76,13 @@ func Test_Layouts(t *testing.T) {
|
|||
Measurement: "influxdb",
|
||||
Cells: []chronograf.Cell{
|
||||
{
|
||||
X: 0,
|
||||
Y: 0,
|
||||
W: 4,
|
||||
H: 4,
|
||||
I: "3b0e646b-2ca3-4df2-95a5-fd80915459dd",
|
||||
Name: "A Graph",
|
||||
X: 0,
|
||||
Y: 0,
|
||||
W: 4,
|
||||
H: 4,
|
||||
I: "3b0e646b-2ca3-4df2-95a5-fd80915459dd",
|
||||
Name: "A Graph",
|
||||
CellColors: []chronograf.CellColor{},
|
||||
Axes: map[string]chronograf.Axis{
|
||||
"x": chronograf.Axis{
|
||||
Bounds: []string{},
|
||||
|
@ -103,12 +104,13 @@ func Test_Layouts(t *testing.T) {
|
|||
Measurement: "influxdb",
|
||||
Cells: []chronograf.Cell{
|
||||
{
|
||||
X: 0,
|
||||
Y: 0,
|
||||
W: 4,
|
||||
H: 4,
|
||||
I: "3b0e646b-2ca3-4df2-95a5-fd80915459dd",
|
||||
Name: "A Graph",
|
||||
X: 0,
|
||||
Y: 0,
|
||||
W: 4,
|
||||
H: 4,
|
||||
I: "3b0e646b-2ca3-4df2-95a5-fd80915459dd",
|
||||
CellColors: []chronograf.CellColor{},
|
||||
Name: "A Graph",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,7 +1,51 @@
|
|||
export const source = {
|
||||
id: '2',
|
||||
name: 'minikube-influx',
|
||||
type: 'influx',
|
||||
url: 'http://192.168.99.100:30400',
|
||||
default: true,
|
||||
telegraf: 'telegraf',
|
||||
organization: 'default',
|
||||
role: 'viewer',
|
||||
links: {
|
||||
self: '/chronograf/v1/sources/2',
|
||||
kapacitors: '/chronograf/v1/sources/2/kapacitors',
|
||||
proxy: '/chronograf/v1/sources/2/proxy',
|
||||
queries: '/chronograf/v1/sources/2/queries',
|
||||
write: '/chronograf/v1/sources/2/write',
|
||||
permissions: '/chronograf/v1/sources/2/permissions',
|
||||
users: '/chronograf/v1/sources/2/users',
|
||||
databases: '/chronograf/v1/sources/2/dbs',
|
||||
annotations: '/chronograf/v1/sources/2/annotations',
|
||||
},
|
||||
}
|
||||
|
||||
export const kapacitor = {
|
||||
id: '1',
|
||||
name: 'Test Kapacitor',
|
||||
url: 'http://localhost:9092',
|
||||
insecureSkipVerify: false,
|
||||
active: true,
|
||||
links: {
|
||||
self: '/chronograf/v1/sources/47/kapacitors/1',
|
||||
proxy: '/chronograf/v1/sources/47/kapacitors/1/proxy',
|
||||
},
|
||||
}
|
||||
|
||||
export const createKapacitorBody = {
|
||||
name: 'Test Kapacitor',
|
||||
url: 'http://localhost:9092',
|
||||
insecureSkipVerify: false,
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
}
|
||||
|
||||
export const updateKapacitorBody = {
|
||||
name: 'Test Kapacitor',
|
||||
url: 'http://localhost:9092',
|
||||
insecureSkipVerify: false,
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
active: true,
|
||||
links: {
|
||||
self: '/chronograf/v1/sources/47/kapacitors/1',
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export default jest.fn(() => Promise.resolve())
|
|
@ -136,7 +136,6 @@
|
|||
"redux-auth-wrapper": "^1.0.0",
|
||||
"redux-thunk": "^1.0.3",
|
||||
"rome": "^2.1.22",
|
||||
"updeep": "^0.13.0",
|
||||
"uuid": "^3.2.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,43 +1,20 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import SideNav from 'src/side_nav'
|
||||
import Notifications from 'shared/components/Notifications'
|
||||
|
||||
import {publishNotification} from 'shared/actions/notifications'
|
||||
const App = ({children}) =>
|
||||
<div className="chronograf-root">
|
||||
<Notifications />
|
||||
<SideNav />
|
||||
{children}
|
||||
</div>
|
||||
|
||||
const {func, node} = PropTypes
|
||||
const {node} = PropTypes
|
||||
|
||||
const App = React.createClass({
|
||||
propTypes: {
|
||||
children: node.isRequired,
|
||||
notify: func.isRequired,
|
||||
},
|
||||
App.propTypes = {
|
||||
children: node.isRequired,
|
||||
}
|
||||
|
||||
handleAddFlashMessage({type, text}) {
|
||||
const {notify} = this.props
|
||||
|
||||
notify(type, text)
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="chronograf-root">
|
||||
<Notifications />
|
||||
<SideNav />
|
||||
{this.props.children &&
|
||||
React.cloneElement(this.props.children, {
|
||||
addFlashMessage: this.handleAddFlashMessage,
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
notify: bindActionCreators(publishNotification, dispatch),
|
||||
})
|
||||
|
||||
export default connect(null, mapDispatchToProps)(App)
|
||||
export default App
|
||||
|
|
|
@ -15,9 +15,17 @@ import {showDatabases} from 'shared/apis/metaQuery'
|
|||
|
||||
import {getSourcesAsync} from 'shared/actions/sources'
|
||||
import {errorThrown as errorThrownAction} from 'shared/actions/errors'
|
||||
import {publishNotification} from 'shared/actions/notifications'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import {DEFAULT_HOME_PAGE} from 'shared/constants'
|
||||
import {
|
||||
NOTIFY_SOURCE_NO_LONGER_AVAILABLE,
|
||||
NOTIFY_NO_SOURCES_AVAILABLE,
|
||||
NOTIFY_UNABLE_TO_RETRIEVE_SOURCES,
|
||||
NOTIFY_USER_REMOVED_FROM_ALL_ORGS,
|
||||
NOTIFY_USER_REMOVED_FROM_CURRENT_ORG,
|
||||
NOTIFY_ORG_HAS_NO_SOURCES,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
// Acts as a 'router middleware'. The main `App` component is responsible for
|
||||
// getting the list of data nodes, but not every page requires them to function.
|
||||
|
@ -85,10 +93,7 @@ class CheckSources extends Component {
|
|||
}
|
||||
|
||||
if (!isFetching && isUsingAuth && !organizations.length) {
|
||||
notify(
|
||||
'error',
|
||||
'You have been removed from all organizations. Please contact your administrator.'
|
||||
)
|
||||
notify(NOTIFY_USER_REMOVED_FROM_ALL_ORGS)
|
||||
return router.push('/purgatory')
|
||||
}
|
||||
|
||||
|
@ -96,7 +101,7 @@ class CheckSources extends Component {
|
|||
me.superAdmin &&
|
||||
!organizations.find(o => o.id === currentOrganization.id)
|
||||
) {
|
||||
notify('error', 'You were removed from your current organization')
|
||||
notify(NOTIFY_USER_REMOVED_FROM_CURRENT_ORG)
|
||||
return router.push('/purgatory')
|
||||
}
|
||||
|
||||
|
@ -118,7 +123,7 @@ class CheckSources extends Component {
|
|||
return router.push(`/sources/${sources[0].id}/${restString}`)
|
||||
}
|
||||
// if you're a viewer and there are no sources, go to purgatory.
|
||||
notify('error', 'Organization has no sources configured')
|
||||
notify(NOTIFY_ORG_HAS_NO_SOURCES)
|
||||
return router.push('/purgatory')
|
||||
}
|
||||
|
||||
|
@ -143,18 +148,12 @@ class CheckSources extends Component {
|
|||
try {
|
||||
const newSources = await getSources()
|
||||
if (newSources.length) {
|
||||
errorThrown(
|
||||
error,
|
||||
`Source ${source.name} is no longer available. Successfully connected to another source.`
|
||||
)
|
||||
errorThrown(error, NOTIFY_SOURCE_NO_LONGER_AVAILABLE(source.name))
|
||||
} else {
|
||||
errorThrown(
|
||||
error,
|
||||
`Unable to connect to source ${source.name}. No other sources available.`
|
||||
)
|
||||
errorThrown(error, NOTIFY_NO_SOURCES_AVAILABLE(source.name))
|
||||
}
|
||||
} catch (error2) {
|
||||
errorThrown(error2, 'Unable to retrieve sources')
|
||||
errorThrown(error2, NOTIFY_UNABLE_TO_RETRIEVE_SOURCES)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -248,7 +247,7 @@ const mapStateToProps = ({sources, auth}) => ({
|
|||
const mapDispatchToProps = dispatch => ({
|
||||
getSources: bindActionCreators(getSourcesAsync, dispatch),
|
||||
errorThrown: bindActionCreators(errorThrownAction, dispatch),
|
||||
notify: bindActionCreators(publishNotification, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(
|
||||
|
|
|
@ -16,8 +16,14 @@ import {
|
|||
deleteMapping as deleteMappingAJAX,
|
||||
} from 'src/admin/apis/chronograf'
|
||||
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import {notify} from 'shared/actions/notifications'
|
||||
import {errorThrown} from 'shared/actions/errors'
|
||||
import {
|
||||
NOTIFY_MAPPING_DELETED,
|
||||
NOTIFY_CHRONOGRAF_ORG_DELETED,
|
||||
NOTIFY_CHRONOGRAF_USER_UPDATED,
|
||||
NOTIFY_CHRONOGRAF_USER_DELETED,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
import {REVERT_STATE_DELAY} from 'shared/constants'
|
||||
|
||||
|
@ -177,12 +183,7 @@ export const deleteMappingAsync = mapping => async dispatch => {
|
|||
dispatch(removeMapping(mapping))
|
||||
try {
|
||||
await deleteMappingAJAX(mapping)
|
||||
dispatch(
|
||||
publishAutoDismissingNotification(
|
||||
'success',
|
||||
`Mapping deleted: ${mapping.id} ${mapping.scheme}`
|
||||
)
|
||||
)
|
||||
dispatch(notify(NOTIFY_MAPPING_DELETED(mapping.id, mapping.scheme)))
|
||||
} catch (error) {
|
||||
dispatch(errorThrown(error))
|
||||
dispatch(addMapping(mapping))
|
||||
|
@ -238,7 +239,7 @@ export const updateUserAsync = (
|
|||
provider: null,
|
||||
scheme: null,
|
||||
})
|
||||
dispatch(publishAutoDismissingNotification('success', successMessage))
|
||||
dispatch(notify(NOTIFY_CHRONOGRAF_USER_UPDATED(successMessage)))
|
||||
// it's not necessary to syncUser again but it's useful for good
|
||||
// measure and for the clarity of insight in the redux story
|
||||
dispatch(syncUser(user, data))
|
||||
|
@ -256,12 +257,7 @@ export const deleteUserAsync = (
|
|||
try {
|
||||
await deleteUserAJAX(user)
|
||||
dispatch(
|
||||
publishAutoDismissingNotification(
|
||||
'success',
|
||||
`${user.name} has been removed from ${isAbsoluteDelete
|
||||
? 'all organizations and deleted'
|
||||
: 'the current organization'}`
|
||||
)
|
||||
notify(NOTIFY_CHRONOGRAF_USER_DELETED(user.name, isAbsoluteDelete))
|
||||
)
|
||||
} catch (error) {
|
||||
dispatch(errorThrown(error))
|
||||
|
@ -313,12 +309,7 @@ export const deleteOrganizationAsync = organization => async dispatch => {
|
|||
dispatch(removeOrganization(organization))
|
||||
try {
|
||||
await deleteOrganizationAJAX(organization)
|
||||
dispatch(
|
||||
publishAutoDismissingNotification(
|
||||
'success',
|
||||
`Organization deleted: ${organization.name}`
|
||||
)
|
||||
)
|
||||
dispatch(notify(NOTIFY_CHRONOGRAF_ORG_DELETED(organization.name)))
|
||||
} catch (error) {
|
||||
dispatch(errorThrown(error))
|
||||
dispatch(addOrganization(organization))
|
||||
|
|
|
@ -18,9 +18,40 @@ import {
|
|||
|
||||
import {killQuery as killQueryProxy} from 'shared/apis/metaQuery'
|
||||
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import {notify} from 'shared/actions/notifications'
|
||||
import {errorThrown} from 'shared/actions/errors'
|
||||
|
||||
import {
|
||||
NOTIFY_DB_USER_CREATED,
|
||||
NOTIFY_DB_USER_CREATION_FAILED,
|
||||
NOTIFY_DB_USER_DELETED,
|
||||
NOTIFY_DB_USER_DELETION_FAILED,
|
||||
NOTIFY_DB_USER_PERMISSIONS_UPDATED,
|
||||
NOTIFY_DB_USER_PERMISSIONS_UPDATE_FAILED,
|
||||
NOTIFY_DB_USER_ROLES_UPDATED,
|
||||
NOTIFY_DB_USER_ROLES_UPDATE_FAILED,
|
||||
NOTIFY_DB_USER_PASSWORD_UPDATED,
|
||||
NOTIFY_DB_USER_PASSWORD_UPDATE_FAILED,
|
||||
NOTIFY_DATABASE_CREATED,
|
||||
NOTIFY_DATABASE_CREATION_FAILED,
|
||||
NOTIFY_DATABASE_DELETED,
|
||||
NOTIFY_DATABASE_DELETION_FAILED,
|
||||
NOTIFY_ROLE_CREATED,
|
||||
NOTIFY_ROLE_CREATION_FAILED,
|
||||
NOTIFY_ROLE_DELETED,
|
||||
NOTIFY_ROLE_DELETION_FAILED,
|
||||
NOTIFY_ROLE_USERS_UPDATED,
|
||||
NOTIFY_ROLE_USERS_UPDATE_FAILED,
|
||||
NOTIFY_ROLE_PERMISSIONS_UPDATED,
|
||||
NOTIFY_ROLE_PERMISSIONS_UPDATE_FAILED,
|
||||
NOTIFY_RETENTION_POLICY_CREATED,
|
||||
NOTIFY_RETENTION_POLICY_CREATION_FAILED,
|
||||
NOTIFY_RETENTION_POLICY_DELETED,
|
||||
NOTIFY_RETENTION_POLICY_DELETION_FAILED,
|
||||
NOTIFY_RETENTION_POLICY_UPDATED,
|
||||
NOTIFY_RETENTION_POLICY_UPDATE_FAILED,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
import {REVERT_STATE_DELAY} from 'shared/constants'
|
||||
import _ from 'lodash'
|
||||
|
||||
|
@ -276,12 +307,12 @@ export const loadDBsAndRPsAsync = url => async dispatch => {
|
|||
export const createUserAsync = (url, user) => async dispatch => {
|
||||
try {
|
||||
const {data} = await createUserAJAX(url, user)
|
||||
dispatch(
|
||||
publishAutoDismissingNotification('success', 'User created successfully')
|
||||
)
|
||||
dispatch(notify(NOTIFY_DB_USER_CREATED))
|
||||
dispatch(syncUser(user, data))
|
||||
} catch (error) {
|
||||
dispatch(errorThrown(error, `Failed to create user: ${error.data.message}`))
|
||||
dispatch(
|
||||
errorThrown(error, NOTIFY_DB_USER_CREATION_FAILED(error.data.message))
|
||||
)
|
||||
// undo optimistic update
|
||||
setTimeout(() => dispatch(deleteUser(user)), REVERT_STATE_DELAY)
|
||||
}
|
||||
|
@ -290,12 +321,12 @@ export const createUserAsync = (url, user) => async dispatch => {
|
|||
export const createRoleAsync = (url, role) => async dispatch => {
|
||||
try {
|
||||
const {data} = await createRoleAJAX(url, role)
|
||||
dispatch(
|
||||
publishAutoDismissingNotification('success', 'Role created successfully')
|
||||
)
|
||||
dispatch(notify(NOTIFY_ROLE_CREATED))
|
||||
dispatch(syncRole(role, data))
|
||||
} catch (error) {
|
||||
dispatch(errorThrown(error, `Failed to create role: ${error.data.message}`))
|
||||
dispatch(
|
||||
errorThrown(error, NOTIFY_ROLE_CREATION_FAILED(error.data.message))
|
||||
)
|
||||
// undo optimistic update
|
||||
setTimeout(() => dispatch(deleteRole(role)), REVERT_STATE_DELAY)
|
||||
}
|
||||
|
@ -305,15 +336,10 @@ export const createDatabaseAsync = (url, database) => async dispatch => {
|
|||
try {
|
||||
const {data} = await createDatabaseAJAX(url, database)
|
||||
dispatch(syncDatabase(database, data))
|
||||
dispatch(
|
||||
publishAutoDismissingNotification(
|
||||
'success',
|
||||
'Database created successfully'
|
||||
)
|
||||
)
|
||||
dispatch(notify(NOTIFY_DATABASE_CREATED))
|
||||
} catch (error) {
|
||||
dispatch(
|
||||
errorThrown(error, `Failed to create database: ${error.data.message}`)
|
||||
errorThrown(error, NOTIFY_DATABASE_CREATION_FAILED(error.data.message))
|
||||
)
|
||||
// undo optimistic update
|
||||
setTimeout(() => dispatch(removeDatabase(database)), REVERT_STATE_DELAY)
|
||||
|
@ -329,19 +355,11 @@ export const createRetentionPolicyAsync = (
|
|||
database.links.retentionPolicies,
|
||||
retentionPolicy
|
||||
)
|
||||
dispatch(
|
||||
publishAutoDismissingNotification(
|
||||
'success',
|
||||
'Retention policy created successfully'
|
||||
)
|
||||
)
|
||||
dispatch(notify(NOTIFY_RETENTION_POLICY_CREATED))
|
||||
dispatch(syncRetentionPolicy(database, retentionPolicy, data))
|
||||
} catch (error) {
|
||||
dispatch(
|
||||
errorThrown(
|
||||
error,
|
||||
`Failed to create retention policy: ${error.data.message}`
|
||||
)
|
||||
errorThrown(NOTIFY_RETENTION_POLICY_CREATION_FAILED(error.data.message))
|
||||
)
|
||||
// undo optimistic update
|
||||
setTimeout(
|
||||
|
@ -360,18 +378,13 @@ export const updateRetentionPolicyAsync = (
|
|||
dispatch(editRetentionPolicyRequested(database, oldRP, newRP))
|
||||
const {data} = await updateRetentionPolicyAJAX(oldRP.links.self, newRP)
|
||||
dispatch(editRetentionPolicyCompleted(database, oldRP, data))
|
||||
dispatch(
|
||||
publishAutoDismissingNotification(
|
||||
'success',
|
||||
'Retention policy updated successfully'
|
||||
)
|
||||
)
|
||||
dispatch(notify(NOTIFY_RETENTION_POLICY_UPDATED))
|
||||
} catch (error) {
|
||||
dispatch(editRetentionPolicyFailed(database, oldRP))
|
||||
dispatch(
|
||||
errorThrown(
|
||||
error,
|
||||
`Failed to update retention policy: ${error.data.message}`
|
||||
NOTIFY_RETENTION_POLICY_UPDATE_FAILED(error.data.message)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -394,9 +407,11 @@ export const deleteRoleAsync = role => async dispatch => {
|
|||
dispatch(deleteRole(role))
|
||||
try {
|
||||
await deleteRoleAJAX(role.links.self)
|
||||
dispatch(publishAutoDismissingNotification('success', 'Role deleted'))
|
||||
dispatch(notify(NOTIFY_ROLE_DELETED(role.name)))
|
||||
} catch (error) {
|
||||
dispatch(errorThrown(error, `Failed to delete role: ${error.data.message}`))
|
||||
dispatch(
|
||||
errorThrown(error, NOTIFY_ROLE_DELETION_FAILED(error.data.message))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -404,9 +419,11 @@ export const deleteUserAsync = user => async dispatch => {
|
|||
dispatch(deleteUser(user))
|
||||
try {
|
||||
await deleteUserAJAX(user.links.self)
|
||||
dispatch(publishAutoDismissingNotification('success', 'User deleted'))
|
||||
dispatch(notify(NOTIFY_DB_USER_DELETED(user.name)))
|
||||
} catch (error) {
|
||||
dispatch(errorThrown(error, `Failed to delete user: ${error.data.message}`))
|
||||
dispatch(
|
||||
errorThrown(error, NOTIFY_DB_USER_DELETION_FAILED(error.data.message))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -414,10 +431,10 @@ export const deleteDatabaseAsync = database => async dispatch => {
|
|||
dispatch(removeDatabase(database))
|
||||
try {
|
||||
await deleteDatabaseAJAX(database.links.self)
|
||||
dispatch(publishAutoDismissingNotification('success', 'Database deleted'))
|
||||
dispatch(notify(NOTIFY_DATABASE_DELETED(database.name)))
|
||||
} catch (error) {
|
||||
dispatch(
|
||||
errorThrown(error, `Failed to delete database: ${error.data.message}`)
|
||||
errorThrown(error, NOTIFY_DATABASE_DELETION_FAILED(error.data.message))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -429,17 +446,12 @@ export const deleteRetentionPolicyAsync = (
|
|||
dispatch(removeRetentionPolicy(database, retentionPolicy))
|
||||
try {
|
||||
await deleteRetentionPolicyAJAX(retentionPolicy.links.self)
|
||||
dispatch(
|
||||
publishAutoDismissingNotification(
|
||||
'success',
|
||||
`Retention policy ${retentionPolicy.name} deleted`
|
||||
)
|
||||
)
|
||||
dispatch(notify(NOTIFY_RETENTION_POLICY_DELETED(retentionPolicy.name)))
|
||||
} catch (error) {
|
||||
dispatch(
|
||||
errorThrown(
|
||||
error,
|
||||
`Failed to delete retentionPolicy: ${error.data.message}`
|
||||
NOTIFY_RETENTION_POLICY_DELETION_FAILED(error.data.message)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -452,10 +464,12 @@ export const updateRoleUsersAsync = (role, users) => async dispatch => {
|
|||
users,
|
||||
role.permissions
|
||||
)
|
||||
dispatch(publishAutoDismissingNotification('success', 'Role users updated'))
|
||||
dispatch(notify(NOTIFY_ROLE_USERS_UPDATED))
|
||||
dispatch(syncRole(role, data))
|
||||
} catch (error) {
|
||||
dispatch(errorThrown(error, `Failed to update role: ${error.data.message}`))
|
||||
dispatch(
|
||||
errorThrown(error, NOTIFY_ROLE_USERS_UPDATE_FAILED(error.data.message))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,13 +483,14 @@ export const updateRolePermissionsAsync = (
|
|||
role.users,
|
||||
permissions
|
||||
)
|
||||
dispatch(
|
||||
publishAutoDismissingNotification('success', 'Role permissions updated')
|
||||
)
|
||||
dispatch(notify(NOTIFY_ROLE_PERMISSIONS_UPDATED))
|
||||
dispatch(syncRole(role, data))
|
||||
} catch (error) {
|
||||
dispatch(
|
||||
errorThrown(error, `Failed to update role: ${error.data.message}`)
|
||||
errorThrown(
|
||||
error,
|
||||
NOTIFY_ROLE_PERMISSIONS_UPDATE_FAILED(error.data.message)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -486,13 +501,14 @@ export const updateUserPermissionsAsync = (
|
|||
) => async dispatch => {
|
||||
try {
|
||||
const {data} = await updateUserAJAX(user.links.self, {permissions})
|
||||
dispatch(
|
||||
publishAutoDismissingNotification('success', 'User permissions updated')
|
||||
)
|
||||
dispatch(notify(NOTIFY_DB_USER_PERMISSIONS_UPDATED))
|
||||
dispatch(syncUser(user, data))
|
||||
} catch (error) {
|
||||
dispatch(
|
||||
errorThrown(error, `Failed to update user: ${error.data.message}`)
|
||||
errorThrown(
|
||||
error,
|
||||
NOTIFY_DB_USER_PERMISSIONS_UPDATE_FAILED(error.data.message)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -500,11 +516,11 @@ export const updateUserPermissionsAsync = (
|
|||
export const updateUserRolesAsync = (user, roles) => async dispatch => {
|
||||
try {
|
||||
const {data} = await updateUserAJAX(user.links.self, {roles})
|
||||
dispatch(publishAutoDismissingNotification('success', 'User roles updated'))
|
||||
dispatch(notify(NOTIFY_DB_USER_ROLES_UPDATED))
|
||||
dispatch(syncUser(user, data))
|
||||
} catch (error) {
|
||||
dispatch(
|
||||
errorThrown(error, `Failed to update user: ${error.data.message}`)
|
||||
errorThrown(error, NOTIFY_DB_USER_ROLES_UPDATE_FAILED(error.data.message))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -512,13 +528,14 @@ export const updateUserRolesAsync = (user, roles) => async dispatch => {
|
|||
export const updateUserPasswordAsync = (user, password) => async dispatch => {
|
||||
try {
|
||||
const {data} = await updateUserAJAX(user.links.self, {password})
|
||||
dispatch(
|
||||
publishAutoDismissingNotification('success', 'User password updated')
|
||||
)
|
||||
dispatch(notify(NOTIFY_DB_USER_PASSWORD_UPDATED))
|
||||
dispatch(syncUser(user, data))
|
||||
} catch (error) {
|
||||
dispatch(
|
||||
errorThrown(error, `Failed to update user: ${error.data.message}`)
|
||||
errorThrown(
|
||||
error,
|
||||
NOTIFY_DB_USER_PASSWORD_UPDATE_FAILED(error.data.message)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import DatabaseTable from 'src/admin/components/DatabaseTable'
|
|||
|
||||
const DatabaseManager = ({
|
||||
databases,
|
||||
notify,
|
||||
isRFDisplayed,
|
||||
isAddDBDisabled,
|
||||
addDatabase,
|
||||
|
@ -46,7 +45,6 @@ const DatabaseManager = ({
|
|||
<DatabaseTable
|
||||
key={db.links.self}
|
||||
database={db}
|
||||
notify={notify}
|
||||
isRFDisplayed={isRFDisplayed}
|
||||
onEditDatabase={onEditDatabase}
|
||||
onKeyDownDatabase={onKeyDownDatabase}
|
||||
|
@ -74,7 +72,6 @@ const {arrayOf, bool, func, shape} = PropTypes
|
|||
|
||||
DatabaseManager.propTypes = {
|
||||
databases: arrayOf(shape()),
|
||||
notify: func,
|
||||
addDatabase: func,
|
||||
isRFDisplayed: bool,
|
||||
isAddDBDisabled: bool,
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import onClickOutside from 'react-onclickoutside'
|
||||
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import {formatRPDuration} from 'utils/formatting'
|
||||
import YesNoButtons from 'shared/components/YesNoButtons'
|
||||
import {DATABASE_TABLE} from 'src/admin/constants/tableSizing'
|
||||
import {NOTIFY_RETENTION_POLICY_CANT_HAVE_EMPTY_FIELDS} from 'shared/copy/notifications'
|
||||
|
||||
class DatabaseRow extends Component {
|
||||
constructor(props) {
|
||||
|
@ -110,7 +116,7 @@ class DatabaseRow extends Component {
|
|||
const replication = isRFDisplayed ? +this.replication.value.trim() : 1
|
||||
|
||||
if (!duration || (isRFDisplayed && !replication)) {
|
||||
notify('error', 'Fields cannot be empty')
|
||||
notify(NOTIFY_RETENTION_POLICY_CANT_HAVE_EMPTY_FIELDS)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -261,8 +267,12 @@ DatabaseRow.propTypes = {
|
|||
onCreate: func,
|
||||
onUpdate: func,
|
||||
onDelete: func,
|
||||
notify: func,
|
||||
notify: func.isRequired,
|
||||
isRFDisplayed: bool,
|
||||
}
|
||||
|
||||
export default onClickOutside(DatabaseRow)
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(null, mapDispatchToProps)(onClickOutside(DatabaseRow))
|
||||
|
|
|
@ -12,7 +12,6 @@ const {func, shape, bool} = PropTypes
|
|||
|
||||
const DatabaseTable = ({
|
||||
database,
|
||||
notify,
|
||||
isRFDisplayed,
|
||||
onEditDatabase,
|
||||
onKeyDownDatabase,
|
||||
|
@ -36,7 +35,6 @@ const DatabaseTable = ({
|
|||
>
|
||||
<DatabaseTableHeader
|
||||
database={database}
|
||||
notify={notify}
|
||||
onEdit={onEditDatabase}
|
||||
onCancel={onCancelDatabase}
|
||||
onDelete={onDeleteDatabase}
|
||||
|
@ -74,7 +72,6 @@ const DatabaseTable = ({
|
|||
return (
|
||||
<DatabaseRow
|
||||
key={rp.links.self}
|
||||
notify={notify}
|
||||
database={database}
|
||||
retentionPolicy={rp}
|
||||
onCreate={onCreateRetentionPolicy}
|
||||
|
@ -96,7 +93,6 @@ const DatabaseTable = ({
|
|||
DatabaseTable.propTypes = {
|
||||
onEditDatabase: func,
|
||||
database: shape(),
|
||||
notify: func,
|
||||
isRFDisplayed: bool,
|
||||
isAddRPDisabled: bool,
|
||||
onKeyDownDatabase: func,
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
||||
import {NOTIFY_DATABASE_DELETE_CONFIRMATION_REQUIRED} from 'shared/copy/notifications'
|
||||
|
||||
const DatabaseTableHeader = ({
|
||||
database,
|
||||
|
@ -76,7 +81,7 @@ const Header = ({
|
|||
|
||||
const onConfirm = db => {
|
||||
if (database.deleteCode !== `DELETE ${database.name}`) {
|
||||
return notify('error', `Type DELETE ${database.name} to confirm`)
|
||||
return notify(NOTIFY_DATABASE_DELETE_CONFIRMATION_REQUIRED(database.name))
|
||||
}
|
||||
|
||||
onDelete(db)
|
||||
|
@ -136,7 +141,7 @@ const {func, shape, bool} = PropTypes
|
|||
|
||||
DatabaseTableHeader.propTypes = {
|
||||
onEdit: func,
|
||||
notify: func,
|
||||
notify: func.isRequired,
|
||||
database: shape(),
|
||||
onKeyDown: func,
|
||||
onCancel: func,
|
||||
|
@ -150,7 +155,7 @@ DatabaseTableHeader.propTypes = {
|
|||
}
|
||||
|
||||
Header.propTypes = {
|
||||
notify: func,
|
||||
notify: func.isRequired,
|
||||
onConfirm: func,
|
||||
onCancel: func,
|
||||
onDelete: func,
|
||||
|
@ -170,4 +175,8 @@ EditHeader.propTypes = {
|
|||
isRFDisplayed: bool,
|
||||
}
|
||||
|
||||
export default DatabaseTableHeader
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(null, mapDispatchToProps)(DatabaseTableHeader)
|
||||
|
|
|
@ -16,6 +16,11 @@ const {
|
|||
colActions,
|
||||
} = ALL_USERS_TABLE
|
||||
|
||||
import {
|
||||
NOTIFY_CHRONOGRAF_USER_ADDED_TO_ORG,
|
||||
NOTIFY_CHRONOGRAF_USER_REMOVED_FROM_ORG,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
class AllUsersTable extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
@ -47,7 +52,7 @@ class AllUsersTable extends Component {
|
|||
this.props.onUpdateUserRoles(
|
||||
user,
|
||||
newRoles,
|
||||
`${user.name} has been added to ${organization.name}`
|
||||
NOTIFY_CHRONOGRAF_USER_ADDED_TO_ORG(user.name, organization.name)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -61,7 +66,7 @@ class AllUsersTable extends Component {
|
|||
this.props.onUpdateUserRoles(
|
||||
user,
|
||||
newRoles,
|
||||
`${user.name} has been removed from ${name}`
|
||||
NOTIFY_CHRONOGRAF_USER_REMOVED_FROM_ORG(user.name, name)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -84,7 +89,6 @@ class AllUsersTable extends Component {
|
|||
onCreateUser,
|
||||
authConfig,
|
||||
meID,
|
||||
notify,
|
||||
onDeleteUser,
|
||||
isLoading,
|
||||
} = this.props
|
||||
|
@ -154,7 +158,6 @@ class AllUsersTable extends Component {
|
|||
organizations={organizations}
|
||||
onBlur={this.handleBlurCreateUserRow}
|
||||
onCreateUser={onCreateUser}
|
||||
notify={notify}
|
||||
/>
|
||||
: null}
|
||||
</tbody>
|
||||
|
@ -209,7 +212,6 @@ AllUsersTable.propTypes = {
|
|||
superAdminNewUsers: bool,
|
||||
}),
|
||||
meID: string.isRequired,
|
||||
notify: func.isRequired,
|
||||
isLoading: bool.isRequired,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
|
||||
import {NOTIFY_CHRONOGRAF_USER_MISSING_NAME_AND_PROVIDER} from 'shared/copy/notifications'
|
||||
import {ALL_USERS_TABLE} from 'src/admin/constants/chronografTableSizing'
|
||||
const {
|
||||
colOrganizations,
|
||||
|
@ -80,8 +84,7 @@ class AllUsersTableRowNew extends Component {
|
|||
if (e.key === 'Enter') {
|
||||
if (preventCreate) {
|
||||
return this.props.notify(
|
||||
'warning',
|
||||
'User must have a name and provider'
|
||||
NOTIFY_CHRONOGRAF_USER_MISSING_NAME_AND_PROVIDER
|
||||
)
|
||||
}
|
||||
this.handleConfirmCreateUser()
|
||||
|
@ -181,4 +184,8 @@ AllUsersTableRowNew.propTypes = {
|
|||
notify: func.isRequired,
|
||||
}
|
||||
|
||||
export default AllUsersTableRowNew
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(null, mapDispatchToProps)(AllUsersTableRowNew)
|
||||
|
|
|
@ -35,14 +35,7 @@ class UsersTable extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
organization,
|
||||
users,
|
||||
onCreateUser,
|
||||
meID,
|
||||
notify,
|
||||
isLoading,
|
||||
} = this.props
|
||||
const {organization, users, onCreateUser, meID, isLoading} = this.props
|
||||
|
||||
const {isCreatingUser} = this.state
|
||||
const {colRole, colProvider, colScheme, colActions} = USERS_TABLE
|
||||
|
@ -83,7 +76,6 @@ class UsersTable extends Component {
|
|||
organization={organization}
|
||||
onBlur={this.handleBlurCreateUserRow}
|
||||
onCreateUser={onCreateUser}
|
||||
notify={notify}
|
||||
/>
|
||||
: null}
|
||||
{users.length
|
||||
|
@ -138,7 +130,6 @@ UsersTable.propTypes = {
|
|||
onUpdateUserRole: func.isRequired,
|
||||
onDeleteUser: func.isRequired,
|
||||
meID: string.isRequired,
|
||||
notify: func.isRequired,
|
||||
isLoading: bool.isRequired,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
|
||||
import {NOTIFY_CHRONOGRAF_USER_MISSING_NAME_AND_PROVIDER} from 'shared/copy/notifications'
|
||||
import {USERS_TABLE} from 'src/admin/constants/chronografTableSizing'
|
||||
import {USER_ROLES} from 'src/admin/constants/chronografAdmin'
|
||||
|
||||
|
@ -61,8 +66,7 @@ class UsersTableRowNew extends Component {
|
|||
if (e.key === 'Enter') {
|
||||
if (preventCreate) {
|
||||
return this.props.notify(
|
||||
'warning',
|
||||
'User must have a name and provider'
|
||||
NOTIFY_CHRONOGRAF_USER_MISSING_NAME_AND_PROVIDER
|
||||
)
|
||||
}
|
||||
this.handleConfirmCreateUser()
|
||||
|
@ -148,4 +152,8 @@ UsersTableRowNew.propTypes = {
|
|||
notify: func.isRequired,
|
||||
}
|
||||
|
||||
export default UsersTableRowNew
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(null, mapDispatchToProps)(UsersTableRowNew)
|
||||
|
|
|
@ -29,7 +29,12 @@ import AdminTabs from 'src/admin/components/AdminTabs'
|
|||
import SourceIndicator from 'shared/components/SourceIndicator'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import {
|
||||
NOTIFY_ROLE_NAME_INVALID,
|
||||
NOTIFY_DB_USER_NAME_PASSWORD_INVALID,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
const isValidUser = user => {
|
||||
const minLen = 3
|
||||
|
@ -75,7 +80,7 @@ class AdminInfluxDBPage extends Component {
|
|||
handleSaveUser = async user => {
|
||||
const {notify} = this.props
|
||||
if (!isValidUser(user)) {
|
||||
notify('error', 'Username and/or password too short')
|
||||
notify(NOTIFY_DB_USER_NAME_PASSWORD_INVALID)
|
||||
return
|
||||
}
|
||||
if (user.isNew) {
|
||||
|
@ -88,7 +93,7 @@ class AdminInfluxDBPage extends Component {
|
|||
handleSaveRole = async role => {
|
||||
const {notify} = this.props
|
||||
if (!isValidRole(role)) {
|
||||
notify('error', 'Role name too short')
|
||||
notify(NOTIFY_ROLE_NAME_INVALID)
|
||||
return
|
||||
}
|
||||
if (role.isNew) {
|
||||
|
@ -229,7 +234,7 @@ AdminInfluxDBPage.propTypes = {
|
|||
updateUserPermissions: func,
|
||||
updateUserRoles: func,
|
||||
updateUserPassword: func,
|
||||
notify: func,
|
||||
notify: func.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({adminInfluxDB: {users, roles, permissions}}) => ({
|
||||
|
@ -265,7 +270,7 @@ const mapDispatchToProps = dispatch => ({
|
|||
),
|
||||
updateUserRoles: bindActionCreators(updateUserRolesAsync, dispatch),
|
||||
updateUserPassword: bindActionCreators(updateUserPasswordAsync, dispatch),
|
||||
notify: bindActionCreators(publishAutoDismissingNotification, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AdminInfluxDBPage)
|
||||
|
|
|
@ -7,7 +7,13 @@ import _ from 'lodash'
|
|||
import DatabaseManager from 'src/admin/components/DatabaseManager'
|
||||
|
||||
import * as adminActionCreators from 'src/admin/actions/influxdb'
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import {
|
||||
NOTIFY_DATABASE_DELETE_CONFIRMATION_REQUIRED,
|
||||
NOTIFY_DATABASE_NAME_ALREADY_EXISTS,
|
||||
NOTIFY_DATABASE_NAME_INVALID,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
class DatabaseManagerPage extends Component {
|
||||
constructor(props) {
|
||||
|
@ -35,11 +41,11 @@ class DatabaseManagerPage extends Component {
|
|||
handleCreateDatabase = database => {
|
||||
const {actions, notify, source, databases} = this.props
|
||||
if (!database.name) {
|
||||
return notify('error', 'Database name cannot be blank')
|
||||
return notify(NOTIFY_DATABASE_NAME_INVALID)
|
||||
}
|
||||
|
||||
if (_.findIndex(databases, {name: database.name}, 1) !== -1) {
|
||||
return notify('error', 'A database by this name already exists')
|
||||
return notify(NOTIFY_DATABASE_NAME_ALREADY_EXISTS)
|
||||
}
|
||||
|
||||
actions.createDatabaseAsync(source.links.databases, database)
|
||||
|
@ -60,11 +66,11 @@ class DatabaseManagerPage extends Component {
|
|||
|
||||
if (key === 'Enter') {
|
||||
if (!database.name) {
|
||||
return notify('error', 'Database name cannot be blank')
|
||||
return notify(NOTIFY_DATABASE_NAME_INVALID)
|
||||
}
|
||||
|
||||
if (_.findIndex(databases, {name: database.name}, 1) !== -1) {
|
||||
return notify('error', 'A database by this name already exists')
|
||||
return notify(NOTIFY_DATABASE_NAME_ALREADY_EXISTS)
|
||||
}
|
||||
|
||||
actions.createDatabaseAsync(source.links.databases, database)
|
||||
|
@ -81,7 +87,9 @@ class DatabaseManagerPage extends Component {
|
|||
|
||||
if (key === 'Enter') {
|
||||
if (database.deleteCode !== `DELETE ${database.name}`) {
|
||||
return notify('error', `Please type DELETE ${database.name} to confirm`)
|
||||
return notify(
|
||||
NOTIFY_DATABASE_DELETE_CONFIRMATION_REQUIRED(database.name)
|
||||
)
|
||||
}
|
||||
|
||||
return actions.deleteDatabaseAsync(database)
|
||||
|
@ -153,7 +161,7 @@ DatabaseManagerPage.propTypes = {
|
|||
removeRetentionPolicy: func,
|
||||
deleteRetentionPolicyAsync: func,
|
||||
}),
|
||||
notify: func,
|
||||
notify: func.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({adminInfluxDB: {databases, retentionPolicies}}) => ({
|
||||
|
@ -163,7 +171,7 @@ const mapStateToProps = ({adminInfluxDB: {databases, retentionPolicies}}) => ({
|
|||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
actions: bindActionCreators(adminActionCreators, dispatch),
|
||||
notify: bindActionCreators(publishAutoDismissingNotification, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(DatabaseManagerPage)
|
||||
|
|
|
@ -4,7 +4,7 @@ import {connect} from 'react-redux'
|
|||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import * as adminChronografActionCreators from 'src/admin/actions/chronograf'
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import ProvidersTable from 'src/admin/components/chronograf/ProvidersTable'
|
||||
|
||||
|
@ -96,7 +96,7 @@ const mapStateToProps = ({
|
|||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
actions: bindActionCreators(adminChronografActionCreators, dispatch),
|
||||
notify: bindActionCreators(publishAutoDismissingNotification, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ProvidersPage)
|
||||
|
|
|
@ -12,13 +12,15 @@ import QueriesTable from 'src/admin/components/QueriesTable'
|
|||
import showDatabasesParser from 'shared/parsing/showDatabases'
|
||||
import showQueriesParser from 'shared/parsing/showQueries'
|
||||
import {TIMES} from 'src/admin/constants'
|
||||
import {NOTIFY_QUERIES_ERROR} from 'shared/copy/notifications'
|
||||
|
||||
import {
|
||||
loadQueries as loadQueriesAction,
|
||||
setQueryToKill as setQueryToKillAction,
|
||||
killQueryAsync,
|
||||
} from 'src/admin/actions/influxdb'
|
||||
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
class QueriesPage extends Component {
|
||||
componentDidMount() {
|
||||
|
@ -42,7 +44,7 @@ class QueriesPage extends Component {
|
|||
showDatabases(source.links.proxy).then(resp => {
|
||||
const {databases, errors} = showDatabasesParser(resp.data)
|
||||
if (errors.length) {
|
||||
errors.forEach(message => notify('error', message))
|
||||
errors.forEach(message => notify(NOTIFY_QUERIES_ERROR(message)))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -53,7 +55,9 @@ class QueriesPage extends Component {
|
|||
queryResponses.forEach(queryResponse => {
|
||||
const result = showQueriesParser(queryResponse.data)
|
||||
if (result.errors.length) {
|
||||
result.errors.forEach(message => notify('error', message))
|
||||
result.errors.forEach(message =>
|
||||
notify(NOTIFY_QUERIES_ERROR(message))
|
||||
)
|
||||
}
|
||||
|
||||
allQueries.push(...result.queries)
|
||||
|
@ -92,7 +96,7 @@ QueriesPage.propTypes = {
|
|||
queryIDToKill: string,
|
||||
setQueryToKill: func,
|
||||
killQuery: func,
|
||||
notify: func,
|
||||
notify: func.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({adminInfluxDB: {queries, queryIDToKill}}) => ({
|
||||
|
@ -104,7 +108,7 @@ const mapDispatchToProps = dispatch => ({
|
|||
loadQueries: bindActionCreators(loadQueriesAction, dispatch),
|
||||
setQueryToKill: bindActionCreators(setQueryToKillAction, dispatch),
|
||||
killQuery: bindActionCreators(killQueryAsync, dispatch),
|
||||
notify: bindActionCreators(publishAutoDismissingNotification, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(QueriesPage)
|
||||
|
|
|
@ -1,15 +1,44 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, {PureComponent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import * as adminChronografActionCreators from 'src/admin/actions/chronograf'
|
||||
import * as configActionCreators from 'shared/actions/config'
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import * as configActionCreators from 'src/shared/actions/config'
|
||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||
|
||||
import AllUsersTable from 'src/admin/components/chronograf/AllUsersTable'
|
||||
import {AuthLinks, User, Role, Organization} from 'src/types'
|
||||
|
||||
class AllUsersPage extends Component {
|
||||
interface Props {
|
||||
notify: () => void
|
||||
links: AuthLinks
|
||||
meID: string
|
||||
users: User[]
|
||||
organizations: Organization[]
|
||||
actionsAdmin: {
|
||||
loadUsersAsync: (link: string) => void
|
||||
loadOrganizationsAsync: (link: string) => void
|
||||
createUserAsync: (link: string, user: User) => void
|
||||
updateUserAsync: (user: User, updatedUser: User, message: string) => void
|
||||
deleteUserAsync: (
|
||||
user: User,
|
||||
deleteObj: {isAbsoluteDelete: boolean}
|
||||
) => void
|
||||
}
|
||||
actionsConfig: {
|
||||
getAuthConfigAsync: (link: string) => void
|
||||
updateAuthConfigAsync: () => void
|
||||
}
|
||||
authConfig: {
|
||||
superAdminNewUsers: boolean
|
||||
}
|
||||
}
|
||||
|
||||
interface State {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export class AllUsersPage extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
|
@ -23,32 +52,6 @@ class AllUsersPage extends Component {
|
|||
getAuthConfigAsync(links.config.auth)
|
||||
}
|
||||
|
||||
handleCreateUser = user => {
|
||||
const {links, actionsAdmin: {createUserAsync}} = this.props
|
||||
createUserAsync(links.allUsers, user)
|
||||
}
|
||||
|
||||
handleUpdateUserRoles = (user, roles, successMessage) => {
|
||||
const {actionsAdmin: {updateUserAsync}} = this.props
|
||||
const updatedUser = {...user, roles}
|
||||
updateUserAsync(user, updatedUser, successMessage)
|
||||
}
|
||||
|
||||
handleUpdateUserSuperAdmin = (user, superAdmin) => {
|
||||
const {actionsAdmin: {updateUserAsync}} = this.props
|
||||
const updatedUser = {...user, superAdmin}
|
||||
updateUserAsync(
|
||||
user,
|
||||
updatedUser,
|
||||
`${user.name}'s SuperAdmin status has been updated`
|
||||
)
|
||||
}
|
||||
|
||||
handleDeleteUser = user => {
|
||||
const {actionsAdmin: {deleteUserAsync}} = this.props
|
||||
deleteUserAsync(user, {isAbsoluteDelete: true})
|
||||
}
|
||||
|
||||
async componentWillMount() {
|
||||
const {
|
||||
links,
|
||||
|
@ -65,65 +68,66 @@ class AllUsersPage extends Component {
|
|||
this.setState({isLoading: false})
|
||||
}
|
||||
|
||||
handleCreateUser = (user: User) => {
|
||||
const {links, actionsAdmin: {createUserAsync}} = this.props
|
||||
createUserAsync(links.allUsers, user)
|
||||
}
|
||||
|
||||
handleUpdateUserRoles = (
|
||||
user: User,
|
||||
roles: Role[],
|
||||
successMessage: string
|
||||
) => {
|
||||
const {actionsAdmin: {updateUserAsync}} = this.props
|
||||
const updatedUser = {...user, roles}
|
||||
updateUserAsync(user, updatedUser, successMessage)
|
||||
}
|
||||
|
||||
handleUpdateUserSuperAdmin = (user: User, superAdmin: boolean) => {
|
||||
const {actionsAdmin: {updateUserAsync}} = this.props
|
||||
const updatedUser = {...user, superAdmin}
|
||||
updateUserAsync(
|
||||
user,
|
||||
updatedUser,
|
||||
`${user.name}'s SuperAdmin status has been updated`
|
||||
)
|
||||
}
|
||||
|
||||
handleDeleteUser = (user: User) => {
|
||||
const {actionsAdmin: {deleteUserAsync}} = this.props
|
||||
deleteUserAsync(user, {isAbsoluteDelete: true})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
organizations,
|
||||
meID,
|
||||
users,
|
||||
authConfig,
|
||||
actionsConfig,
|
||||
links,
|
||||
notify,
|
||||
authConfig,
|
||||
actionsConfig,
|
||||
organizations,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<AllUsersTable
|
||||
meID={meID}
|
||||
users={users}
|
||||
links={links}
|
||||
notify={notify}
|
||||
authConfig={authConfig}
|
||||
actionsConfig={actionsConfig}
|
||||
organizations={organizations}
|
||||
isLoading={this.state.isLoading}
|
||||
onDeleteUser={this.handleDeleteUser}
|
||||
onCreateUser={this.handleCreateUser}
|
||||
onUpdateUserRoles={this.handleUpdateUserRoles}
|
||||
onUpdateUserSuperAdmin={this.handleUpdateUserSuperAdmin}
|
||||
onDeleteUser={this.handleDeleteUser}
|
||||
links={links}
|
||||
authConfig={authConfig}
|
||||
actionsConfig={actionsConfig}
|
||||
notify={notify}
|
||||
isLoading={this.state.isLoading}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||
|
||||
AllUsersPage.propTypes = {
|
||||
links: shape({
|
||||
users: string.isRequired,
|
||||
config: shape({
|
||||
auth: string.isRequired,
|
||||
}).isRequired,
|
||||
}),
|
||||
meID: string.isRequired,
|
||||
users: arrayOf(shape),
|
||||
organizations: arrayOf(shape),
|
||||
actionsAdmin: shape({
|
||||
loadUsersAsync: func.isRequired,
|
||||
loadOrganizationsAsync: func.isRequired,
|
||||
createUserAsync: func.isRequired,
|
||||
updateUserAsync: func.isRequired,
|
||||
deleteUserAsync: func.isRequired,
|
||||
}),
|
||||
actionsConfig: shape({
|
||||
getAuthConfigAsync: func.isRequired,
|
||||
updateAuthConfigAsync: func.isRequired,
|
||||
}),
|
||||
authConfig: shape({
|
||||
superAdminNewUsers: bool,
|
||||
}),
|
||||
notify: func.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({
|
||||
links,
|
||||
adminChronograf: {organizations, users},
|
||||
|
@ -138,7 +142,7 @@ const mapStateToProps = ({
|
|||
const mapDispatchToProps = dispatch => ({
|
||||
actionsAdmin: bindActionCreators(adminChronografActionCreators, dispatch),
|
||||
actionsConfig: bindActionCreators(configActionCreators, dispatch),
|
||||
notify: bindActionCreators(publishAutoDismissingNotification, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AllUsersPage)
|
|
@ -4,7 +4,7 @@ import {connect} from 'react-redux'
|
|||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import * as adminChronografActionCreators from 'src/admin/actions/chronograf'
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import UsersTable from 'src/admin/components/chronograf/UsersTable'
|
||||
|
||||
|
@ -116,7 +116,7 @@ const mapStateToProps = ({links, adminChronograf: {organizations, users}}) => ({
|
|||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
actions: bindActionCreators(adminChronografActionCreators, dispatch),
|
||||
notify: bindActionCreators(publishAutoDismissingNotification, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UsersPage)
|
||||
|
|
|
@ -23,17 +23,17 @@ export const renameCell = cellName => ({
|
|||
},
|
||||
})
|
||||
|
||||
export const updateSingleStatColors = singleStatColors => ({
|
||||
type: 'UPDATE_SINGLE_STAT_COLORS',
|
||||
export const updateThresholdsListColors = thresholdsListColors => ({
|
||||
type: 'UPDATE_THRESHOLDS_LIST_COLORS',
|
||||
payload: {
|
||||
singleStatColors,
|
||||
thresholdsListColors,
|
||||
},
|
||||
})
|
||||
|
||||
export const updateSingleStatType = singleStatType => ({
|
||||
type: 'UPDATE_SINGLE_STAT_TYPE',
|
||||
export const updateThresholdsListType = thresholdsListType => ({
|
||||
type: 'UPDATE_THRESHOLDS_LIST_TYPE',
|
||||
payload: {
|
||||
singleStatType,
|
||||
thresholdsListType,
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -50,3 +50,10 @@ export const updateAxes = axes => ({
|
|||
axes,
|
||||
},
|
||||
})
|
||||
|
||||
export const updateTableOptions = tableOptions => ({
|
||||
type: 'UPDATE_TABLE_OPTIONS',
|
||||
payload: {
|
||||
tableOptions,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -8,10 +8,14 @@ import {
|
|||
runTemplateVariableQuery,
|
||||
} from 'src/dashboards/apis'
|
||||
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import {notify} from 'shared/actions/notifications'
|
||||
import {errorThrown} from 'shared/actions/errors'
|
||||
|
||||
import {NEW_DEFAULT_DASHBOARD_CELL} from 'src/dashboards/constants'
|
||||
import {
|
||||
NOTIFY_DASHBOARD_DELETED,
|
||||
NOTIFY_DASHBOARD_DELETE_FAILED,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
import {
|
||||
TEMPLATE_VARIABLE_SELECTED,
|
||||
|
@ -257,15 +261,13 @@ export const deleteDashboardAsync = dashboard => async dispatch => {
|
|||
dispatch(deleteDashboard(dashboard))
|
||||
try {
|
||||
await deleteDashboardAJAX(dashboard)
|
||||
dispatch(
|
||||
publishAutoDismissingNotification(
|
||||
'success',
|
||||
'Dashboard deleted successfully.'
|
||||
)
|
||||
)
|
||||
dispatch(notify(NOTIFY_DASHBOARD_DELETED(dashboard.name)))
|
||||
} catch (error) {
|
||||
dispatch(
|
||||
errorThrown(error, `Failed to delete dashboard: ${error.data.message}.`)
|
||||
errorThrown(
|
||||
error,
|
||||
NOTIFY_DASHBOARD_DELETE_FAILED(dashboard.name, error.data.message)
|
||||
)
|
||||
)
|
||||
dispatch(deleteDashboardFailed(dashboard))
|
||||
}
|
||||
|
|
|
@ -8,12 +8,15 @@ import Input from 'src/dashboards/components/DisplayOptionsInput'
|
|||
import {Tabber, Tab} from 'src/dashboards/components/Tabber'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
|
||||
import {DISPLAY_OPTIONS, TOOLTIP_CONTENT} from 'src/dashboards/constants'
|
||||
import {
|
||||
AXES_SCALE_OPTIONS,
|
||||
TOOLTIP_Y_VALUE_FORMAT,
|
||||
} from 'src/dashboards/constants/cellEditor'
|
||||
import {GRAPH_TYPES} from 'src/dashboards/graphics/graph'
|
||||
|
||||
import {updateAxes} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
|
||||
const {LINEAR, LOG, BASE_2, BASE_10} = DISPLAY_OPTIONS
|
||||
const {LINEAR, LOG, BASE_2, BASE_10} = AXES_SCALE_OPTIONS
|
||||
const getInputMin = scale => (scale === LOG ? '0' : null)
|
||||
|
||||
class AxesOptions extends Component {
|
||||
|
@ -140,7 +143,7 @@ class AxesOptions extends Component {
|
|||
<Tabber
|
||||
labelText="Y-Value's Format"
|
||||
tipID="Y-Values's Format"
|
||||
tipContent={TOOLTIP_CONTENT.FORMAT}
|
||||
tipContent={TOOLTIP_Y_VALUE_FORMAT}
|
||||
>
|
||||
<Tab
|
||||
text="K/M/B"
|
||||
|
|
|
@ -4,7 +4,7 @@ import PropTypes from 'prop-types'
|
|||
import _ from 'lodash'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import ResizeContainer from 'shared/components/ResizeContainer'
|
||||
import ResizeContainer from 'src/shared/components/ResizeContainer'
|
||||
import QueryMaker from 'src/dashboards/components/QueryMaker'
|
||||
import Visualization from 'src/dashboards/components/Visualization'
|
||||
import OverlayControls from 'src/dashboards/components/OverlayControls'
|
||||
|
@ -21,10 +21,10 @@ import {
|
|||
removeUnselectedTemplateValues,
|
||||
TYPE_QUERY_CONFIG,
|
||||
} from 'src/dashboards/constants'
|
||||
import {OVERLAY_TECHNOLOGY} from 'shared/constants/classNames'
|
||||
import {OVERLAY_TECHNOLOGY} from 'src/shared/constants/classNames'
|
||||
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants'
|
||||
import {AUTO_GROUP_BY} from 'shared/constants'
|
||||
import {stringifyColorValues} from 'src/dashboards/constants/gaugeColors'
|
||||
import {AUTO_GROUP_BY} from 'src/shared/constants'
|
||||
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
|
||||
|
||||
class CellEditorOverlay extends Component {
|
||||
constructor(props) {
|
||||
|
@ -106,7 +106,7 @@ class CellEditorOverlay extends Component {
|
|||
|
||||
handleSaveCell = () => {
|
||||
const {queriesWorkingDraft, staticLegend} = this.state
|
||||
const {cell, singleStatColors, gaugeColors} = this.props
|
||||
const {cell, thresholdsListColors, gaugeColors} = this.props
|
||||
|
||||
const queries = queriesWorkingDraft.map(q => {
|
||||
const timeRange = q.range || {upper: null, lower: ':dashboardTime:'}
|
||||
|
@ -120,13 +120,18 @@ class CellEditorOverlay extends Component {
|
|||
})
|
||||
|
||||
let colors = []
|
||||
if (cell.type === 'gauge') {
|
||||
colors = stringifyColorValues(gaugeColors)
|
||||
} else if (
|
||||
cell.type === 'single-stat' ||
|
||||
cell.type === 'line-plus-single-stat'
|
||||
) {
|
||||
colors = stringifyColorValues(singleStatColors)
|
||||
|
||||
switch (cell.type) {
|
||||
case 'gauge': {
|
||||
colors = stringifyColorValues(gaugeColors)
|
||||
break
|
||||
}
|
||||
case 'single-stat':
|
||||
case 'line-plus-single-stat':
|
||||
case 'table': {
|
||||
colors = stringifyColorValues(thresholdsListColors)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
this.props.onSave({
|
||||
|
@ -375,8 +380,8 @@ CellEditorOverlay.propTypes = {
|
|||
}).isRequired,
|
||||
dashboardID: string.isRequired,
|
||||
sources: arrayOf(shape()),
|
||||
singleStatType: string.isRequired,
|
||||
singleStatColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
thresholdsListType: string.isRequired,
|
||||
thresholdsListColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
gaugeColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ class DisplayOptions extends Component {
|
|||
staticLegend,
|
||||
onToggleStaticLegend,
|
||||
onResetFocus,
|
||||
queryConfigs,
|
||||
} = this.props
|
||||
switch (type) {
|
||||
case 'gauge':
|
||||
|
@ -49,7 +50,12 @@ class DisplayOptions extends Component {
|
|||
case 'single-stat':
|
||||
return <SingleStatOptions onResetFocus={onResetFocus} />
|
||||
case 'table':
|
||||
return <TableOptions />
|
||||
return (
|
||||
<TableOptions
|
||||
onResetFocus={onResetFocus}
|
||||
queryConfigs={queryConfigs}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<AxesOptions
|
||||
|
|
|
@ -11,10 +11,10 @@ import Threshold from 'src/dashboards/components/Threshold'
|
|||
|
||||
import {
|
||||
COLOR_TYPE_THRESHOLD,
|
||||
GAUGE_COLORS,
|
||||
THRESHOLD_COLORS,
|
||||
MAX_THRESHOLDS,
|
||||
MIN_THRESHOLDS,
|
||||
} from 'src/dashboards/constants/gaugeColors'
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
import {
|
||||
updateGaugeColors,
|
||||
|
@ -27,7 +27,7 @@ class GaugeOptions extends Component {
|
|||
const sortedColors = _.sortBy(gaugeColors, color => color.value)
|
||||
|
||||
if (sortedColors.length <= MAX_THRESHOLDS) {
|
||||
const randomColor = _.random(0, GAUGE_COLORS.length - 1)
|
||||
const randomColor = _.random(0, THRESHOLD_COLORS.length - 1)
|
||||
|
||||
const maxValue = sortedColors[sortedColors.length - 1].value
|
||||
const minValue = sortedColors[0].value
|
||||
|
@ -43,8 +43,8 @@ class GaugeOptions extends Component {
|
|||
type: COLOR_TYPE_THRESHOLD,
|
||||
id: uuid.v4(),
|
||||
value: randomValue,
|
||||
hex: GAUGE_COLORS[randomColor].hex,
|
||||
name: GAUGE_COLORS[randomColor].name,
|
||||
hex: THRESHOLD_COLORS[randomColor].hex,
|
||||
name: THRESHOLD_COLORS[randomColor].name,
|
||||
}
|
||||
|
||||
const updatedColors = _.sortBy(
|
||||
|
@ -165,9 +165,9 @@ class GaugeOptions extends Component {
|
|||
>
|
||||
<div className="display-options--cell-wrapper">
|
||||
<h5 className="display-options--header">Gauge Controls</h5>
|
||||
<div className="gauge-controls">
|
||||
<div className="thresholds-list">
|
||||
<button
|
||||
className="btn btn-sm btn-primary gauge-controls--add-threshold"
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={this.handleAddThreshold}
|
||||
disabled={disableAddThreshold}
|
||||
>
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import InputClickToEdit from 'shared/components/InputClickToEdit'
|
||||
|
||||
const GraphOptionsCustomizableColumn = ({
|
||||
originalColumnName,
|
||||
newColumnName,
|
||||
onColumnRename,
|
||||
}) => {
|
||||
return (
|
||||
<div className="column-controls--section">
|
||||
<div className="column-controls--label">
|
||||
{originalColumnName}
|
||||
</div>
|
||||
<InputClickToEdit
|
||||
value={newColumnName}
|
||||
wrapperClass="column-controls-input"
|
||||
onBlur={onColumnRename}
|
||||
placeholder="Rename..."
|
||||
appearAsNormalInput={true}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
const {func, string} = PropTypes
|
||||
|
||||
GraphOptionsCustomizableColumn.propTypes = {
|
||||
originalColumnName: string,
|
||||
newColumnName: string,
|
||||
onColumnRename: func,
|
||||
}
|
||||
|
||||
export default GraphOptionsCustomizableColumn
|
|
@ -0,0 +1,48 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
|
||||
import InputClickToEdit from 'src/shared/components/InputClickToEdit'
|
||||
|
||||
type Column = {
|
||||
internalName: string
|
||||
displayName: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
internalName: string
|
||||
displayName: string
|
||||
onColumnRename: (column: Column) => void
|
||||
}
|
||||
|
||||
class GraphOptionsCustomizableColumn extends PureComponent<Props, {}> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleColumnRename = this.handleColumnRename.bind(this)
|
||||
}
|
||||
|
||||
handleColumnRename(rename) {
|
||||
const {onColumnRename, internalName} = this.props
|
||||
onColumnRename({internalName, displayName: rename})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {internalName, displayName} = this.props
|
||||
|
||||
return (
|
||||
<div className="column-controls--section">
|
||||
<div className="column-controls--label">
|
||||
{internalName}
|
||||
</div>
|
||||
<InputClickToEdit
|
||||
value={displayName}
|
||||
wrapperClass="column-controls-input"
|
||||
onBlur={this.handleColumnRename}
|
||||
placeholder="Rename..."
|
||||
appearAsNormalInput={true}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default GraphOptionsCustomizableColumn
|
|
@ -1,10 +1,22 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, {SFC} from 'react'
|
||||
|
||||
import GraphOptionsCustomizableColumn from 'src/dashboards/components/GraphOptionsCustomizableColumn'
|
||||
import uuid from 'uuid'
|
||||
|
||||
const GraphOptionsCustomizeColumns = ({columns, onColumnRename}) => {
|
||||
type Column = {
|
||||
internalName: string
|
||||
displayName: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
columns: Column[]
|
||||
onColumnRename: (column: Column) => void
|
||||
}
|
||||
|
||||
const GraphOptionsCustomizeColumns: SFC<Props> = ({
|
||||
columns,
|
||||
onColumnRename,
|
||||
}) => {
|
||||
return (
|
||||
<div className="graph-options-group">
|
||||
<label className="form-label">Customize Columns</label>
|
||||
|
@ -12,8 +24,8 @@ const GraphOptionsCustomizeColumns = ({columns, onColumnRename}) => {
|
|||
return (
|
||||
<GraphOptionsCustomizableColumn
|
||||
key={uuid.v4()}
|
||||
originalColumnName={col.name}
|
||||
newColumnName={col.newName}
|
||||
internalName={col.internalName}
|
||||
displayName={col.displayName}
|
||||
onColumnRename={onColumnRename}
|
||||
/>
|
||||
)
|
||||
|
@ -21,16 +33,5 @@ const GraphOptionsCustomizeColumns = ({columns, onColumnRename}) => {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
|
||||
GraphOptionsCustomizeColumns.propTypes = {
|
||||
columns: arrayOf(
|
||||
shape({
|
||||
name: string,
|
||||
newName: string,
|
||||
})
|
||||
),
|
||||
onColumnRename: func,
|
||||
}
|
||||
|
||||
export default GraphOptionsCustomizeColumns
|
|
@ -2,30 +2,39 @@ import React from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
SINGLE_STAT_BG,
|
||||
SINGLE_STAT_TEXT,
|
||||
} from 'src/dashboards/constants/gaugeColors'
|
||||
THRESHOLD_TYPE_BG,
|
||||
THRESHOLD_TYPE_TEXT,
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
// TODO: Needs major refactoring to make thresholds a much more general component to be shared between single stat, gauge, and table.
|
||||
const GraphOptionsTextWrapping = ({singleStatType, onToggleTextWrapping}) => {
|
||||
const GraphOptionsTextWrapping = ({
|
||||
thresholdsListType,
|
||||
onToggleTextWrapping,
|
||||
}) => {
|
||||
return (
|
||||
<div className="form-group col-xs-12">
|
||||
<label>Text Wrapping</label>
|
||||
<ul className="nav nav-tablist nav-tablist-sm">
|
||||
<li
|
||||
className={`${singleStatType === SINGLE_STAT_BG ? 'active' : ''}`}
|
||||
className={`${thresholdsListType === THRESHOLD_TYPE_BG
|
||||
? 'active'
|
||||
: ''}`}
|
||||
onClick={onToggleTextWrapping}
|
||||
>
|
||||
Truncate
|
||||
</li>
|
||||
<li
|
||||
className={`${singleStatType === SINGLE_STAT_TEXT ? 'active' : ''}`}
|
||||
className={`${thresholdsListType === THRESHOLD_TYPE_TEXT
|
||||
? 'active'
|
||||
: ''}`}
|
||||
onClick={onToggleTextWrapping}
|
||||
>
|
||||
Wrap
|
||||
</li>
|
||||
<li
|
||||
className={`${singleStatType === SINGLE_STAT_BG ? 'active' : ''}`}
|
||||
className={`${thresholdsListType === THRESHOLD_TYPE_BG
|
||||
? 'active'
|
||||
: ''}`}
|
||||
onClick={onToggleTextWrapping}
|
||||
>
|
||||
Single Line
|
||||
|
@ -37,7 +46,7 @@ const GraphOptionsTextWrapping = ({singleStatType, onToggleTextWrapping}) => {
|
|||
const {func, string} = PropTypes
|
||||
|
||||
GraphOptionsTextWrapping.propTypes = {
|
||||
singleStatType: string,
|
||||
thresholdsListType: string,
|
||||
onToggleTextWrapping: func,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
SINGLE_STAT_BG,
|
||||
SINGLE_STAT_TEXT,
|
||||
} from 'src/dashboards/constants/gaugeColors'
|
||||
|
||||
// TODO: Needs major refactoring to make thresholds a much more general component to be shared between single stat, gauge, and table.
|
||||
const GraphOptionsThresholdColoring = ({
|
||||
onToggleSingleStatType,
|
||||
singleStatType,
|
||||
}) => {
|
||||
return (
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label>Threshold Coloring</label>
|
||||
<ul className="nav nav-tablist nav-tablist-sm">
|
||||
<li
|
||||
className={`${singleStatType === SINGLE_STAT_BG ? 'active' : ''}`}
|
||||
onClick={onToggleSingleStatType(SINGLE_STAT_BG)}
|
||||
>
|
||||
Background
|
||||
</li>
|
||||
<li
|
||||
className={`${singleStatType === SINGLE_STAT_TEXT ? 'active' : ''}`}
|
||||
onClick={onToggleSingleStatType(SINGLE_STAT_TEXT)}
|
||||
>
|
||||
Text
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
const {func, string} = PropTypes
|
||||
|
||||
GraphOptionsThresholdColoring.propTypes = {
|
||||
singleStatType: string,
|
||||
onToggleSingleStatType: func,
|
||||
}
|
||||
|
||||
export default GraphOptionsThresholdColoring
|
|
@ -1,78 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Threshold from 'src/dashboards/components/Threshold'
|
||||
import ColorDropdown from 'shared/components/ColorDropdown'
|
||||
|
||||
import {
|
||||
GAUGE_COLORS,
|
||||
SINGLE_STAT_BASE,
|
||||
} from 'src/dashboards/constants/gaugeColors'
|
||||
|
||||
// TODO: Needs major refactoring to make thresholds a much more general component to be shared between single stat, gauge, and table.
|
||||
const GraphOptionsThresholds = ({
|
||||
onAddThreshold,
|
||||
disableAddThreshold,
|
||||
sortedColors,
|
||||
formatColor,
|
||||
onChooseColor,
|
||||
onValidateColorValue,
|
||||
onUpdateColorValue,
|
||||
onDeleteThreshold,
|
||||
}) => {
|
||||
return (
|
||||
<div className="gauge-controls graph-options-group">
|
||||
<label className="form-label">Thresholds</label>
|
||||
<button
|
||||
className="btn btn-sm btn-primary gauge-controls--add-threshold"
|
||||
onClick={onAddThreshold}
|
||||
disabled={disableAddThreshold}
|
||||
>
|
||||
<span className="icon plus" /> Add Threshold
|
||||
</button>
|
||||
{sortedColors.map(
|
||||
color =>
|
||||
color.id === SINGLE_STAT_BASE
|
||||
? <div className="gauge-controls--section" key={color.id}>
|
||||
<div className="gauge-controls--label">Base Color</div>
|
||||
<ColorDropdown
|
||||
colors={GAUGE_COLORS}
|
||||
selected={formatColor(color)}
|
||||
onChoose={onChooseColor(color)}
|
||||
stretchToFit={true}
|
||||
/>
|
||||
</div>
|
||||
: <Threshold
|
||||
visualizationType="single-stat"
|
||||
threshold={color}
|
||||
key={color.id}
|
||||
onChooseColor={onChooseColor}
|
||||
onValidateColorValue={onValidateColorValue}
|
||||
onUpdateColorValue={onUpdateColorValue}
|
||||
onDeleteThreshold={onDeleteThreshold}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
const {arrayOf, bool, func, shape, string, number} = PropTypes
|
||||
|
||||
GraphOptionsThresholds.propTypes = {
|
||||
onAddThreshold: func,
|
||||
disableAddThreshold: bool,
|
||||
sortedColors: arrayOf(
|
||||
shape({
|
||||
hex: string,
|
||||
id: string,
|
||||
name: string,
|
||||
type: string,
|
||||
value: number,
|
||||
})
|
||||
),
|
||||
formatColor: func,
|
||||
onChooseColor: func,
|
||||
onValidateColorValue: func,
|
||||
onUpdateColorValue: func,
|
||||
onDeleteThreshold: func,
|
||||
}
|
||||
|
||||
export default GraphOptionsThresholds
|
|
@ -1,22 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
const GraphOptionsTimeFormat = ({TimeFormat, onTimeFormatChange}) =>
|
||||
<div className="form-group col-xs-12">
|
||||
<label>Time Format</label>
|
||||
<input
|
||||
className="form-control input-sm"
|
||||
placeholder="mm/dd/yyyy HH:mm:ss.ss"
|
||||
value={TimeFormat}
|
||||
onChange={onTimeFormatChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
const {func, string} = PropTypes
|
||||
|
||||
GraphOptionsTimeFormat.propTypes = {
|
||||
TimeFormat: string,
|
||||
onTimeFormatChange: func,
|
||||
}
|
||||
|
||||
export default GraphOptionsTimeFormat
|
|
@ -0,0 +1,90 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import InputClickToEdit from 'src/shared/components/InputClickToEdit'
|
||||
import {Dropdown} from 'src/shared/components/Dropdown'
|
||||
import QuestionMarkTooltip from 'src/shared/components/QuestionMarkTooltip'
|
||||
import {
|
||||
FORMAT_OPTIONS,
|
||||
TIME_FORMAT_DEFAULT,
|
||||
TIME_FORMAT_CUSTOM,
|
||||
} from 'src/shared/constants/tableGraph'
|
||||
|
||||
interface TimeFormatOptions {
|
||||
text: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
timeFormat: string
|
||||
onTimeFormatChange: (format: string) => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
customFormat: boolean
|
||||
format: string
|
||||
}
|
||||
|
||||
class GraphOptionsTimeFormat extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
format: this.props.timeFormat || TIME_FORMAT_DEFAULT,
|
||||
customFormat: false,
|
||||
}
|
||||
}
|
||||
|
||||
get onTimeFormatChange() {
|
||||
return this.props.onTimeFormatChange
|
||||
}
|
||||
|
||||
handleChooseFormat = (formatOption: TimeFormatOptions) => {
|
||||
if (formatOption.text === TIME_FORMAT_CUSTOM) {
|
||||
this.setState({customFormat: true})
|
||||
} else {
|
||||
this.setState({format: formatOption.text, customFormat: false})
|
||||
this.onTimeFormatChange(formatOption.text)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {format, customFormat} = this.state
|
||||
const {onTimeFormatChange} = this.props
|
||||
const tipContent =
|
||||
'For information on formatting, see http://momentjs.com/docs/#/parsing/string-format/'
|
||||
|
||||
const formatOption = FORMAT_OPTIONS.find(op => op.text === format)
|
||||
const showCustom = !formatOption || customFormat
|
||||
|
||||
return (
|
||||
<div className="form-group col-xs-12">
|
||||
<label>
|
||||
Time Format
|
||||
{customFormat &&
|
||||
<QuestionMarkTooltip
|
||||
tipID="Time Axis Format"
|
||||
tipContent={tipContent}
|
||||
/>}
|
||||
</label>
|
||||
<Dropdown
|
||||
items={FORMAT_OPTIONS}
|
||||
selected={showCustom ? TIME_FORMAT_CUSTOM : format}
|
||||
buttonColor="btn-default"
|
||||
buttonSize="btn-xs"
|
||||
className="dropdown-stretch"
|
||||
onChoose={this.handleChooseFormat}
|
||||
/>
|
||||
{showCustom &&
|
||||
<div className="column-controls--section">
|
||||
<InputClickToEdit
|
||||
wrapperClass="column-controls-input "
|
||||
value={format}
|
||||
onBlur={onTimeFormatChange}
|
||||
onChange={onTimeFormatChange}
|
||||
placeholder="Enter custom format..."
|
||||
appearAsNormalInput={true}
|
||||
/>
|
||||
</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default GraphOptionsTimeFormat
|
|
@ -3,121 +3,13 @@ import PropTypes from 'prop-types'
|
|||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import _ from 'lodash'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import Threshold from 'src/dashboards/components/Threshold'
|
||||
import ColorDropdown from 'shared/components/ColorDropdown'
|
||||
import ThresholdsList from 'shared/components/ThresholdsList'
|
||||
import ThresholdsListTypeToggle from 'shared/components/ThresholdsListTypeToggle'
|
||||
|
||||
import {
|
||||
GAUGE_COLORS,
|
||||
DEFAULT_VALUE_MIN,
|
||||
DEFAULT_VALUE_MAX,
|
||||
MAX_THRESHOLDS,
|
||||
SINGLE_STAT_BASE,
|
||||
SINGLE_STAT_TEXT,
|
||||
SINGLE_STAT_BG,
|
||||
} from 'src/dashboards/constants/gaugeColors'
|
||||
|
||||
import {
|
||||
updateSingleStatType,
|
||||
updateSingleStatColors,
|
||||
updateAxes,
|
||||
} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
|
||||
const formatColor = color => {
|
||||
const {hex, name} = color
|
||||
return {hex, name}
|
||||
}
|
||||
import {updateAxes} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
|
||||
class SingleStatOptions extends Component {
|
||||
handleToggleSingleStatType = newType => () => {
|
||||
const {handleUpdateSingleStatType} = this.props
|
||||
|
||||
handleUpdateSingleStatType(newType)
|
||||
}
|
||||
|
||||
handleAddThreshold = () => {
|
||||
const {
|
||||
singleStatColors,
|
||||
singleStatType,
|
||||
handleUpdateSingleStatColors,
|
||||
onResetFocus,
|
||||
} = this.props
|
||||
|
||||
const randomColor = _.random(0, GAUGE_COLORS.length - 1)
|
||||
|
||||
const maxValue = DEFAULT_VALUE_MIN
|
||||
const minValue = DEFAULT_VALUE_MAX
|
||||
|
||||
let randomValue = _.round(_.random(minValue, maxValue, true), 2)
|
||||
|
||||
if (singleStatColors.length > 0) {
|
||||
const colorsValues = _.mapValues(singleStatColors, 'value')
|
||||
do {
|
||||
randomValue = _.round(_.random(minValue, maxValue, true), 2)
|
||||
} while (_.includes(colorsValues, randomValue))
|
||||
}
|
||||
|
||||
const newThreshold = {
|
||||
type: singleStatType,
|
||||
id: uuid.v4(),
|
||||
value: randomValue,
|
||||
hex: GAUGE_COLORS[randomColor].hex,
|
||||
name: GAUGE_COLORS[randomColor].name,
|
||||
}
|
||||
|
||||
const updatedColors = _.sortBy(
|
||||
[...singleStatColors, newThreshold],
|
||||
color => color.value
|
||||
)
|
||||
|
||||
handleUpdateSingleStatColors(updatedColors)
|
||||
onResetFocus()
|
||||
}
|
||||
|
||||
handleDeleteThreshold = threshold => () => {
|
||||
const {handleUpdateSingleStatColors, onResetFocus} = this.props
|
||||
const singleStatColors = this.props.singleStatColors.filter(
|
||||
color => color.id !== threshold.id
|
||||
)
|
||||
const sortedColors = _.sortBy(singleStatColors, color => color.value)
|
||||
|
||||
handleUpdateSingleStatColors(sortedColors)
|
||||
onResetFocus()
|
||||
}
|
||||
|
||||
handleChooseColor = threshold => chosenColor => {
|
||||
const {handleUpdateSingleStatColors} = this.props
|
||||
|
||||
const singleStatColors = this.props.singleStatColors.map(
|
||||
color =>
|
||||
color.id === threshold.id
|
||||
? {...color, hex: chosenColor.hex, name: chosenColor.name}
|
||||
: color
|
||||
)
|
||||
|
||||
handleUpdateSingleStatColors(singleStatColors)
|
||||
}
|
||||
|
||||
handleUpdateColorValue = (threshold, value) => {
|
||||
const {handleUpdateSingleStatColors} = this.props
|
||||
|
||||
const singleStatColors = this.props.singleStatColors.map(
|
||||
color => (color.id === threshold.id ? {...color, value} : color)
|
||||
)
|
||||
|
||||
handleUpdateSingleStatColors(singleStatColors)
|
||||
}
|
||||
|
||||
handleValidateColorValue = (threshold, targetValue) => {
|
||||
const {singleStatColors} = this.props
|
||||
const sortedColors = _.sortBy(singleStatColors, color => color.value)
|
||||
|
||||
return !sortedColors.some(color => color.value === targetValue)
|
||||
}
|
||||
|
||||
handleUpdatePrefix = e => {
|
||||
const {handleUpdateAxes, axes} = this.props
|
||||
const newAxes = {...axes, y: {...axes.y, prefix: e.target.value}}
|
||||
|
@ -132,21 +24,8 @@ class SingleStatOptions extends Component {
|
|||
handleUpdateAxes(newAxes)
|
||||
}
|
||||
|
||||
handleSortColors = () => {
|
||||
const {singleStatColors, handleUpdateSingleStatColors} = this.props
|
||||
const sortedColors = _.sortBy(singleStatColors, color => color.value)
|
||||
|
||||
handleUpdateSingleStatColors(sortedColors)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
singleStatColors,
|
||||
singleStatType,
|
||||
axes: {y: {prefix, suffix}},
|
||||
} = this.props
|
||||
|
||||
const disableAddThreshold = singleStatColors.length > MAX_THRESHOLDS
|
||||
const {axes: {y: {prefix, suffix}}, onResetFocus} = this.props
|
||||
|
||||
return (
|
||||
<FancyScrollbar
|
||||
|
@ -155,38 +34,7 @@ class SingleStatOptions extends Component {
|
|||
>
|
||||
<div className="display-options--cell-wrapper">
|
||||
<h5 className="display-options--header">Single Stat Controls</h5>
|
||||
<div className="gauge-controls">
|
||||
<button
|
||||
className="btn btn-sm btn-primary gauge-controls--add-threshold"
|
||||
onClick={this.handleAddThreshold}
|
||||
disabled={disableAddThreshold}
|
||||
>
|
||||
<span className="icon plus" /> Add Threshold
|
||||
</button>
|
||||
{singleStatColors.map(
|
||||
color =>
|
||||
color.id === SINGLE_STAT_BASE
|
||||
? <div className="gauge-controls--section" key={color.id}>
|
||||
<div className="gauge-controls--label">Base Color</div>
|
||||
<ColorDropdown
|
||||
colors={GAUGE_COLORS}
|
||||
selected={formatColor(color)}
|
||||
onChoose={this.handleChooseColor(color)}
|
||||
stretchToFit={true}
|
||||
/>
|
||||
</div>
|
||||
: <Threshold
|
||||
visualizationType="single-stat"
|
||||
threshold={color}
|
||||
key={color.id}
|
||||
onChooseColor={this.handleChooseColor}
|
||||
onValidateColorValue={this.handleValidateColorValue}
|
||||
onUpdateColorValue={this.handleUpdateColorValue}
|
||||
onDeleteThreshold={this.handleDeleteThreshold}
|
||||
onSortColors={this.handleSortColors}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<ThresholdsList onResetFocus={onResetFocus} />
|
||||
<div className="graph-options-group form-group-wrapper">
|
||||
<div className="form-group col-xs-6">
|
||||
<label>Prefix</label>
|
||||
|
@ -208,27 +56,7 @@ class SingleStatOptions extends Component {
|
|||
maxLength="5"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-6">
|
||||
<label>Coloring</label>
|
||||
<ul className="nav nav-tablist nav-tablist-sm">
|
||||
<li
|
||||
className={`${singleStatType === SINGLE_STAT_BG
|
||||
? 'active'
|
||||
: ''}`}
|
||||
onClick={this.handleToggleSingleStatType(SINGLE_STAT_BG)}
|
||||
>
|
||||
Background
|
||||
</li>
|
||||
<li
|
||||
className={`${singleStatType === SINGLE_STAT_TEXT
|
||||
? 'active'
|
||||
: ''}`}
|
||||
onClick={this.handleToggleSingleStatType(SINGLE_STAT_TEXT)}
|
||||
>
|
||||
Text
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ThresholdsListTypeToggle containerClass="form-group col-xs-6" />
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
|
@ -236,47 +64,19 @@ class SingleStatOptions extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, number, shape, string} = PropTypes
|
||||
|
||||
SingleStatOptions.defaultProps = {
|
||||
colors: [],
|
||||
}
|
||||
const {func, shape} = PropTypes
|
||||
|
||||
SingleStatOptions.propTypes = {
|
||||
singleStatType: string.isRequired,
|
||||
singleStatColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
handleUpdateSingleStatType: func.isRequired,
|
||||
handleUpdateSingleStatColors: func.isRequired,
|
||||
handleUpdateAxes: func.isRequired,
|
||||
axes: shape({}).isRequired,
|
||||
onResetFocus: func.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({
|
||||
cellEditorOverlay: {singleStatType, singleStatColors, cell: {axes}},
|
||||
}) => ({
|
||||
singleStatType,
|
||||
singleStatColors,
|
||||
const mapStateToProps = ({cellEditorOverlay: {cell: {axes}}}) => ({
|
||||
axes,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleUpdateSingleStatType: bindActionCreators(
|
||||
updateSingleStatType,
|
||||
dispatch
|
||||
),
|
||||
handleUpdateSingleStatColors: bindActionCreators(
|
||||
updateSingleStatColors,
|
||||
dispatch
|
||||
),
|
||||
handleUpdateAxes: bindActionCreators(updateAxes, dispatch),
|
||||
})
|
||||
|
||||
|
|
|
@ -1,177 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import _ from 'lodash'
|
||||
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import GraphOptionsTimeFormat from 'src/dashboards/components/GraphOptionsTimeFormat'
|
||||
import GraphOptionsTimeAxis from 'src/dashboards/components/GraphOptionsTimeAxis'
|
||||
import GraphOptionsSortBy from 'src/dashboards/components/GraphOptionsSortBy'
|
||||
import GraphOptionsTextWrapping from 'src/dashboards/components/GraphOptionsTextWrapping'
|
||||
import GraphOptionsCustomizeColumns from 'src/dashboards/components/GraphOptionsCustomizeColumns'
|
||||
import GraphOptionsThresholds from 'src/dashboards/components/GraphOptionsThresholds'
|
||||
import GraphOptionsThresholdColoring from 'src/dashboards/components/GraphOptionsThresholdColoring'
|
||||
|
||||
import {MAX_THRESHOLDS} from 'src/dashboards/constants/gaugeColors'
|
||||
|
||||
import {
|
||||
updateSingleStatType,
|
||||
updateSingleStatColors,
|
||||
updateAxes,
|
||||
} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
|
||||
const formatColor = color => {
|
||||
const {hex, name} = color
|
||||
return {hex, name}
|
||||
}
|
||||
|
||||
class TableOptions extends Component {
|
||||
state = {TimeAxis: 'VERTICAL', TimeFormat: 'mm/dd/yyyy HH:mm:ss.ss'}
|
||||
|
||||
handleToggleSingleStatType = () => {}
|
||||
|
||||
handleAddThreshold = () => {}
|
||||
|
||||
handleDeleteThreshold = () => () => {}
|
||||
|
||||
handleChooseColor = () => () => {}
|
||||
|
||||
handleChooseSortBy = () => {}
|
||||
|
||||
handleTimeFormatChange = () => {}
|
||||
|
||||
handleToggleTimeAxis = () => {}
|
||||
|
||||
handleToggleTextWrapping = () => {}
|
||||
|
||||
handleColumnRename = () => {}
|
||||
|
||||
handleUpdateColorValue = () => {}
|
||||
|
||||
handleValidateColorValue = () => {}
|
||||
|
||||
render() {
|
||||
const {
|
||||
singleStatColors,
|
||||
singleStatType,
|
||||
// axes: {y: {prefix, suffix}},
|
||||
} = this.props
|
||||
|
||||
const {TimeFormat, TimeAxis} = this.state
|
||||
|
||||
const disableAddThreshold = singleStatColors.length > MAX_THRESHOLDS
|
||||
|
||||
const sortedColors = _.sortBy(singleStatColors, color => color.value)
|
||||
|
||||
const columns = [
|
||||
'cpu.mean_usage_system',
|
||||
'cpu.mean_usage_idle',
|
||||
'cpu.mean_usage_user',
|
||||
].map(col => ({
|
||||
text: col,
|
||||
name: col,
|
||||
newName: '',
|
||||
}))
|
||||
const tableSortByOptions = [
|
||||
'cpu.mean_usage_system',
|
||||
'cpu.mean_usage_idle',
|
||||
'cpu.mean_usage_user',
|
||||
].map(col => ({text: col}))
|
||||
|
||||
return (
|
||||
<FancyScrollbar
|
||||
className="display-options--cell y-axis-controls"
|
||||
autoHide={false}
|
||||
>
|
||||
<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}
|
||||
/>
|
||||
<GraphOptionsTimeAxis
|
||||
TimeAxis={TimeAxis}
|
||||
onToggleTimeAxis={this.handleToggleTimeAxis}
|
||||
/>
|
||||
<GraphOptionsSortBy
|
||||
sortByOptions={tableSortByOptions}
|
||||
onChooseSortBy={this.handleChooseSortBy}
|
||||
/>
|
||||
<GraphOptionsTextWrapping
|
||||
singleStatType={singleStatType}
|
||||
onToggleTextWrapping={this.handleToggleTextWrapping}
|
||||
/>
|
||||
</div>
|
||||
<GraphOptionsCustomizeColumns
|
||||
columns={columns}
|
||||
onColumnRename={this.handleColumnRename}
|
||||
/>
|
||||
<GraphOptionsThresholds
|
||||
onAddThreshold={this.handleAddThreshold}
|
||||
disableAddThreshold={disableAddThreshold}
|
||||
sortedColors={sortedColors}
|
||||
formatColor={formatColor}
|
||||
onChooseColor={this.handleChooseColor}
|
||||
onValidateColorValue={this.handleValidateColorValue}
|
||||
onUpdateColorValue={this.handleUpdateColorValue}
|
||||
onDeleteThreshold={this.handleDeleteThreshold}
|
||||
/>
|
||||
<div className="form-group-wrapper graph-options-group">
|
||||
<GraphOptionsThresholdColoring
|
||||
onToggleSingleStatType={this.handleToggleSingleStatType}
|
||||
singleStatColors={singleStatType}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, number, shape, string} = PropTypes
|
||||
|
||||
TableOptions.defaultProps = {
|
||||
colors: [],
|
||||
}
|
||||
|
||||
TableOptions.propTypes = {
|
||||
singleStatType: string.isRequired,
|
||||
singleStatColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
handleUpdateSingleStatType: func.isRequired,
|
||||
handleUpdateSingleStatColors: func.isRequired,
|
||||
handleUpdateAxes: func.isRequired,
|
||||
axes: shape({}).isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({
|
||||
cellEditorOverlay: {singleStatType, singleStatColors, cell: {axes}},
|
||||
}) => ({
|
||||
singleStatType,
|
||||
singleStatColors,
|
||||
axes,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleUpdateSingleStatType: bindActionCreators(
|
||||
updateSingleStatType,
|
||||
dispatch
|
||||
),
|
||||
handleUpdateSingleStatColors: bindActionCreators(
|
||||
updateSingleStatColors,
|
||||
dispatch
|
||||
),
|
||||
handleUpdateAxes: bindActionCreators(updateAxes, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TableOptions)
|
|
@ -0,0 +1,161 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import _ from 'lodash'
|
||||
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import GraphOptionsTimeFormat from 'src/dashboards/components/GraphOptionsTimeFormat'
|
||||
import GraphOptionsTimeAxis from 'src/dashboards/components/GraphOptionsTimeAxis'
|
||||
import GraphOptionsSortBy from 'src/dashboards/components/GraphOptionsSortBy'
|
||||
import GraphOptionsTextWrapping from 'src/dashboards/components/GraphOptionsTextWrapping'
|
||||
import GraphOptionsCustomizeColumns from 'src/dashboards/components/GraphOptionsCustomizeColumns'
|
||||
import ThresholdsList from 'src/shared/components/ThresholdsList'
|
||||
import ThresholdsListTypeToggle from 'src/shared/components/ThresholdsListTypeToggle'
|
||||
|
||||
import {TIME_COLUMN_DEFAULT} from 'src/shared/constants/tableGraph'
|
||||
import {updateTableOptions} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
|
||||
type TableColumn = {
|
||||
internalName: string
|
||||
displayName: string
|
||||
}
|
||||
|
||||
type Options = {
|
||||
timeFormat: string
|
||||
verticalTimeAxis: boolean
|
||||
sortBy: TableColumn
|
||||
wrapping: string
|
||||
columnNames: TableColumn[]
|
||||
}
|
||||
|
||||
type QueryConfig = {
|
||||
measurement: string
|
||||
fields: [
|
||||
{
|
||||
alias: string
|
||||
value: string
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
interface Props {
|
||||
queryConfigs: QueryConfig[]
|
||||
handleUpdateTableOptions: (options: Options) => void
|
||||
tableOptions: Options
|
||||
onResetFocus: () => void
|
||||
}
|
||||
|
||||
export class TableOptions extends PureComponent<Props, {}> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const {queryConfigs, handleUpdateTableOptions, tableOptions} = this.props
|
||||
const {columnNames} = tableOptions
|
||||
const timeColumn =
|
||||
(columnNames && columnNames.find(c => c.internalName === 'time')) ||
|
||||
TIME_COLUMN_DEFAULT
|
||||
|
||||
const columns = [
|
||||
timeColumn,
|
||||
..._.flatten(
|
||||
queryConfigs.map(qc => {
|
||||
const {measurement, fields} = qc
|
||||
return fields.map(f => {
|
||||
const internalName = `${measurement}.${f.alias}`
|
||||
const existing = columnNames.find(
|
||||
c => c.internalName === internalName
|
||||
)
|
||||
return existing || {internalName, displayName: ''}
|
||||
})
|
||||
})
|
||||
),
|
||||
]
|
||||
|
||||
handleUpdateTableOptions({...tableOptions, columnNames: columns})
|
||||
}
|
||||
|
||||
handleChooseSortBy = () => {}
|
||||
|
||||
handleTimeFormatChange = timeFormat => {
|
||||
const {tableOptions, handleUpdateTableOptions} = this.props
|
||||
handleUpdateTableOptions({...tableOptions, timeFormat})
|
||||
}
|
||||
|
||||
handleToggleTimeAxis = () => {}
|
||||
|
||||
handleToggleTextWrapping = () => {}
|
||||
|
||||
handleColumnRename = column => {
|
||||
const {handleUpdateTableOptions, tableOptions} = this.props
|
||||
const {columnNames} = tableOptions
|
||||
const updatedColumns = columnNames.map(
|
||||
op => (op.internalName === column.internalName ? column : op)
|
||||
)
|
||||
handleUpdateTableOptions({...tableOptions, columnNames: updatedColumns})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
tableOptions: {timeFormat, columnNames: columns},
|
||||
onResetFocus,
|
||||
} = this.props
|
||||
|
||||
const TimeAxis = 'vertical'
|
||||
|
||||
const tableSortByOptions = [
|
||||
'cpu.mean_usage_system',
|
||||
'cpu.mean_usage_idle',
|
||||
'cpu.mean_usage_user',
|
||||
].map(col => ({text: col}))
|
||||
|
||||
return (
|
||||
<FancyScrollbar
|
||||
className="display-options--cell y-axis-controls"
|
||||
autoHide={false}
|
||||
>
|
||||
<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}
|
||||
/>
|
||||
<GraphOptionsTimeAxis
|
||||
TimeAxis={TimeAxis}
|
||||
onToggleTimeAxis={this.handleToggleTimeAxis}
|
||||
/>
|
||||
<GraphOptionsSortBy
|
||||
sortByOptions={tableSortByOptions}
|
||||
onChooseSortBy={this.handleChooseSortBy}
|
||||
/>
|
||||
<GraphOptionsTextWrapping
|
||||
thresholdsListType="background"
|
||||
onToggleTextWrapping={this.handleToggleTextWrapping}
|
||||
/>
|
||||
</div>
|
||||
<GraphOptionsCustomizeColumns
|
||||
columns={columns}
|
||||
onColumnRename={this.handleColumnRename}
|
||||
/>
|
||||
<ThresholdsList showListHeading={true} onResetFocus={onResetFocus} />
|
||||
<div className="form-group-wrapper graph-options-group">
|
||||
<ThresholdsListTypeToggle containerClass="form-group col-xs-6" />
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({cellEditorOverlay: {cell: {tableOptions}}}) => ({
|
||||
tableOptions,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleUpdateTableOptions: bindActionCreators(updateTableOptions, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TableOptions)
|
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
|
|||
|
||||
import ColorDropdown from 'shared/components/ColorDropdown'
|
||||
|
||||
import {GAUGE_COLORS} from 'src/dashboards/constants/gaugeColors'
|
||||
import {THRESHOLD_COLORS} from 'shared/constants/thresholds'
|
||||
|
||||
class Threshold extends Component {
|
||||
constructor(props) {
|
||||
|
@ -54,14 +54,14 @@ class Threshold extends Component {
|
|||
const selectedColor = {hex, name}
|
||||
|
||||
let label = 'Threshold'
|
||||
let labelClass = 'gauge-controls--label-editable'
|
||||
let labelClass = 'threshold-item--label__editable'
|
||||
let canBeDeleted = true
|
||||
|
||||
if (visualizationType === 'gauge') {
|
||||
labelClass =
|
||||
isMin || isMax
|
||||
? 'gauge-controls--label'
|
||||
: 'gauge-controls--label-editable'
|
||||
? 'threshold-item--label'
|
||||
: 'threshold-item--label__editable'
|
||||
canBeDeleted = !(isMin || isMax)
|
||||
}
|
||||
|
||||
|
@ -73,17 +73,17 @@ class Threshold extends Component {
|
|||
}
|
||||
|
||||
const inputClass = valid
|
||||
? 'form-control input-sm gauge-controls--input'
|
||||
: 'form-control input-sm gauge-controls--input form-volcano'
|
||||
? 'form-control input-sm threshold-item--input'
|
||||
: 'form-control input-sm threshold-item--input form-volcano'
|
||||
|
||||
return (
|
||||
<div className="gauge-controls--section">
|
||||
<div className="threshold-item">
|
||||
<div className={labelClass}>
|
||||
{label}
|
||||
</div>
|
||||
{canBeDeleted
|
||||
? <button
|
||||
className="btn btn-default btn-sm btn-square gauge-controls--delete"
|
||||
className="btn btn-default btn-sm btn-square"
|
||||
onClick={onDeleteThreshold(threshold)}
|
||||
>
|
||||
<span className="icon remove" />
|
||||
|
@ -99,7 +99,7 @@ class Threshold extends Component {
|
|||
ref={r => (this.thresholdInputRef = r)}
|
||||
/>
|
||||
<ColorDropdown
|
||||
colors={GAUGE_COLORS}
|
||||
colors={THRESHOLD_COLORS}
|
||||
selected={selectedColor}
|
||||
onChoose={onChooseColor(threshold)}
|
||||
disabled={isMax && disableMaxColor}
|
||||
|
|
|
@ -2,11 +2,11 @@ import React from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import RefreshingGraph from 'shared/components/RefreshingGraph'
|
||||
import RefreshingGraph from 'src/shared/components/RefreshingGraph'
|
||||
import buildQueries from 'utils/buildQueriesForGraphs'
|
||||
import VisualizationName from 'src/dashboards/components/VisualizationName'
|
||||
|
||||
import {stringifyColorValues} from 'src/dashboards/constants/gaugeColors'
|
||||
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
|
||||
|
||||
const DashVisualization = (
|
||||
{
|
||||
|
@ -20,11 +20,12 @@ const DashVisualization = (
|
|||
editQueryStatus,
|
||||
resizerTopHeight,
|
||||
staticLegend,
|
||||
singleStatColors,
|
||||
thresholdsListColors,
|
||||
tableOptions,
|
||||
},
|
||||
{source: {links: {proxy}}}
|
||||
) => {
|
||||
const colors = type === 'gauge' ? gaugeColors : singleStatColors
|
||||
const colors = type === 'gauge' ? gaugeColors : thresholdsListColors
|
||||
|
||||
return (
|
||||
<div className="graph">
|
||||
|
@ -34,6 +35,7 @@ const DashVisualization = (
|
|||
colors={stringifyColorValues(colors)}
|
||||
axes={axes}
|
||||
type={type}
|
||||
tableOptions={tableOptions}
|
||||
queries={buildQueries(proxy, queryConfigs, timeRange)}
|
||||
templates={templates}
|
||||
autoRefresh={autoRefresh}
|
||||
|
@ -63,8 +65,9 @@ DashVisualization.propTypes = {
|
|||
bounds: arrayOf(string),
|
||||
}),
|
||||
}),
|
||||
tableOptions: shape({}),
|
||||
resizerTopHeight: number,
|
||||
singleStatColors: arrayOf(
|
||||
thresholdsListColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
|
@ -94,12 +97,17 @@ DashVisualization.contextTypes = {
|
|||
}
|
||||
|
||||
const mapStateToProps = ({
|
||||
cellEditorOverlay: {singleStatColors, gaugeColors, cell: {type, axes}},
|
||||
cellEditorOverlay: {
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
cell: {type, axes, tableOptions},
|
||||
},
|
||||
}) => ({
|
||||
gaugeColors,
|
||||
singleStatColors,
|
||||
thresholdsListColors,
|
||||
type,
|
||||
axes,
|
||||
tableOptions,
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, null)(DashVisualization)
|
||||
|
|
|
@ -22,7 +22,9 @@ import {TEMPLATE_TYPES} from 'src/dashboards/constants'
|
|||
import generateTemplateVariableQuery from 'src/dashboards/utils/templateVariableQueryGenerator'
|
||||
|
||||
import {errorThrown as errorThrownAction} from 'shared/actions/errors'
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import {NOTIFY_TEMP_VAR_ALREADY_EXISTS} from 'shared/copy/notifications'
|
||||
|
||||
const compact = values => uniq(values).filter(value => /\S/.test(value))
|
||||
|
||||
|
@ -144,10 +146,7 @@ class RowWrapper extends Component {
|
|||
const tempVar = `\u003a${_tempVar}\u003a` // add ':'s
|
||||
|
||||
if (tempVarAlreadyExists(tempVar, id)) {
|
||||
return notify(
|
||||
'error',
|
||||
`Variable '${_tempVar}' already exists. Please enter a new value.`
|
||||
)
|
||||
return notify(NOTIFY_TEMP_VAR_ALREADY_EXISTS(_tempVar))
|
||||
}
|
||||
|
||||
this.setState({
|
||||
|
@ -348,7 +347,7 @@ TemplateVariableRow.propTypes = {
|
|||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onErrorThrown: bindActionCreators(errorThrownAction, dispatch),
|
||||
notify: bindActionCreators(publishAutoDismissingNotification, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(null, mapDispatchToProps)(OnClickOutside(RowWrapper))
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import {DEFAULT_TABLE_OPTIONS} from 'src/shared/constants/tableGraph'
|
||||
|
||||
export const initializeOptions = cellType => {
|
||||
switch (cellType) {
|
||||
case 'table':
|
||||
return DEFAULT_TABLE_OPTIONS
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export const AXES_SCALE_OPTIONS = {
|
||||
LINEAR: 'linear',
|
||||
LOG: 'log',
|
||||
BASE_2: '2',
|
||||
BASE_10: '10',
|
||||
}
|
||||
|
||||
export const TOOLTIP_Y_VALUE_FORMAT =
|
||||
'<p><strong>K/M/B</strong> = Thousand / Million / Billion<br/><strong>K/M/G</strong> = Kilo / Mega / Giga </p>'
|
|
@ -91,16 +91,7 @@ export const removeUnselectedTemplateValues = templates => {
|
|||
return {...template, values: selectedValues}
|
||||
})
|
||||
}
|
||||
export const DISPLAY_OPTIONS = {
|
||||
LINEAR: 'linear',
|
||||
LOG: 'log',
|
||||
BASE_2: '2',
|
||||
BASE_10: '10',
|
||||
}
|
||||
export const TOOLTIP_CONTENT = {
|
||||
FORMAT:
|
||||
'<p><strong>K/M/B</strong> = Thousand / Million / Billion<br/><strong>K/M/G</strong> = Kilo / Mega / Giga </p>',
|
||||
}
|
||||
|
||||
export const TYPE_QUERY_CONFIG = 'queryConfig'
|
||||
export const TYPE_SHIFTED = 'shifted queryConfig'
|
||||
export const TYPE_IFQL = 'ifql'
|
||||
|
|
|
@ -16,7 +16,7 @@ import TemplateVariableManager from 'src/dashboards/components/template_variable
|
|||
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
||||
|
||||
import {errorThrown as errorThrownAction} from 'shared/actions/errors'
|
||||
import {publishNotification} from 'shared/actions/notifications'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
import idNormalizer, {TYPE_ID} from 'src/normalizers/id'
|
||||
import {NULL_HOVER_TIME} from 'src/shared/constants/tableGraph'
|
||||
|
||||
|
@ -34,6 +34,7 @@ import {
|
|||
} from 'shared/actions/app'
|
||||
import {presentationButtonDispatcher} from 'shared/dispatchers'
|
||||
import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'shared/constants'
|
||||
import {NOTIFY_DASHBOARD_NOT_FOUND} from 'shared/copy/notifications'
|
||||
|
||||
const FORMAT_INFLUXQL = 'influxql'
|
||||
const defaultTimeRange = {
|
||||
|
@ -90,7 +91,7 @@ class DashboardPage extends Component {
|
|||
|
||||
if (!dashboard) {
|
||||
router.push(`/sources/${source.id}/dashboards`)
|
||||
return notify('error', `Dashboard ${dashboardID} could not be found`)
|
||||
return notify(NOTIFY_DASHBOARD_NOT_FOUND(dashboardID))
|
||||
}
|
||||
|
||||
// Refresh and persists influxql generated template variable values.
|
||||
|
@ -272,8 +273,8 @@ class DashboardPage extends Component {
|
|||
manualRefresh,
|
||||
onManualRefresh,
|
||||
cellQueryStatus,
|
||||
singleStatType,
|
||||
singleStatColors,
|
||||
thresholdsListType,
|
||||
thresholdsListColors,
|
||||
dashboardActions,
|
||||
inPresentationMode,
|
||||
handleChooseAutoRefresh,
|
||||
|
@ -379,8 +380,8 @@ class DashboardPage extends Component {
|
|||
onCancel={handleHideCellEditorOverlay}
|
||||
templates={templatesIncludingDashTime}
|
||||
editQueryStatus={dashboardActions.editCellQueryStatus}
|
||||
singleStatType={singleStatType}
|
||||
singleStatColors={singleStatColors}
|
||||
thresholdsListType={thresholdsListType}
|
||||
thresholdsListColors={thresholdsListColors}
|
||||
gaugeColors={gaugeColors}
|
||||
/>
|
||||
: null}
|
||||
|
@ -511,8 +512,8 @@ DashboardPage.propTypes = {
|
|||
handleShowCellEditorOverlay: func.isRequired,
|
||||
handleHideCellEditorOverlay: func.isRequired,
|
||||
selectedCell: shape({}),
|
||||
singleStatType: string.isRequired,
|
||||
singleStatColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
thresholdsListType: string.isRequired,
|
||||
thresholdsListColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
gaugeColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
}
|
||||
|
||||
|
@ -526,7 +527,12 @@ const mapStateToProps = (state, {params: {dashboardID}}) => {
|
|||
sources,
|
||||
dashTimeV1,
|
||||
auth: {me, isUsingAuth},
|
||||
cellEditorOverlay: {cell, singleStatType, singleStatColors, gaugeColors},
|
||||
cellEditorOverlay: {
|
||||
cell,
|
||||
thresholdsListType,
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
},
|
||||
} = state
|
||||
|
||||
const meRole = _.get(me, 'role', null)
|
||||
|
@ -553,8 +559,8 @@ const mapStateToProps = (state, {params: {dashboardID}}) => {
|
|||
inPresentationMode,
|
||||
showTemplateControlBar,
|
||||
selectedCell,
|
||||
singleStatType,
|
||||
singleStatColors,
|
||||
thresholdsListType,
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
}
|
||||
}
|
||||
|
@ -568,7 +574,7 @@ const mapDispatchToProps = dispatch => ({
|
|||
handleClickPresentationButton: presentationButtonDispatcher(dispatch),
|
||||
dashboardActions: bindActionCreators(dashboardActionCreators, dispatch),
|
||||
errorThrown: bindActionCreators(errorThrownAction, dispatch),
|
||||
notify: bindActionCreators(publishNotification, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
getAnnotationsAsync: bindActionCreators(
|
||||
annotationActions.getAnnotationsAsync,
|
||||
dispatch
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import {
|
||||
SINGLE_STAT_TEXT,
|
||||
DEFAULT_SINGLESTAT_COLORS,
|
||||
THRESHOLD_TYPE_TEXT,
|
||||
DEFAULT_THRESHOLDS_LIST_COLORS,
|
||||
DEFAULT_GAUGE_COLORS,
|
||||
validateGaugeColors,
|
||||
validateSingleStatColors,
|
||||
getSingleStatType,
|
||||
} from 'src/dashboards/constants/gaugeColors'
|
||||
validateThresholdsListColors,
|
||||
getThresholdsListType,
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
import {initializeOptions} from 'src/dashboards/constants/cellEditor'
|
||||
|
||||
export const initialState = {
|
||||
cell: null,
|
||||
singleStatType: SINGLE_STAT_TEXT,
|
||||
singleStatColors: DEFAULT_SINGLESTAT_COLORS,
|
||||
thresholdsListType: THRESHOLD_TYPE_TEXT,
|
||||
thresholdsListColors: DEFAULT_THRESHOLDS_LIST_COLORS,
|
||||
gaugeColors: DEFAULT_GAUGE_COLORS,
|
||||
}
|
||||
|
||||
|
@ -19,11 +21,22 @@ export default function cellEditorOverlay(state = initialState, action) {
|
|||
case 'SHOW_CELL_EDITOR_OVERLAY': {
|
||||
const {cell, cell: {colors}} = action.payload
|
||||
|
||||
const singleStatType = getSingleStatType(colors)
|
||||
const singleStatColors = validateSingleStatColors(colors, singleStatType)
|
||||
const thresholdsListType = getThresholdsListType(colors)
|
||||
const thresholdsListColors = validateThresholdsListColors(
|
||||
colors,
|
||||
thresholdsListType
|
||||
)
|
||||
const gaugeColors = validateGaugeColors(colors)
|
||||
|
||||
return {...state, cell, singleStatType, singleStatColors, gaugeColors}
|
||||
const tableOptions = cell.tableOptions || initializeOptions('table')
|
||||
|
||||
return {
|
||||
...state,
|
||||
cell: {...cell, tableOptions},
|
||||
thresholdsListType,
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
}
|
||||
}
|
||||
|
||||
case 'HIDE_CELL_EDITOR_OVERLAY': {
|
||||
|
@ -46,21 +59,21 @@ export default function cellEditorOverlay(state = initialState, action) {
|
|||
return {...state, cell}
|
||||
}
|
||||
|
||||
case 'UPDATE_SINGLE_STAT_COLORS': {
|
||||
const {singleStatColors} = action.payload
|
||||
case 'UPDATE_THRESHOLDS_LIST_COLORS': {
|
||||
const {thresholdsListColors} = action.payload
|
||||
|
||||
return {...state, singleStatColors}
|
||||
return {...state, thresholdsListColors}
|
||||
}
|
||||
|
||||
case 'UPDATE_SINGLE_STAT_TYPE': {
|
||||
const {singleStatType} = action.payload
|
||||
case 'UPDATE_THRESHOLDS_LIST_TYPE': {
|
||||
const {thresholdsListType} = action.payload
|
||||
|
||||
const singleStatColors = state.singleStatColors.map(color => ({
|
||||
const thresholdsListColors = state.thresholdsListColors.map(color => ({
|
||||
...color,
|
||||
type: singleStatType,
|
||||
type: thresholdsListType,
|
||||
}))
|
||||
|
||||
return {...state, singleStatType, singleStatColors}
|
||||
return {...state, thresholdsListType, thresholdsListColors}
|
||||
}
|
||||
|
||||
case 'UPDATE_GAUGE_COLORS': {
|
||||
|
@ -75,6 +88,13 @@ export default function cellEditorOverlay(state = initialState, action) {
|
|||
|
||||
return {...state, cell}
|
||||
}
|
||||
|
||||
case 'UPDATE_TABLE_OPTIONS': {
|
||||
const {tableOptions} = action.payload
|
||||
const cell = {...state.cell, tableOptions}
|
||||
|
||||
return {...state, cell}
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
import {writeLineProtocol as writeLineProtocolAJAX} from 'src/data_explorer/apis'
|
||||
|
||||
import {errorThrown} from 'shared/actions/errors'
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import {notify} from 'shared/actions/notifications'
|
||||
|
||||
import {NOTIFY_DATA_WRITTEN} from 'shared/copy/notifications'
|
||||
|
||||
export const writeLineProtocolAsync = (source, db, data) => async dispatch => {
|
||||
try {
|
||||
await writeLineProtocolAJAX(source, db, data)
|
||||
dispatch(
|
||||
publishAutoDismissingNotification(
|
||||
'success',
|
||||
'Data was written successfully'
|
||||
)
|
||||
)
|
||||
dispatch(notify(NOTIFY_DATA_WRITTEN))
|
||||
} catch (response) {
|
||||
const errorMessage = `Write failed: ${response.data.error}`
|
||||
dispatch(errorThrown(response, errorMessage))
|
||||
|
|
|
@ -12,6 +12,12 @@ import ManualRefresh from 'src/shared/components/ManualRefresh'
|
|||
import {getCpuAndLoadForHosts, getLayouts, getAppsForHosts} from '../apis'
|
||||
import {getEnv} from 'src/shared/apis/env'
|
||||
import {setAutoRefresh} from 'shared/actions/app'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import {
|
||||
NOTIFY_UNABLE_TO_GET_HOSTS,
|
||||
NOTIFY_UNABLE_TO_GET_APPS,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
class HostsPage extends Component {
|
||||
constructor(props) {
|
||||
|
@ -25,9 +31,9 @@ class HostsPage extends Component {
|
|||
}
|
||||
|
||||
async fetchHostsData() {
|
||||
const {source, links, addFlashMessage} = this.props
|
||||
const {source, links, notify} = this.props
|
||||
const {telegrafSystemInterval} = await getEnv(links.environment)
|
||||
const hostsError = 'Unable to get hosts'
|
||||
const hostsError = NOTIFY_UNABLE_TO_GET_HOSTS.message
|
||||
try {
|
||||
const hosts = await getCpuAndLoadForHosts(
|
||||
source.links.proxy,
|
||||
|
@ -51,7 +57,7 @@ class HostsPage extends Component {
|
|||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
addFlashMessage({type: 'error', text: hostsError})
|
||||
notify(NOTIFY_UNABLE_TO_GET_HOSTS)
|
||||
this.setState({
|
||||
hostsError,
|
||||
hostsLoading: false,
|
||||
|
@ -60,14 +66,14 @@ class HostsPage extends Component {
|
|||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const {addFlashMessage, autoRefresh} = this.props
|
||||
const {notify, autoRefresh} = this.props
|
||||
|
||||
this.setState({hostsLoading: true}) // Only print this once
|
||||
const {data} = await getLayouts()
|
||||
this.layouts = data.layouts
|
||||
if (!this.layouts) {
|
||||
const layoutError = 'Unable to get apps for hosts'
|
||||
addFlashMessage({type: 'error', text: layoutError})
|
||||
const layoutError = NOTIFY_UNABLE_TO_GET_APPS.message
|
||||
notify(NOTIFY_UNABLE_TO_GET_APPS)
|
||||
this.setState({
|
||||
hostsError: layoutError,
|
||||
hostsLoading: false,
|
||||
|
@ -169,11 +175,11 @@ HostsPage.propTypes = {
|
|||
links: shape({
|
||||
environment: string.isRequired,
|
||||
}),
|
||||
addFlashMessage: func,
|
||||
autoRefresh: number.isRequired,
|
||||
manualRefresh: number,
|
||||
onChooseAutoRefresh: func.isRequired,
|
||||
onManualRefresh: func.isRequired,
|
||||
notify: func.isRequired,
|
||||
}
|
||||
|
||||
HostsPage.defaultProps = {
|
||||
|
@ -182,6 +188,7 @@ HostsPage.defaultProps = {
|
|||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onChooseAutoRefresh: bindActionCreators(setAutoRefresh, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import uuid from 'uuid'
|
||||
import {getActiveKapacitor} from 'shared/apis'
|
||||
import {publishNotification} from 'shared/actions/notifications'
|
||||
import {notify} from 'shared/actions/notifications'
|
||||
import {
|
||||
getRules,
|
||||
getRule as getRuleAJAX,
|
||||
|
@ -11,6 +11,17 @@ import {
|
|||
} from 'src/kapacitor/apis'
|
||||
import {errorThrown} from 'shared/actions/errors'
|
||||
|
||||
import {
|
||||
NOTIFY_ALERT_RULE_DELETED,
|
||||
NOTIFY_ALERT_RULE_DELETION_FAILED,
|
||||
NOTIFY_ALERT_RULE_STATUS_UPDATED,
|
||||
NOTIFY_ALERT_RULE_STATUS_UPDATE_FAILED,
|
||||
NOTIFY_TICKSCRIPT_CREATED,
|
||||
NOTIFY_TICKSCRIPT_CREATION_FAILED,
|
||||
NOTIFY_TICKSCRIPT_UPDATED,
|
||||
NOTIFY_TICKSCRIPT_UPDATE_FAILED,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
const loadQuery = query => ({
|
||||
type: 'KAPA_LOAD_QUERY',
|
||||
payload: {
|
||||
|
@ -170,27 +181,21 @@ export const deleteRule = rule => dispatch => {
|
|||
deleteRuleAPI(rule)
|
||||
.then(() => {
|
||||
dispatch(deleteRuleSuccess(rule.id))
|
||||
dispatch(
|
||||
publishNotification('success', `${rule.name} deleted successfully`)
|
||||
)
|
||||
dispatch(notify(NOTIFY_ALERT_RULE_DELETED(rule.name)))
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch(
|
||||
publishNotification('error', `${rule.name} could not be deleted`)
|
||||
)
|
||||
dispatch(notify(NOTIFY_ALERT_RULE_DELETION_FAILED(rule.name)))
|
||||
})
|
||||
}
|
||||
|
||||
export const updateRuleStatus = (rule, status) => dispatch => {
|
||||
updateRuleStatusAPI(rule, status)
|
||||
.then(() => {
|
||||
dispatch(
|
||||
publishNotification('success', `${rule.name} ${status} successfully`)
|
||||
)
|
||||
dispatch(notify(NOTIFY_ALERT_RULE_STATUS_UPDATED(rule.name, status)))
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch(
|
||||
publishNotification('error', `${rule.name} could not be ${status}`)
|
||||
notify(NOTIFY_ALERT_RULE_STATUS_UPDATE_FAILED(rule.name, status))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -198,11 +203,11 @@ export const updateRuleStatus = (rule, status) => dispatch => {
|
|||
export const createTask = (kapacitor, task) => async dispatch => {
|
||||
try {
|
||||
const {data} = await createTaskAJAX(kapacitor, task)
|
||||
dispatch(publishNotification('success', 'TICKscript successfully created'))
|
||||
dispatch(notify(NOTIFY_TICKSCRIPT_CREATED))
|
||||
return data
|
||||
} catch (error) {
|
||||
if (!error) {
|
||||
dispatch(errorThrown('Could not communicate with server'))
|
||||
dispatch(errorThrown(NOTIFY_TICKSCRIPT_CREATION_FAILED))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -218,11 +223,11 @@ export const updateTask = (
|
|||
) => async dispatch => {
|
||||
try {
|
||||
const {data} = await updateTaskAJAX(kapacitor, task, ruleID, sourceID)
|
||||
dispatch(publishNotification('success', 'TICKscript saved'))
|
||||
dispatch(notify(NOTIFY_TICKSCRIPT_UPDATED))
|
||||
return data
|
||||
} catch (error) {
|
||||
if (!error) {
|
||||
dispatch(errorThrown('Could not communicate with server'))
|
||||
dispatch(errorThrown(NOTIFY_TICKSCRIPT_UPDATE_FAILED))
|
||||
return
|
||||
}
|
||||
return error.data
|
||||
|
|
|
@ -4,30 +4,38 @@ import AlertTabs from 'src/kapacitor/components/AlertTabs'
|
|||
|
||||
import {Kapacitor, Source} from 'src/types'
|
||||
|
||||
type FlashMessage = {type: string; text: string}
|
||||
export interface Notification {
|
||||
id?: string
|
||||
type: string
|
||||
icon: string
|
||||
duration: number
|
||||
message: string
|
||||
}
|
||||
|
||||
type NotificationFunc = () => Notification
|
||||
|
||||
interface AlertOutputProps {
|
||||
exists: boolean
|
||||
kapacitor: Kapacitor
|
||||
addFlashMessage: (message: FlashMessage) => void
|
||||
source: Source
|
||||
hash: string
|
||||
notify: (message: Notification | NotificationFunc) => void
|
||||
}
|
||||
|
||||
const AlertOutputs: SFC<AlertOutputProps> = ({
|
||||
exists,
|
||||
kapacitor,
|
||||
addFlashMessage,
|
||||
source,
|
||||
hash,
|
||||
exists,
|
||||
source,
|
||||
kapacitor,
|
||||
notify,
|
||||
}) => {
|
||||
if (exists) {
|
||||
return (
|
||||
<AlertTabs
|
||||
hash={hash}
|
||||
source={source}
|
||||
kapacitor={kapacitor}
|
||||
addFlashMessage={addFlashMessage}
|
||||
hash={hash}
|
||||
notify={notify}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import _ from 'lodash'
|
||||
|
||||
import {Tab, Tabs, TabPanel, TabPanels, TabList} from 'shared/components/Tabs'
|
||||
|
@ -23,6 +24,14 @@ import {
|
|||
VictorOpsConfig,
|
||||
} from './config'
|
||||
|
||||
import {
|
||||
NOTIFY_REFRESH_KAPACITOR_FAILED,
|
||||
NOTIFY_ALERT_ENDPOINT_SAVED,
|
||||
NOTIFY_ALERT_ENDPOINT_SAVE_FAILED,
|
||||
NOTIFY_TEST_ALERT_SENT,
|
||||
NOTIFY_TEST_ALERT_FAILED,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
class AlertTabs extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
@ -48,10 +57,7 @@ class AlertTabs extends Component {
|
|||
this.setState({configSections: sections})
|
||||
} catch (error) {
|
||||
this.setState({configSections: null})
|
||||
this.props.addFlashMessage({
|
||||
type: 'error',
|
||||
text: 'There was an error getting the Kapacitor config',
|
||||
})
|
||||
this.props.notify(NOTIFY_REFRESH_KAPACITOR_FAILED)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,17 +87,11 @@ class AlertTabs extends Component {
|
|||
propsToSend
|
||||
)
|
||||
this.refreshKapacitorConfig(this.props.kapacitor)
|
||||
this.props.addFlashMessage({
|
||||
type: 'success',
|
||||
text: `Alert configuration for ${section} successfully saved.`,
|
||||
})
|
||||
this.props.notify(NOTIFY_ALERT_ENDPOINT_SAVED(section))
|
||||
return true
|
||||
} catch ({data: {error}}) {
|
||||
const errorMsg = _.join(_.drop(_.split(error, ': '), 2), ': ')
|
||||
this.props.addFlashMessage({
|
||||
type: 'error',
|
||||
text: `There was an error saving the alert configuration for ${section}: ${errorMsg}`,
|
||||
})
|
||||
this.props.notify(NOTIFY_ALERT_ENDPOINT_SAVE_FAILED(section, errorMsg))
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -103,21 +103,12 @@ class AlertTabs extends Component {
|
|||
try {
|
||||
const {data} = await testAlertOutput(this.props.kapacitor, section)
|
||||
if (data.success) {
|
||||
this.props.addFlashMessage({
|
||||
type: 'success',
|
||||
text: `Successfully triggered an alert to ${section}. If the alert does not reach its destination, please check your configuration settings.`,
|
||||
})
|
||||
this.props.notify(NOTIFY_TEST_ALERT_SENT(section))
|
||||
} else {
|
||||
this.props.addFlashMessage({
|
||||
type: 'error',
|
||||
text: `There was an error sending an alert to ${section}: ${data.message}`,
|
||||
})
|
||||
this.props.notify(NOTIFY_TEST_ALERT_FAILED(section, data.message))
|
||||
}
|
||||
} catch (error) {
|
||||
this.props.addFlashMessage({
|
||||
type: 'error',
|
||||
text: `There was an error sending an alert to ${section}.`,
|
||||
})
|
||||
this.props.notify(NOTIFY_TEST_ALERT_FAILED(section))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,7 +320,7 @@ AlertTabs.propTypes = {
|
|||
proxy: string.isRequired,
|
||||
}).isRequired,
|
||||
}),
|
||||
addFlashMessage: func.isRequired,
|
||||
notify: func.isRequired,
|
||||
hash: string.isRequired,
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,6 @@ DataSection.propTypes = {
|
|||
query: shape({
|
||||
id: string.isRequired,
|
||||
}).isRequired,
|
||||
addFlashMessage: func,
|
||||
actions: shape({
|
||||
chooseNamespace: func.isRequired,
|
||||
chooseMeasurement: func.isRequired,
|
||||
|
|
|
@ -4,9 +4,18 @@ import AlertOutputs from 'src/kapacitor/components/AlertOutputs'
|
|||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import Input from 'src/kapacitor/components/KapacitorFormInput'
|
||||
|
||||
import {insecureSkipVerifyText} from 'src/shared/copy/tooltipText'
|
||||
import {Kapacitor, Source} from 'src/types'
|
||||
|
||||
type FlashMessage = {type: string; text: string}
|
||||
export interface Notification {
|
||||
id?: string
|
||||
type: string
|
||||
icon: string
|
||||
duration: number
|
||||
message: string
|
||||
}
|
||||
|
||||
type NotificationFunc = () => Notification
|
||||
|
||||
interface Props {
|
||||
kapacitor: Kapacitor
|
||||
|
@ -14,23 +23,25 @@ interface Props {
|
|||
onReset: (e: MouseEvent<HTMLButtonElement>) => void
|
||||
onSubmit: (e: ChangeEvent<HTMLFormElement>) => void
|
||||
onInputChange: (e: ChangeEvent<HTMLInputElement>) => void
|
||||
onCheckboxChange: (e: ChangeEvent<HTMLInputElement>) => void
|
||||
onChangeUrl: (e: ChangeEvent<HTMLInputElement>) => void
|
||||
addFlashMessage: (message: FlashMessage) => void
|
||||
source: Source
|
||||
hash: string
|
||||
notify: (message: Notification | NotificationFunc) => void
|
||||
}
|
||||
|
||||
const KapacitorForm: SFC<Props> = ({
|
||||
onChangeUrl,
|
||||
onReset,
|
||||
kapacitor,
|
||||
kapacitor: {url, name, username, password},
|
||||
kapacitor: {url, name, username, password, insecureSkipVerify},
|
||||
onSubmit,
|
||||
exists,
|
||||
onInputChange,
|
||||
addFlashMessage,
|
||||
onCheckboxChange,
|
||||
source,
|
||||
hash,
|
||||
notify,
|
||||
}) =>
|
||||
<div className="page">
|
||||
<div className="page-header">
|
||||
|
@ -84,6 +95,22 @@ const KapacitorForm: SFC<Props> = ({
|
|||
inputType="password"
|
||||
/>
|
||||
</div>
|
||||
{url.startsWith('https') &&
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="insecureSkipVerifyCheckbox"
|
||||
name="insecureSkipVerify"
|
||||
checked={insecureSkipVerify}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
<label htmlFor="insecureSkipVerifyCheckbox">Unsafe SSL</label>
|
||||
</div>
|
||||
<label className="form-helper">
|
||||
{insecureSkipVerifyText}
|
||||
</label>
|
||||
</div>}
|
||||
<div className="form-group form-group-submit col-xs-12 text-center">
|
||||
<button
|
||||
className="btn btn-default"
|
||||
|
@ -106,15 +133,13 @@ const KapacitorForm: SFC<Props> = ({
|
|||
</div>
|
||||
</div>
|
||||
<div className="col-md-9">
|
||||
{
|
||||
<AlertOutputs
|
||||
exists={exists}
|
||||
kapacitor={kapacitor}
|
||||
addFlashMessage={addFlashMessage}
|
||||
source={source}
|
||||
hash={hash}
|
||||
/>
|
||||
}
|
||||
<AlertOutputs
|
||||
hash={hash}
|
||||
exists={exists}
|
||||
source={source}
|
||||
kapacitor={kapacitor}
|
||||
notify={notify}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import NameSection from 'src/kapacitor/components/NameSection'
|
||||
import ValuesSection from 'src/kapacitor/components/ValuesSection'
|
||||
|
@ -12,6 +14,17 @@ import {createRule, editRule} from 'src/kapacitor/apis'
|
|||
import buildInfluxQLQuery from 'utils/influxql'
|
||||
import {timeRanges} from 'shared/data/timeRanges'
|
||||
import {DEFAULT_RULE_ID} from 'src/kapacitor/constants'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import {
|
||||
NOTIFY_ALERT_RULE_CREATED,
|
||||
NOTIFY_ALERT_RULE_CREATION_FAILED,
|
||||
NOTIFY_ALERT_RULE_UPDATED,
|
||||
NOTIFY_ALERT_RULE_UPDATE_FAILED,
|
||||
NOTIFY_ALERT_RULE_REQUIRES_QUERY,
|
||||
NOTIFY_ALERT_RULE_REQUIRES_CONDITION_VALUE,
|
||||
NOTIFY_ALERT_RULE_DEADMAN_INVALID,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
class KapacitorRule extends Component {
|
||||
constructor(props) {
|
||||
|
@ -27,14 +40,7 @@ class KapacitorRule extends Component {
|
|||
}
|
||||
|
||||
handleCreate = pathname => {
|
||||
const {
|
||||
addFlashMessage,
|
||||
queryConfigs,
|
||||
rule,
|
||||
source,
|
||||
router,
|
||||
kapacitor,
|
||||
} = this.props
|
||||
const {notify, queryConfigs, rule, source, router, kapacitor} = this.props
|
||||
|
||||
const newRule = Object.assign({}, rule, {
|
||||
query: queryConfigs[rule.queryID],
|
||||
|
@ -44,18 +50,15 @@ class KapacitorRule extends Component {
|
|||
createRule(kapacitor, newRule)
|
||||
.then(() => {
|
||||
router.push(pathname || `/sources/${source.id}/alert-rules`)
|
||||
addFlashMessage({type: 'success', text: 'Rule successfully created'})
|
||||
notify(NOTIFY_ALERT_RULE_CREATED)
|
||||
})
|
||||
.catch(() => {
|
||||
addFlashMessage({
|
||||
type: 'error',
|
||||
text: 'There was a problem creating the rule',
|
||||
})
|
||||
notify(NOTIFY_ALERT_RULE_CREATION_FAILED)
|
||||
})
|
||||
}
|
||||
|
||||
handleEdit = pathname => {
|
||||
const {addFlashMessage, queryConfigs, rule, router, source} = this.props
|
||||
const {notify, queryConfigs, rule, router, source} = this.props
|
||||
const updatedRule = Object.assign({}, rule, {
|
||||
query: queryConfigs[rule.queryID],
|
||||
})
|
||||
|
@ -63,16 +66,10 @@ class KapacitorRule extends Component {
|
|||
editRule(updatedRule)
|
||||
.then(() => {
|
||||
router.push(pathname || `/sources/${source.id}/alert-rules`)
|
||||
addFlashMessage({
|
||||
type: 'success',
|
||||
text: `${rule.name} successfully saved!`,
|
||||
})
|
||||
notify(NOTIFY_ALERT_RULE_UPDATED(rule.name))
|
||||
})
|
||||
.catch(e => {
|
||||
addFlashMessage({
|
||||
type: 'error',
|
||||
text: `There was a problem saving ${rule.name}: ${e.data.message}`,
|
||||
})
|
||||
notify(NOTIFY_ALERT_RULE_UPDATE_FAILED(rule.name, e.data.message))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -118,11 +115,11 @@ class KapacitorRule extends Component {
|
|||
}
|
||||
|
||||
if (!buildInfluxQLQuery({}, query)) {
|
||||
return 'Please select a Database, Measurement, and Field'
|
||||
return NOTIFY_ALERT_RULE_REQUIRES_QUERY
|
||||
}
|
||||
|
||||
if (!rule.values.value) {
|
||||
return 'Please enter a value in the Conditions section'
|
||||
return NOTIFY_ALERT_RULE_REQUIRES_CONDITION_VALUE
|
||||
}
|
||||
|
||||
return ''
|
||||
|
@ -131,7 +128,7 @@ class KapacitorRule extends Component {
|
|||
deadmanValidation = () => {
|
||||
const {query} = this.props
|
||||
if (query && (!query.database || !query.measurement)) {
|
||||
return 'Deadman rules require a Database and Measurement'
|
||||
return NOTIFY_ALERT_RULE_DEADMAN_INVALID
|
||||
}
|
||||
|
||||
return ''
|
||||
|
@ -234,7 +231,7 @@ KapacitorRule.propTypes = {
|
|||
queryConfigs: shape({}).isRequired,
|
||||
queryConfigActions: shape({}).isRequired,
|
||||
ruleActions: shape({}).isRequired,
|
||||
addFlashMessage: func.isRequired,
|
||||
notify: func.isRequired,
|
||||
ruleID: string.isRequired,
|
||||
handlersFromConfig: arrayOf(shape({})).isRequired,
|
||||
router: shape({
|
||||
|
@ -244,4 +241,8 @@ KapacitorRule.propTypes = {
|
|||
configLink: string.isRequired,
|
||||
}
|
||||
|
||||
export default KapacitorRule
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(null, mapDispatchToProps)(KapacitorRule)
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import React, {PureComponent, ChangeEvent} from 'react'
|
||||
import {withRouter} from 'react-router'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||
|
||||
import {Source} from 'src/types'
|
||||
|
||||
|
@ -12,10 +16,27 @@ import {
|
|||
|
||||
import KapacitorForm from '../components/KapacitorForm'
|
||||
|
||||
import {
|
||||
NOTIFY_KAPACITOR_CONNECTION_FAILED,
|
||||
NOTIFY_KAPACITOR_NAME_ALREADY_TAKEN,
|
||||
NOTIFY_KAPACITOR_UPDATED,
|
||||
NOTIFY_KAPACITOR_UPDATE_FAILED,
|
||||
NOTIFY_KAPACITOR_CREATED,
|
||||
NOTIFY_KAPACITOR_CREATION_FAILED,
|
||||
} from 'src/shared/copy/notifications'
|
||||
|
||||
export const defaultName = 'My Kapacitor'
|
||||
export const kapacitorPort = '9092'
|
||||
|
||||
type FlashMessage = {type: string; text: string}
|
||||
export interface Notification {
|
||||
id?: string
|
||||
type: string
|
||||
icon: string
|
||||
duration: number
|
||||
message: string
|
||||
}
|
||||
|
||||
export type NotificationFunc = () => Notification
|
||||
|
||||
interface Kapacitor {
|
||||
url: string
|
||||
|
@ -23,6 +44,7 @@ interface Kapacitor {
|
|||
username: string
|
||||
password: string
|
||||
active: boolean
|
||||
insecureSkipVerify: boolean
|
||||
links: {
|
||||
self: string
|
||||
}
|
||||
|
@ -30,7 +52,7 @@ interface Kapacitor {
|
|||
|
||||
interface Props {
|
||||
source: Source
|
||||
addFlashMessage: (message: FlashMessage) => void
|
||||
notify: (message: Notification | NotificationFunc) => void
|
||||
kapacitor: Kapacitor
|
||||
router: {push: (url: string) => void}
|
||||
location: {pathname: string; hash: string}
|
||||
|
@ -46,24 +68,15 @@ export class KapacitorPage extends PureComponent<Props, State> {
|
|||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
kapacitor: {
|
||||
url: this.parseKapacitorURL(),
|
||||
name: defaultName,
|
||||
username: '',
|
||||
password: '',
|
||||
active: false,
|
||||
links: {
|
||||
self: '',
|
||||
},
|
||||
},
|
||||
exists: false,
|
||||
kapacitor: this.defaultKapacitor,
|
||||
exists: false
|
||||
}
|
||||
|
||||
this.handleSubmit = this.handleSubmit.bind(this)
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const {source, params: {id}, addFlashMessage} = this.props
|
||||
const {source, params: {id}, notify} = this.props
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
|
@ -72,15 +85,23 @@ export class KapacitorPage extends PureComponent<Props, State> {
|
|||
const kapacitor = await getKapacitor(source, id)
|
||||
this.setState({kapacitor})
|
||||
await this.checkKapacitorConnection(kapacitor)
|
||||
} catch (err) {
|
||||
console.error('Could not get kapacitor: ', err)
|
||||
addFlashMessage({
|
||||
type: 'error',
|
||||
text: 'Could not connect to Kapacitor',
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Could not get kapacitor: ', error)
|
||||
notify(NOTIFY_KAPACITOR_CONNECTION_FAILED)
|
||||
}
|
||||
}
|
||||
|
||||
handleCheckboxChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const {checked} = e.target
|
||||
|
||||
this.setState({
|
||||
kapacitor: {
|
||||
...this.state.kapacitor,
|
||||
insecureSkipVerify: checked
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const {value, name} = e.target
|
||||
|
||||
|
@ -97,7 +118,7 @@ export class KapacitorPage extends PureComponent<Props, State> {
|
|||
handleSubmit = async e => {
|
||||
e.preventDefault()
|
||||
const {
|
||||
addFlashMessage,
|
||||
notify,
|
||||
source,
|
||||
source: {kapacitors = []},
|
||||
params,
|
||||
|
@ -110,10 +131,7 @@ export class KapacitorPage extends PureComponent<Props, State> {
|
|||
const isNew = !params.id
|
||||
|
||||
if (isNew && isNameTaken) {
|
||||
addFlashMessage({
|
||||
type: 'error',
|
||||
text: `There is already a Kapacitor configuration named "${kapacitor.name}"`,
|
||||
})
|
||||
notify(NOTIFY_KAPACITOR_NAME_ALREADY_TAKEN)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -122,13 +140,10 @@ export class KapacitorPage extends PureComponent<Props, State> {
|
|||
const {data} = await updateKapacitor(kapacitor)
|
||||
this.setState({kapacitor: data})
|
||||
this.checkKapacitorConnection(data)
|
||||
addFlashMessage({type: 'success', text: 'Kapacitor Updated!'})
|
||||
notify(NOTIFY_KAPACITOR_UPDATED)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
addFlashMessage({
|
||||
type: 'error',
|
||||
text: 'There was a problem updating the Kapacitor record',
|
||||
})
|
||||
notify(NOTIFY_KAPACITOR_UPDATE_FAILED)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
|
@ -137,34 +152,31 @@ export class KapacitorPage extends PureComponent<Props, State> {
|
|||
this.setState({kapacitor: data})
|
||||
this.checkKapacitorConnection(data)
|
||||
router.push(`/sources/${source.id}/kapacitors/${data.id}/edit`)
|
||||
addFlashMessage({
|
||||
type: 'success',
|
||||
text: 'Kapacitor Created! Configuring endpoints is optional.',
|
||||
})
|
||||
notify(NOTIFY_KAPACITOR_CREATED)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
addFlashMessage({
|
||||
type: 'error',
|
||||
text: 'There was a problem creating the Kapacitor record',
|
||||
})
|
||||
notify(NOTIFY_KAPACITOR_CREATION_FAILED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleResetToDefaults = e => {
|
||||
e.preventDefault()
|
||||
const defaultState = {
|
||||
this.setState({kapacitor: {...this.defaultKapacitor}})
|
||||
}
|
||||
|
||||
private get defaultKapacitor() {
|
||||
return {
|
||||
url: this.parseKapacitorURL(),
|
||||
name: defaultName,
|
||||
username: '',
|
||||
password: '',
|
||||
active: false,
|
||||
insecureSkipVerify: false,
|
||||
links: {
|
||||
self: '',
|
||||
},
|
||||
}
|
||||
|
||||
this.setState({kapacitor: {...defaultState}})
|
||||
}
|
||||
|
||||
private checkKapacitorConnection = async (kapacitor: Kapacitor) => {
|
||||
|
@ -172,11 +184,9 @@ export class KapacitorPage extends PureComponent<Props, State> {
|
|||
await pingKapacitor(kapacitor)
|
||||
this.setState({exists: true})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
this.setState({exists: false})
|
||||
this.props.addFlashMessage({
|
||||
type: 'error',
|
||||
text: 'Could not connect to Kapacitor. Check settings.',
|
||||
})
|
||||
this.props.notify(NOTIFY_KAPACITOR_CONNECTION_FAILED)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,7 +198,7 @@ export class KapacitorPage extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {source, addFlashMessage, location, params} = this.props
|
||||
const {source, location, params, notify} = this.props
|
||||
const hash = (location && location.hash) || (params && params.hash) || ''
|
||||
const {kapacitor, exists} = this.state
|
||||
|
||||
|
@ -199,13 +209,18 @@ export class KapacitorPage extends PureComponent<Props, State> {
|
|||
exists={exists}
|
||||
kapacitor={kapacitor}
|
||||
onSubmit={this.handleSubmit}
|
||||
addFlashMessage={addFlashMessage}
|
||||
onChangeUrl={this.handleChangeUrl}
|
||||
onReset={this.handleResetToDefaults}
|
||||
onInputChange={this.handleInputChange}
|
||||
notify={notify}
|
||||
onCheckboxChange={this.handleCheckboxChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(KapacitorPage)
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(null, mapDispatchToProps)(withRouter(KapacitorPage))
|
||||
|
|
|
@ -10,6 +10,12 @@ import {getActiveKapacitor, getKapacitorConfig} from 'shared/apis/index'
|
|||
import {DEFAULT_RULE_ID} from 'src/kapacitor/constants'
|
||||
import KapacitorRule from 'src/kapacitor/components/KapacitorRule'
|
||||
import parseHandlersFromConfig from 'src/shared/parsing/parseHandlersFromConfig'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import {
|
||||
NOTIFY_KAPACITOR_CREATION_FAILED,
|
||||
NOTIFY_COULD_NOT_FIND_KAPACITOR,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
class KapacitorRulePage extends Component {
|
||||
constructor(props) {
|
||||
|
@ -22,7 +28,7 @@ class KapacitorRulePage extends Component {
|
|||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const {params, source, ruleActions, addFlashMessage} = this.props
|
||||
const {params, source, ruleActions, notify} = this.props
|
||||
|
||||
if (params.ruleID === 'new') {
|
||||
ruleActions.loadDefaultRule()
|
||||
|
@ -32,10 +38,7 @@ class KapacitorRulePage extends Component {
|
|||
|
||||
const kapacitor = await getActiveKapacitor(this.props.source)
|
||||
if (!kapacitor) {
|
||||
return addFlashMessage({
|
||||
type: 'error',
|
||||
text: "We couldn't find a configured Kapacitor for this source", // eslint-disable-line quotes
|
||||
})
|
||||
return notify(NOTIFY_COULD_NOT_FIND_KAPACITOR)
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -43,10 +46,7 @@ class KapacitorRulePage extends Component {
|
|||
const handlersFromConfig = parseHandlersFromConfig(kapacitorConfig)
|
||||
this.setState({kapacitor, handlersFromConfig})
|
||||
} catch (error) {
|
||||
addFlashMessage({
|
||||
type: 'error',
|
||||
text: 'There was a problem communicating with Kapacitor',
|
||||
})
|
||||
notify(NOTIFY_KAPACITOR_CREATION_FAILED)
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
|
@ -60,7 +60,6 @@ class KapacitorRulePage extends Component {
|
|||
router,
|
||||
ruleActions,
|
||||
queryConfigs,
|
||||
addFlashMessage,
|
||||
queryConfigActions,
|
||||
} = this.props
|
||||
const {handlersFromConfig, kapacitor} = this.state
|
||||
|
@ -79,7 +78,6 @@ class KapacitorRulePage extends Component {
|
|||
queryConfigs={queryConfigs}
|
||||
queryConfigActions={queryConfigActions}
|
||||
ruleActions={ruleActions}
|
||||
addFlashMessage={addFlashMessage}
|
||||
handlersFromConfig={handlersFromConfig}
|
||||
ruleID={params.ruleID}
|
||||
router={router}
|
||||
|
@ -99,7 +97,7 @@ KapacitorRulePage.propTypes = {
|
|||
self: string.isRequired,
|
||||
}),
|
||||
}),
|
||||
addFlashMessage: func,
|
||||
notify: func,
|
||||
rules: shape({}).isRequired,
|
||||
queryConfigs: shape({}).isRequired,
|
||||
ruleActions: shape({
|
||||
|
@ -128,6 +126,7 @@ const mapStateToProps = ({rules, kapacitorQueryConfigs: queryConfigs}) => ({
|
|||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
ruleActions: bindActionCreators(kapacitorRuleActionCreators, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
queryConfigActions: bindActionCreators(
|
||||
kapacitorQueryConfigActionCreators,
|
||||
dispatch
|
||||
|
|
|
@ -78,7 +78,6 @@ KapacitorRulesPage.propTypes = {
|
|||
deleteRule: func.isRequired,
|
||||
updateRuleStatus: func.isRequired,
|
||||
}).isRequired,
|
||||
addFlashMessage: func,
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
|
|
|
@ -12,7 +12,6 @@ export const KapacitorTasksPage = React.createClass({
|
|||
kapacitors: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
addFlashMessage: PropTypes.func,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
|
|
|
@ -9,7 +9,13 @@ import * as kapactiorActionCreators from 'src/kapacitor/actions/view'
|
|||
import * as errorActionCreators from 'shared/actions/errors'
|
||||
import {getActiveKapacitor} from 'src/shared/apis'
|
||||
import {getLogStreamByRuleID, pingKapacitorVersion} from 'src/kapacitor/apis'
|
||||
import {publishNotification} from 'shared/actions/notifications'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
|
||||
import {
|
||||
NOTIFY_TICKSCRIPT_LOGGING_UNAVAILABLE,
|
||||
NOTIFY_TICKSCRIPT_LOGGING_ERROR,
|
||||
NOTIFY_KAPACITOR_NOT_FOUND,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
class TickscriptPage extends Component {
|
||||
constructor(props) {
|
||||
|
@ -44,11 +50,7 @@ class TickscriptPage extends Component {
|
|||
this.setState({
|
||||
areLogsEnabled: false,
|
||||
})
|
||||
notify(
|
||||
'warning',
|
||||
'Could not use logging, requires Kapacitor version 1.4',
|
||||
{once: true}
|
||||
)
|
||||
notify(NOTIFY_TICKSCRIPT_LOGGING_UNAVAILABLE)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -117,7 +119,7 @@ class TickscriptPage extends Component {
|
|||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
notify('error', error)
|
||||
notify(NOTIFY_TICKSCRIPT_LOGGING_ERROR(error))
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
@ -132,9 +134,7 @@ class TickscriptPage extends Component {
|
|||
|
||||
const kapacitor = await getActiveKapacitor(source)
|
||||
if (!kapacitor) {
|
||||
errorActions.errorThrown(
|
||||
'We could not find a configured Kapacitor for this source'
|
||||
)
|
||||
errorActions.errorThrown(NOTIFY_KAPACITOR_NOT_FOUND)
|
||||
}
|
||||
|
||||
if (this._isEditing()) {
|
||||
|
@ -287,7 +287,7 @@ const mapStateToProps = state => {
|
|||
const mapDispatchToProps = dispatch => ({
|
||||
kapacitorActions: bindActionCreators(kapactiorActionCreators, dispatch),
|
||||
errorActions: bindActionCreators(errorActionCreators, dispatch),
|
||||
notify: bindActionCreators(publishNotification, dispatch),
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TickscriptPage)
|
||||
|
|
|
@ -9,8 +9,9 @@ export const loadLocalStorage = errorsQueue => {
|
|||
|
||||
// eslint-disable-next-line no-undef
|
||||
if (state.VERSION && state.VERSION !== VERSION) {
|
||||
const errorText =
|
||||
'New version of Chronograf detected. Local settings cleared.'
|
||||
// eslint-disable-next-line no-undef
|
||||
const version = VERSION ? ` (${VERSION})` : ''
|
||||
const errorText = `Welcome to the latest Chronograf ${version}. Local settings cleared.`
|
||||
|
||||
console.log(errorText) // eslint-disable-line no-console
|
||||
errorsQueue.push(errorText)
|
||||
|
@ -52,7 +53,6 @@ export const saveToLocalStorage = ({
|
|||
timeRange,
|
||||
dataExplorer,
|
||||
dashTimeV1: {ranges},
|
||||
dismissedNotifications,
|
||||
}) => {
|
||||
try {
|
||||
const appPersisted = Object.assign({}, {app: {persisted}})
|
||||
|
@ -67,7 +67,6 @@ export const saveToLocalStorage = ({
|
|||
dataExplorer,
|
||||
VERSION, // eslint-disable-line no-undef
|
||||
dashTimeV1,
|
||||
dismissedNotifications,
|
||||
})
|
||||
)
|
||||
} catch (err) {
|
||||
|
|
|
@ -2,10 +2,10 @@ import {getMe as getMeAJAX, updateMe as updateMeAJAX} from 'shared/apis/auth'
|
|||
|
||||
import {getLinksAsync} from 'shared/actions/links'
|
||||
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
import {notify} from 'shared/actions/notifications'
|
||||
import {errorThrown} from 'shared/actions/errors'
|
||||
|
||||
import {NOTIFICATION_DISMISS_DELAY} from 'shared/constants'
|
||||
import {NOTIFY_USER_SWITCHED_ORGS} from 'shared/copy/notifications'
|
||||
|
||||
export const authExpired = auth => ({
|
||||
type: 'AUTH_EXPIRED',
|
||||
|
@ -92,11 +92,8 @@ export const meChangeOrganizationAsync = (
|
|||
r => r.organization === me.currentOrganization.id
|
||||
)
|
||||
dispatch(
|
||||
publishAutoDismissingNotification(
|
||||
'success',
|
||||
`Now logged in to '${me.currentOrganization
|
||||
.name}' as '${currentRole.name}'`,
|
||||
NOTIFICATION_DISMISS_DELAY
|
||||
notify(
|
||||
NOTIFY_USER_SWITCHED_ORGS(me.currentOrganization.name, currentRole.name)
|
||||
)
|
||||
)
|
||||
dispatch(meChangeOrganizationCompleted())
|
||||
|
|
|
@ -1,31 +1,9 @@
|
|||
export function publishNotification(type, message, options = {once: false}) {
|
||||
// this validator is purely for development purposes. It might make sense to move this to a middleware.
|
||||
const validTypes = ['error', 'success', 'warning']
|
||||
if (!validTypes.includes(type) || message === undefined) {
|
||||
console.error('handleNotification must have a valid type and text') // eslint-disable-line no-console
|
||||
}
|
||||
export const notify = notification => ({
|
||||
type: 'PUBLISH_NOTIFICATION',
|
||||
payload: {notification},
|
||||
})
|
||||
|
||||
return {
|
||||
type: 'NOTIFICATION_RECEIVED',
|
||||
payload: {
|
||||
type,
|
||||
message,
|
||||
once: options.once,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function dismissNotification(type) {
|
||||
return {
|
||||
type: 'NOTIFICATION_DISMISSED',
|
||||
payload: {
|
||||
type,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function dismissAllNotifications() {
|
||||
return {
|
||||
type: 'ALL_NOTIFICATIONS_DISMISSED',
|
||||
}
|
||||
}
|
||||
export const dismissNotification = id => ({
|
||||
type: 'DISMISS_NOTIFICATION',
|
||||
payload: {id},
|
||||
})
|
||||
|
|
|
@ -5,10 +5,15 @@ import {
|
|||
updateKapacitor as updateKapacitorAJAX,
|
||||
deleteKapacitor as deleteKapacitorAJAX,
|
||||
} from 'shared/apis'
|
||||
import {publishNotification} from './notifications'
|
||||
import {notify} from './notifications'
|
||||
import {errorThrown} from 'shared/actions/errors'
|
||||
|
||||
import {HTTP_NOT_FOUND} from 'shared/constants'
|
||||
import {
|
||||
NOTIFY_SERVER_ERROR,
|
||||
NOTIFY_COULD_NOT_RETRIEVE_KAPACITORS,
|
||||
NOTIFY_COULD_NOT_DELETE_KAPACITOR,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
export const loadSources = sources => ({
|
||||
type: 'LOAD_SOURCES',
|
||||
|
@ -71,9 +76,7 @@ export const removeAndLoadSources = source => async dispatch => {
|
|||
const {data: {sources: newSources}} = await getSourcesAJAX()
|
||||
dispatch(loadSources(newSources))
|
||||
} catch (err) {
|
||||
dispatch(
|
||||
publishNotification('error', 'Internal Server Error. Check API Logs')
|
||||
)
|
||||
dispatch(notify(NOTIFY_SERVER_ERROR))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,12 +85,7 @@ export const fetchKapacitorsAsync = source => async dispatch => {
|
|||
const {data} = await getKapacitorsAJAX(source)
|
||||
dispatch(fetchKapacitors(source, data.kapacitors))
|
||||
} catch (err) {
|
||||
dispatch(
|
||||
publishNotification(
|
||||
'error',
|
||||
`Internal Server Error. Could not retrieve kapacitors for source ${source.id}.`
|
||||
)
|
||||
)
|
||||
dispatch(notify(NOTIFY_COULD_NOT_RETRIEVE_KAPACITORS(source.id)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,12 +101,7 @@ export const deleteKapacitorAsync = kapacitor => async dispatch => {
|
|||
await deleteKapacitorAJAX(kapacitor)
|
||||
dispatch(deleteKapacitor(kapacitor))
|
||||
} catch (err) {
|
||||
dispatch(
|
||||
publishNotification(
|
||||
'error',
|
||||
'Internal Server Error. Could not delete Kapacitor config.'
|
||||
)
|
||||
)
|
||||
dispatch(notify(NOTIFY_COULD_NOT_DELETE_KAPACITOR))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,20 +36,31 @@ export function deleteSource(source) {
|
|||
})
|
||||
}
|
||||
|
||||
export function pingKapacitor(kapacitor) {
|
||||
return AJAX({
|
||||
method: 'GET',
|
||||
url: kapacitor.links.ping,
|
||||
})
|
||||
export const pingKapacitor = async kapacitor => {
|
||||
try {
|
||||
const data = await AJAX({
|
||||
method: 'GET',
|
||||
url: kapacitor.links.ping,
|
||||
})
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export function getKapacitor(source, kapacitorID) {
|
||||
return AJAX({
|
||||
url: `${source.links.kapacitors}/${kapacitorID}`,
|
||||
method: 'GET',
|
||||
}).then(({data}) => {
|
||||
export const getKapacitor = async (source, kapacitorID) => {
|
||||
try {
|
||||
const {data} = await AJAX({
|
||||
url: `${source.links.kapacitors}/${kapacitorID}`,
|
||||
method: 'GET',
|
||||
})
|
||||
|
||||
return data
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export const getActiveKapacitor = async source => {
|
||||
|
@ -93,7 +104,7 @@ export const deleteKapacitor = async kapacitor => {
|
|||
|
||||
export function createKapacitor(
|
||||
source,
|
||||
{url, name = 'My Kapacitor', username, password}
|
||||
{url, name = 'My Kapacitor', username, password, insecureSkipVerify}
|
||||
) {
|
||||
return AJAX({
|
||||
url: source.links.kapacitors,
|
||||
|
@ -103,6 +114,7 @@ export function createKapacitor(
|
|||
url,
|
||||
username,
|
||||
password,
|
||||
insecureSkipVerify,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -114,6 +126,7 @@ export function updateKapacitor({
|
|||
username,
|
||||
password,
|
||||
active,
|
||||
insecureSkipVerify,
|
||||
}) {
|
||||
return AJAX({
|
||||
url: links.self,
|
||||
|
@ -124,12 +137,18 @@ export function updateKapacitor({
|
|||
username,
|
||||
password,
|
||||
active,
|
||||
insecureSkipVerify,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const getKapacitorConfig = async kapacitor => {
|
||||
return await kapacitorProxy(kapacitor, 'GET', '/kapacitor/v1/config', '')
|
||||
try {
|
||||
return await kapacitorProxy(kapacitor, 'GET', '/kapacitor/v1/config', '')
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export const getKapacitorConfigSection = (kapacitor, section) => {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {DYGRAPH_CONTAINER_XLABEL_MARGIN} from 'shared/constants'
|
||||
import {NULL_HOVER_TIME} from 'shared/constants/tableGraph'
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import Annotations from 'src/shared/components/Annotations'
|
|||
import Crosshair from 'src/shared/components/Crosshair'
|
||||
|
||||
import getRange, {getStackedRange} from 'shared/parsing/getRangeForDygraph'
|
||||
import {DISPLAY_OPTIONS} from 'src/dashboards/constants'
|
||||
import {AXES_SCALE_OPTIONS} from 'src/dashboards/constants/cellEditor'
|
||||
import {buildDefaultYLabel} from 'shared/presenters'
|
||||
import {numberValueFormatter} from 'src/utils/formatting'
|
||||
import {NULL_HOVER_TIME} from 'src/shared/constants/tableGraph'
|
||||
|
@ -26,7 +26,7 @@ import {
|
|||
hasherino,
|
||||
highlightSeriesOpts,
|
||||
} from 'src/shared/graphs/helpers'
|
||||
const {LINEAR, LOG, BASE_10, BASE_2} = DISPLAY_OPTIONS
|
||||
const {LINEAR, LOG, BASE_10, BASE_2} = AXES_SCALE_OPTIONS
|
||||
|
||||
class Dygraph extends Component {
|
||||
constructor(props) {
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
COLOR_TYPE_MIN,
|
||||
COLOR_TYPE_MAX,
|
||||
MIN_THRESHOLDS,
|
||||
} from 'src/dashboards/constants/gaugeColors'
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
class Gauge extends Component {
|
||||
constructor(props) {
|
||||
|
|
|
@ -3,11 +3,9 @@ import PropTypes from 'prop-types'
|
|||
import lastValues from 'shared/parsing/lastValues'
|
||||
import Gauge from 'shared/components/Gauge'
|
||||
|
||||
import {
|
||||
DEFAULT_GAUGE_COLORS,
|
||||
stringifyColorValues,
|
||||
} from 'src/dashboards/constants/gaugeColors'
|
||||
import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'shared/constants'
|
||||
import {DEFAULT_GAUGE_COLORS} from 'src/shared/constants/thresholds'
|
||||
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
|
||||
import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'src/shared/constants'
|
||||
|
||||
class GaugeChart extends PureComponent {
|
||||
render() {
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
class InputClickToEdit extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
isEditing: null,
|
||||
value: this.props.value,
|
||||
}
|
||||
}
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
isEditing: false,
|
||||
value: this.props.value,
|
||||
})
|
||||
}
|
||||
|
||||
handleInputClick = () => {
|
||||
this.setState({isEditing: true})
|
||||
}
|
||||
|
||||
handleInputBlur = e => {
|
||||
const {onUpdate, value} = this.props
|
||||
|
||||
if (value !== e.target.value) {
|
||||
onUpdate(e.target.value)
|
||||
}
|
||||
|
||||
this.setState({isEditing: false, value: e.target.value})
|
||||
}
|
||||
|
||||
handleKeyDown = e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.handleInputBlur(e)
|
||||
}
|
||||
if (e.key === 'Escape') {
|
||||
this.handleCancel()
|
||||
}
|
||||
}
|
||||
|
||||
handleFocus = e => {
|
||||
e.target.select()
|
||||
}
|
||||
|
||||
render() {
|
||||
const {isEditing, value} = this.state
|
||||
const {
|
||||
wrapperClass: wrapper,
|
||||
disabled,
|
||||
tabIndex,
|
||||
placeholder,
|
||||
appearAsNormalInput,
|
||||
} = this.props
|
||||
|
||||
const wrapperClass = `${wrapper}${appearAsNormalInput
|
||||
? ' input-cte__normal'
|
||||
: ''}`
|
||||
const defaultStyle = value ? 'input-cte' : 'input-cte__empty'
|
||||
|
||||
return disabled
|
||||
? <div className={wrapperClass}>
|
||||
<div className="input-cte__disabled">
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
: <div className={wrapperClass}>
|
||||
{isEditing
|
||||
? <input
|
||||
type="text"
|
||||
className="form-control input-sm provider--input"
|
||||
defaultValue={value}
|
||||
onBlur={this.handleInputBlur}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
autoFocus={true}
|
||||
onFocus={this.handleFocus}
|
||||
ref={r => (this.inputRef = r)}
|
||||
tabIndex={tabIndex}
|
||||
spellCheck={false}
|
||||
/>
|
||||
: <div
|
||||
className={defaultStyle}
|
||||
onClick={this.handleInputClick}
|
||||
onFocus={this.handleInputClick}
|
||||
tabIndex={tabIndex}
|
||||
>
|
||||
{value || placeholder}
|
||||
{appearAsNormalInput || <span className="icon pencil" />}
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
const {func, bool, number, string} = PropTypes
|
||||
|
||||
InputClickToEdit.propTypes = {
|
||||
wrapperClass: string.isRequired,
|
||||
value: string,
|
||||
onUpdate: func.isRequired,
|
||||
disabled: bool,
|
||||
tabIndex: number,
|
||||
placeholder: string,
|
||||
appearAsNormalInput: bool,
|
||||
}
|
||||
|
||||
export default InputClickToEdit
|
|
@ -41,7 +41,7 @@ const Layout = (
|
|||
{
|
||||
host,
|
||||
cell,
|
||||
cell: {h, axes, type, colors, legend},
|
||||
cell: {h, axes, type, colors, legend, tableOptions},
|
||||
source,
|
||||
sources,
|
||||
onZoom,
|
||||
|
@ -79,6 +79,7 @@ const Layout = (
|
|||
inView={cell.inView}
|
||||
axes={axes}
|
||||
type={type}
|
||||
tableOptions={tableOptions}
|
||||
staticLegend={IS_STATIC_LEGEND(legend)}
|
||||
cellHeight={h}
|
||||
onZoom={onZoom}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import classnames from 'classnames'
|
||||
|
||||
import {dismissNotification as dismissNotificationAction} from 'shared/actions/notifications'
|
||||
|
||||
import {NOTIFICATION_TRANSITION} from 'shared/constants/index'
|
||||
|
||||
class Notification extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
opacity: 1,
|
||||
height: 0,
|
||||
dismissed: false,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {notification: {duration}} = this.props
|
||||
|
||||
// Trigger animation in
|
||||
const {height} = this.notificationRef.getBoundingClientRect()
|
||||
this.setState({height})
|
||||
|
||||
if (duration >= 0) {
|
||||
// Automatically dismiss notification after duration prop
|
||||
this.dismissTimer = setTimeout(this.handleDismiss, duration)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.dismissTimer)
|
||||
clearTimeout(this.deleteTimer)
|
||||
}
|
||||
|
||||
handleDismiss = () => {
|
||||
const {notification: {id}, dismissNotification} = this.props
|
||||
|
||||
this.setState({dismissed: true})
|
||||
this.deleteTimer = setTimeout(
|
||||
() => dismissNotification(id),
|
||||
NOTIFICATION_TRANSITION
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {notification: {type, message, icon}} = this.props
|
||||
const {height, dismissed} = this.state
|
||||
|
||||
const notificationContainerClass = classnames('notification-container', {
|
||||
show: !!height,
|
||||
'notification-dismissed': dismissed,
|
||||
})
|
||||
const notificationClass = `notification notification-${type}`
|
||||
const notificationMargin = 4
|
||||
|
||||
return (
|
||||
<div
|
||||
className={notificationContainerClass}
|
||||
style={{height: height + notificationMargin}}
|
||||
>
|
||||
<div
|
||||
className={notificationClass}
|
||||
ref={r => (this.notificationRef = r)}
|
||||
>
|
||||
<span className={`icon ${icon}`} />
|
||||
<div className="notification-message">
|
||||
{message}
|
||||
</div>
|
||||
<button className="notification-close" onClick={this.handleDismiss} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {func, number, shape, string} = PropTypes
|
||||
|
||||
Notification.propTypes = {
|
||||
notification: shape({
|
||||
id: string.isRequired,
|
||||
type: string.isRequired,
|
||||
message: string.isRequired,
|
||||
duration: number.isRequired,
|
||||
icon: string.isRequired,
|
||||
}).isRequired,
|
||||
dismissNotification: func.isRequired,
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
dismissNotification: bindActionCreators(dismissNotificationAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(null, mapDispatchToProps)(Notification)
|
|
@ -1,111 +1,39 @@
|
|||
import React, {Component} from 'react'
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import {withRouter} from 'react-router'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import {getNotificationID} from 'src/shared/reducers/notifications'
|
||||
import Notification from 'shared/components/Notification'
|
||||
|
||||
import {
|
||||
publishNotification as publishNotificationAction,
|
||||
dismissNotification as dismissNotificationAction,
|
||||
dismissAllNotifications as dismissAllNotificationsAction,
|
||||
} from 'shared/actions/notifications'
|
||||
const Notifications = ({notifications, inPresentationMode}) =>
|
||||
<div
|
||||
className={`${inPresentationMode
|
||||
? 'notification-center__presentation-mode'
|
||||
: 'notification-center'}`}
|
||||
>
|
||||
{notifications.map(n => <Notification key={n.id} notification={n} />)}
|
||||
</div>
|
||||
|
||||
class Notifications extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.location.pathname !== this.props.location.pathname) {
|
||||
this.props.dismissAllNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
renderNotification = (type, message) => {
|
||||
const isDismissed = this.props.dismissedNotifications[
|
||||
getNotificationID(message, type)
|
||||
]
|
||||
if (!message || isDismissed) {
|
||||
return null
|
||||
}
|
||||
const cls = classnames('alert', {
|
||||
'alert-danger': type === 'error',
|
||||
'alert-success': type === 'success',
|
||||
'alert-warning': type === 'warning',
|
||||
})
|
||||
return (
|
||||
<div className={cls} role="alert">
|
||||
{message}
|
||||
{this.renderDismiss(type)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
handleDismiss = type => () => this.props.dismissNotification(type)
|
||||
|
||||
renderDismiss = type => {
|
||||
return (
|
||||
<button
|
||||
className="close"
|
||||
data-dismiss="alert"
|
||||
aria-label="Close"
|
||||
onClick={this.handleDismiss(type)}
|
||||
>
|
||||
<span className="icon remove" />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {success, error, warning} = this.props.notifications
|
||||
if (!success && !error && !warning) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flash-messages">
|
||||
{this.renderNotification('success', success)}
|
||||
{this.renderNotification('error', error)}
|
||||
{this.renderNotification('warning', warning)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {func, shape, string} = PropTypes
|
||||
const {arrayOf, bool, number, shape, string} = PropTypes
|
||||
|
||||
Notifications.propTypes = {
|
||||
location: shape({
|
||||
pathname: string.isRequired,
|
||||
}).isRequired,
|
||||
publishNotification: func.isRequired,
|
||||
dismissNotification: func.isRequired,
|
||||
dismissAllNotifications: func.isRequired,
|
||||
notifications: shape({
|
||||
success: string,
|
||||
error: string,
|
||||
warning: string,
|
||||
}),
|
||||
dismissedNotifications: shape({}),
|
||||
notifications: arrayOf(
|
||||
shape({
|
||||
id: string.isRequired,
|
||||
type: string.isRequired,
|
||||
message: string.isRequired,
|
||||
duration: number.isRequired,
|
||||
icon: string,
|
||||
})
|
||||
),
|
||||
inPresentationMode: bool,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({notifications, dismissedNotifications}) => ({
|
||||
const mapStateToProps = ({
|
||||
notifications,
|
||||
dismissedNotifications,
|
||||
app: {ephemeral: {inPresentationMode}},
|
||||
}) => ({
|
||||
notifications,
|
||||
inPresentationMode,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
publishNotification: bindActionCreators(publishNotificationAction, dispatch),
|
||||
dismissNotification: bindActionCreators(dismissNotificationAction, dispatch),
|
||||
dismissAllNotifications: bindActionCreators(
|
||||
dismissAllNotificationsAction,
|
||||
dispatch
|
||||
),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(
|
||||
withRouter(Notifications)
|
||||
)
|
||||
export default connect(mapStateToProps, null)(Notifications)
|
||||
|
|
|
@ -22,6 +22,7 @@ const RefreshingGraph = ({
|
|||
onZoom,
|
||||
cellID,
|
||||
queries,
|
||||
tableOptions,
|
||||
templates,
|
||||
timeRange,
|
||||
cellHeight,
|
||||
|
@ -95,6 +96,7 @@ const RefreshingGraph = ({
|
|||
resizerTopHeight={resizerTopHeight}
|
||||
resizeCoords={resizeCoords}
|
||||
cellID={cellID}
|
||||
tableOptions={tableOptions}
|
||||
hoverTime={hoverTime}
|
||||
onSetHoverTime={onSetHoverTime}
|
||||
inView={inView}
|
||||
|
@ -163,6 +165,7 @@ RefreshingGraph.propTypes = {
|
|||
),
|
||||
cellID: string,
|
||||
inView: bool,
|
||||
tableOptions: shape({}),
|
||||
}
|
||||
|
||||
RefreshingGraph.defaultProps = {
|
||||
|
|
|
@ -5,8 +5,7 @@ import lastValues from 'shared/parsing/lastValues'
|
|||
|
||||
import {SMALL_CELL_HEIGHT} from 'shared/graphs/helpers'
|
||||
import {DYGRAPH_CONTAINER_V_MARGIN} from 'shared/constants'
|
||||
import {SINGLE_STAT_TEXT} from 'src/dashboards/constants/gaugeColors'
|
||||
import {generateSingleStatHexs} from 'shared/constants/colorOperations'
|
||||
import {generateThresholdsListHexs} from 'shared/constants/colorOperations'
|
||||
|
||||
class SingleStat extends PureComponent {
|
||||
render() {
|
||||
|
@ -33,13 +32,11 @@ class SingleStat extends PureComponent {
|
|||
const lastValue = lastValues(data)[1]
|
||||
const precision = 100.0
|
||||
const roundedValue = Math.round(+lastValue * precision) / precision
|
||||
const colorizeText = !!colors.find(color => color.type === SINGLE_STAT_TEXT)
|
||||
|
||||
const {bgColor, textColor} = generateSingleStatHexs(
|
||||
const {bgColor, textColor} = generateThresholdsListHexs(
|
||||
colors,
|
||||
lineGraph,
|
||||
colorizeText,
|
||||
lastValue
|
||||
lastValue,
|
||||
lineGraph
|
||||
)
|
||||
|
||||
const backgroundColor = bgColor
|
||||
|
|
|
@ -3,14 +3,18 @@ import PropTypes from 'prop-types'
|
|||
import _ from 'lodash'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import {MultiGrid} from 'react-virtualized'
|
||||
import moment from 'moment'
|
||||
|
||||
import {timeSeriesToTableGraph} from 'src/utils/timeSeriesToDygraph'
|
||||
import {
|
||||
NULL_COLUMN_INDEX,
|
||||
NULL_ROW_INDEX,
|
||||
NULL_HOVER_TIME,
|
||||
TIME_FORMAT_DEFAULT,
|
||||
TIME_COLUMN_DEFAULT,
|
||||
} from 'src/shared/constants/tableGraph'
|
||||
|
||||
import {MultiGrid} from 'react-virtualized'
|
||||
import {generateThresholdsListHexs} from 'shared/constants/colorOperations'
|
||||
|
||||
const isEmpty = data => data.length <= 1
|
||||
|
||||
|
@ -59,14 +63,24 @@ class TableGraph extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
cellRenderer = ({columnIndex, rowIndex, key, style, parent}) => {
|
||||
cellRenderer = ({columnIndex, rowIndex, key, parent, style}) => {
|
||||
const data = this._data
|
||||
const {hoveredColumnIndex, hoveredRowIndex} = this.state
|
||||
const {colors} = this.props
|
||||
|
||||
const columnCount = _.get(data, ['0', 'length'], 0)
|
||||
const rowCount = data.length
|
||||
const {tableOptions} = this.props
|
||||
const timeFormat = tableOptions
|
||||
? tableOptions.timeFormat
|
||||
: TIME_FORMAT_DEFAULT
|
||||
const columnNames = tableOptions
|
||||
? tableOptions.columnNames
|
||||
: [TIME_COLUMN_DEFAULT]
|
||||
|
||||
const isFixedRow = rowIndex === 0 && columnIndex > 0
|
||||
const isFixedColumn = rowIndex > 0 && columnIndex === 0
|
||||
const isTimeData = isFixedColumn
|
||||
const isFixedCorner = rowIndex === 0 && columnIndex === 0
|
||||
const isLastRow = rowIndex === rowCount - 1
|
||||
const isLastColumn = columnIndex === columnCount - 1
|
||||
|
@ -74,7 +88,22 @@ class TableGraph extends Component {
|
|||
rowIndex === parent.props.scrollToRow ||
|
||||
(rowIndex === hoveredRowIndex && hoveredRowIndex !== 0) ||
|
||||
(columnIndex === hoveredColumnIndex && hoveredColumnIndex !== 0)
|
||||
const dataIsNumerical = _.isNumber([rowIndex][columnIndex])
|
||||
const dataIsNumerical = _.isNumber(data[rowIndex][columnIndex])
|
||||
|
||||
let cellStyle = style
|
||||
|
||||
if (!isFixedRow && !isFixedColumn && !isFixedCorner) {
|
||||
const {bgColor, textColor} = generateThresholdsListHexs(
|
||||
colors,
|
||||
data[rowIndex][columnIndex]
|
||||
)
|
||||
|
||||
cellStyle = {
|
||||
...style,
|
||||
backgroundColor: bgColor,
|
||||
color: textColor,
|
||||
}
|
||||
}
|
||||
|
||||
const cellClass = classnames('table-graph-cell', {
|
||||
'table-graph-cell__fixed-row': isFixedRow,
|
||||
|
@ -86,21 +115,30 @@ class TableGraph extends Component {
|
|||
'table-graph-cell__numerical': dataIsNumerical,
|
||||
})
|
||||
|
||||
const cellData = data[rowIndex][columnIndex]
|
||||
const foundColumn = columnNames.find(
|
||||
column => column.internalName === cellData
|
||||
)
|
||||
const columnName =
|
||||
foundColumn && (foundColumn.displayName || foundColumn.internalName)
|
||||
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
style={style}
|
||||
style={cellStyle}
|
||||
className={cellClass}
|
||||
onMouseOver={this.handleHover(columnIndex, rowIndex)}
|
||||
>
|
||||
{`${data[rowIndex][columnIndex]}`}
|
||||
{isTimeData
|
||||
? `${moment(cellData).format(timeFormat)}`
|
||||
: columnName || `${cellData}`}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {hoveredColumnIndex, hoveredRowIndex} = this.state
|
||||
const {hoverTime} = this.props
|
||||
const {hoverTime, tableOptions, colors} = this.props
|
||||
const data = this._data
|
||||
const columnCount = _.get(data, ['0', 'length'], 0)
|
||||
const rowCount = data.length
|
||||
|
@ -128,11 +166,18 @@ class TableGraph extends Component {
|
|||
fixedRowCount={1}
|
||||
enableFixedColumnScroll={true}
|
||||
enableFixedRowScroll={true}
|
||||
timeFormat={
|
||||
tableOptions ? tableOptions.timeFormat : TIME_FORMAT_DEFAULT
|
||||
}
|
||||
columnNames={
|
||||
tableOptions ? tableOptions.columnNames : [TIME_COLUMN_DEFAULT]
|
||||
}
|
||||
scrollToRow={hoverTimeRow}
|
||||
cellRenderer={this.cellRenderer}
|
||||
hoveredColumnIndex={hoveredColumnIndex}
|
||||
hoveredRowIndex={hoveredRowIndex}
|
||||
hoverTime={hoverTime}
|
||||
colors={colors}
|
||||
/>}
|
||||
</div>
|
||||
)
|
||||
|
@ -144,8 +189,18 @@ const {arrayOf, number, shape, string, func} = PropTypes
|
|||
TableGraph.propTypes = {
|
||||
cellHeight: number,
|
||||
data: arrayOf(shape()),
|
||||
tableOptions: shape({}),
|
||||
hoverTime: string,
|
||||
onSetHoverTime: func,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
}
|
||||
|
||||
export default TableGraph
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import _ from 'lodash'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import Threshold from 'src/dashboards/components/Threshold'
|
||||
import ColorDropdown from 'shared/components/ColorDropdown'
|
||||
|
||||
import {updateThresholdsListColors} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
|
||||
import {
|
||||
THRESHOLD_COLORS,
|
||||
DEFAULT_VALUE_MIN,
|
||||
DEFAULT_VALUE_MAX,
|
||||
MAX_THRESHOLDS,
|
||||
THRESHOLD_TYPE_BASE,
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
const formatColor = color => {
|
||||
const {hex, name} = color
|
||||
return {hex, name}
|
||||
}
|
||||
|
||||
class ThresholdsList extends Component {
|
||||
handleAddThreshold = () => {
|
||||
const {
|
||||
thresholdsListColors,
|
||||
thresholdsListType,
|
||||
handleUpdateThresholdsListColors,
|
||||
onResetFocus,
|
||||
} = this.props
|
||||
|
||||
const randomColor = _.random(0, THRESHOLD_COLORS.length - 1)
|
||||
|
||||
const maxValue = DEFAULT_VALUE_MIN
|
||||
const minValue = DEFAULT_VALUE_MAX
|
||||
|
||||
let randomValue = _.round(_.random(minValue, maxValue, true), 2)
|
||||
|
||||
if (thresholdsListColors.length > 0) {
|
||||
const colorsValues = _.mapValues(thresholdsListColors, 'value')
|
||||
do {
|
||||
randomValue = _.round(_.random(minValue, maxValue, true), 2)
|
||||
} while (_.includes(colorsValues, randomValue))
|
||||
}
|
||||
|
||||
const newThreshold = {
|
||||
type: thresholdsListType,
|
||||
id: uuid.v4(),
|
||||
value: randomValue,
|
||||
hex: THRESHOLD_COLORS[randomColor].hex,
|
||||
name: THRESHOLD_COLORS[randomColor].name,
|
||||
}
|
||||
|
||||
const updatedColors = _.sortBy(
|
||||
[...thresholdsListColors, newThreshold],
|
||||
color => color.value
|
||||
)
|
||||
|
||||
handleUpdateThresholdsListColors(updatedColors)
|
||||
onResetFocus()
|
||||
}
|
||||
|
||||
handleDeleteThreshold = threshold => () => {
|
||||
const {
|
||||
handleUpdateThresholdsListColors,
|
||||
onResetFocus,
|
||||
thresholdsListColors,
|
||||
} = this.props
|
||||
const updatedThresholdsListColors = thresholdsListColors.filter(
|
||||
color => color.id !== threshold.id
|
||||
)
|
||||
const sortedColors = _.sortBy(
|
||||
updatedThresholdsListColors,
|
||||
color => color.value
|
||||
)
|
||||
|
||||
handleUpdateThresholdsListColors(sortedColors)
|
||||
onResetFocus()
|
||||
}
|
||||
|
||||
handleChooseColor = threshold => chosenColor => {
|
||||
const {handleUpdateThresholdsListColors} = this.props
|
||||
|
||||
const thresholdsListColors = this.props.thresholdsListColors.map(
|
||||
color =>
|
||||
color.id === threshold.id
|
||||
? {...color, hex: chosenColor.hex, name: chosenColor.name}
|
||||
: color
|
||||
)
|
||||
|
||||
handleUpdateThresholdsListColors(thresholdsListColors)
|
||||
}
|
||||
|
||||
handleUpdateColorValue = (threshold, value) => {
|
||||
const {handleUpdateThresholdsListColors} = this.props
|
||||
|
||||
const thresholdsListColors = this.props.thresholdsListColors.map(
|
||||
color => (color.id === threshold.id ? {...color, value} : color)
|
||||
)
|
||||
|
||||
handleUpdateThresholdsListColors(thresholdsListColors)
|
||||
}
|
||||
|
||||
handleValidateColorValue = (threshold, targetValue) => {
|
||||
const {thresholdsListColors} = this.props
|
||||
const sortedColors = _.sortBy(thresholdsListColors, color => color.value)
|
||||
|
||||
return !sortedColors.some(color => color.value === targetValue)
|
||||
}
|
||||
|
||||
handleSortColors = () => {
|
||||
const {thresholdsListColors, handleUpdateThresholdsListColors} = this.props
|
||||
const sortedColors = _.sortBy(thresholdsListColors, color => color.value)
|
||||
|
||||
handleUpdateThresholdsListColors(sortedColors)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {thresholdsListColors, showListHeading} = this.props
|
||||
const disableAddThreshold = thresholdsListColors.length > MAX_THRESHOLDS
|
||||
|
||||
const thresholdsListClass = `thresholds-list${showListHeading &&
|
||||
' graph-options-group'}`
|
||||
|
||||
return (
|
||||
<div className={thresholdsListClass}>
|
||||
{showListHeading && <label className="form-label">Thresholds</label>}
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={this.handleAddThreshold}
|
||||
disabled={disableAddThreshold}
|
||||
>
|
||||
<span className="icon plus" /> Add Threshold
|
||||
</button>
|
||||
{thresholdsListColors.map(
|
||||
color =>
|
||||
color.id === THRESHOLD_TYPE_BASE
|
||||
? <div className="threshold-item" key={uuid.v4()}>
|
||||
<div className="threshold-item--label">Base Color</div>
|
||||
<ColorDropdown
|
||||
colors={THRESHOLD_COLORS}
|
||||
selected={formatColor(color)}
|
||||
onChoose={this.handleChooseColor(color)}
|
||||
stretchToFit={true}
|
||||
/>
|
||||
</div>
|
||||
: <Threshold
|
||||
visualizationType="single-stat"
|
||||
threshold={color}
|
||||
key={color.id}
|
||||
onChooseColor={this.handleChooseColor}
|
||||
onValidateColorValue={this.handleValidateColorValue}
|
||||
onUpdateColorValue={this.handleUpdateColorValue}
|
||||
onDeleteThreshold={this.handleDeleteThreshold}
|
||||
onSortColors={this.handleSortColors}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
const {arrayOf, bool, func, number, shape, string} = PropTypes
|
||||
|
||||
ThresholdsList.defaultProps = {
|
||||
showListHeading: false,
|
||||
}
|
||||
ThresholdsList.propTypes = {
|
||||
thresholdsListType: string.isRequired,
|
||||
thresholdsListColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
handleUpdateThresholdsListColors: func.isRequired,
|
||||
onResetFocus: func.isRequired,
|
||||
showListHeading: bool,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({
|
||||
cellEditorOverlay: {thresholdsListType, thresholdsListColors},
|
||||
}) => ({
|
||||
thresholdsListType,
|
||||
thresholdsListColors,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleUpdateThresholdsListColors: bindActionCreators(
|
||||
updateThresholdsListColors,
|
||||
dispatch
|
||||
),
|
||||
})
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ThresholdsList)
|
|
@ -0,0 +1,67 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import {updateThresholdsListType} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
|
||||
import {
|
||||
THRESHOLD_TYPE_TEXT,
|
||||
THRESHOLD_TYPE_BG,
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
class ThresholdsListTypeToggle extends Component {
|
||||
handleToggleThresholdsListType = newType => () => {
|
||||
const {handleUpdateThresholdsListType} = this.props
|
||||
|
||||
handleUpdateThresholdsListType(newType)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {thresholdsListType, containerClass} = this.props
|
||||
|
||||
return (
|
||||
<div className={containerClass}>
|
||||
<label>Threshold Coloring</label>
|
||||
<ul className="nav nav-tablist nav-tablist-sm">
|
||||
<li
|
||||
className={`${thresholdsListType === THRESHOLD_TYPE_BG
|
||||
? 'active'
|
||||
: ''}`}
|
||||
onClick={this.handleToggleThresholdsListType(THRESHOLD_TYPE_BG)}
|
||||
>
|
||||
Background
|
||||
</li>
|
||||
<li
|
||||
className={`${thresholdsListType === THRESHOLD_TYPE_TEXT
|
||||
? 'active'
|
||||
: ''}`}
|
||||
onClick={this.handleToggleThresholdsListType(THRESHOLD_TYPE_TEXT)}
|
||||
>
|
||||
Text
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
const {func, string} = PropTypes
|
||||
|
||||
ThresholdsListTypeToggle.propTypes = {
|
||||
thresholdsListType: string.isRequired,
|
||||
handleUpdateThresholdsListType: func.isRequired,
|
||||
containerClass: string.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({cellEditorOverlay: {thresholdsListType}}) => ({
|
||||
thresholdsListType,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleUpdateThresholdsListType: bindActionCreators(
|
||||
updateThresholdsListType,
|
||||
dispatch
|
||||
),
|
||||
})
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(
|
||||
ThresholdsListTypeToggle
|
||||
)
|
|
@ -1,8 +1,9 @@
|
|||
import _ from 'lodash'
|
||||
import {
|
||||
GAUGE_COLORS,
|
||||
SINGLE_STAT_BASE,
|
||||
} from 'src/dashboards/constants/gaugeColors'
|
||||
THRESHOLD_COLORS,
|
||||
THRESHOLD_TYPE_BASE,
|
||||
THRESHOLD_TYPE_TEXT,
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
const hexToRgb = hex => {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
||||
|
@ -41,20 +42,24 @@ const findNearestCrossedThreshold = (colors, lastValue) => {
|
|||
return nearestCrossedThreshold
|
||||
}
|
||||
|
||||
export const generateSingleStatHexs = (
|
||||
export const stringifyColorValues = colors => {
|
||||
return colors.map(color => ({...color, value: `${color.value}`}))
|
||||
}
|
||||
|
||||
export const generateThresholdsListHexs = (
|
||||
colors,
|
||||
containsLineGraph,
|
||||
colorizeText,
|
||||
lastValue
|
||||
lastValue,
|
||||
containsLineGraph
|
||||
) => {
|
||||
const defaultColoring = {bgColor: null, textColor: GAUGE_COLORS[11].hex}
|
||||
const defaultColoring = {bgColor: null, textColor: THRESHOLD_COLORS[11].hex}
|
||||
const lastValueNumber = Number(lastValue) || 0
|
||||
|
||||
if (!colors.length || !lastValue) {
|
||||
return defaultColoring
|
||||
}
|
||||
|
||||
// baseColor is expected in all cases
|
||||
const baseColor = colors.find(color => (color.id = SINGLE_STAT_BASE)) || {
|
||||
const baseColor = colors.find(color => (color.id = THRESHOLD_TYPE_BASE)) || {
|
||||
hex: defaultColoring.textColor,
|
||||
}
|
||||
|
||||
|
@ -66,17 +71,20 @@ export const generateSingleStatHexs = (
|
|||
}
|
||||
|
||||
// When there is only a base color and it's applied to the text
|
||||
if (colorizeText && colors.length === 1) {
|
||||
const shouldColorizeText = !!colors.find(
|
||||
color => color.type === THRESHOLD_TYPE_TEXT
|
||||
)
|
||||
if (shouldColorizeText && colors.length === 1) {
|
||||
return baseColor
|
||||
? {bgColor: null, textColor: baseColor.hex}
|
||||
: defaultColoring
|
||||
}
|
||||
|
||||
// When there's multiple colors and they're applied to the text
|
||||
if (colorizeText && colors.length > 1) {
|
||||
if (shouldColorizeText && colors.length > 1) {
|
||||
const nearestCrossedThreshold = findNearestCrossedThreshold(
|
||||
colors,
|
||||
lastValue
|
||||
lastValueNumber
|
||||
)
|
||||
const bgColor = null
|
||||
const textColor = nearestCrossedThreshold.hex
|
||||
|
@ -96,7 +104,7 @@ export const generateSingleStatHexs = (
|
|||
if (colors.length > 1) {
|
||||
const nearestCrossedThreshold = findNearestCrossedThreshold(
|
||||
colors,
|
||||
lastValue
|
||||
lastValueNumber
|
||||
)
|
||||
|
||||
const bgColor = nearestCrossedThreshold
|
||||
|
|
|
@ -386,9 +386,6 @@ export const DROPDOWN_MENU_MAX_HEIGHT = 240
|
|||
export const HEARTBEAT_INTERVAL = 10000 // ms
|
||||
|
||||
export const PRESENTATION_MODE_ANIMATION_DELAY = 0 // In milliseconds.
|
||||
export const PRESENTATION_MODE_NOTIFICATION_DELAY = 2000 // In milliseconds.
|
||||
|
||||
export const NOTIFICATION_DISMISS_DELAY = 4000 // in milliseconds
|
||||
|
||||
export const REVERT_STATE_DELAY = 1500 // ms
|
||||
|
||||
|
@ -445,3 +442,8 @@ export const cellSupportsAnnotations = cellType => {
|
|||
]
|
||||
return !!supportedTypes.find(type => type === cellType)
|
||||
}
|
||||
|
||||
export const NOTIFICATION_TRANSITION = 250
|
||||
export const FIVE_SECONDS = 5000
|
||||
export const TEN_SECONDS = 10000
|
||||
export const INFINITE = -1
|
||||
|
|
|
@ -2,3 +2,28 @@ export const NULL_COLUMN_INDEX = -1
|
|||
export const NULL_ROW_INDEX = -1
|
||||
|
||||
export const NULL_HOVER_TIME = '0'
|
||||
|
||||
export const TIME_FORMAT_DEFAULT = 'MM/DD/YYYY HH:mm:ss.ss'
|
||||
export const TIME_FORMAT_CUSTOM = 'Custom'
|
||||
|
||||
export const TIME_COLUMN_DEFAULT = {internalName: 'time', displayName: ''}
|
||||
|
||||
export const FORMAT_OPTIONS = [
|
||||
{text: TIME_FORMAT_DEFAULT},
|
||||
{text: 'MM/DD/YYYY HH:mm'},
|
||||
{text: 'MM/DD/YYYY'},
|
||||
{text: 'h:mm:ss A'},
|
||||
{text: 'h:mm A'},
|
||||
{text: 'MMMM D, YYYY'},
|
||||
{text: 'MMMM D, YYYY h:mm A'},
|
||||
{text: 'dddd, MMMM D, YYYY h:mm A'},
|
||||
{text: TIME_FORMAT_CUSTOM},
|
||||
]
|
||||
|
||||
export const DEFAULT_TABLE_OPTIONS = {
|
||||
timeFormat: 'MM/DD/YYYY HH:mm:ss.ss',
|
||||
verticalTimeAxis: true,
|
||||
sortBy: TIME_COLUMN_DEFAULT,
|
||||
wrapping: 'truncate',
|
||||
columnNames: [TIME_COLUMN_DEFAULT],
|
||||
}
|
||||
|
|
|
@ -9,11 +9,13 @@ export const COLOR_TYPE_MAX = 'max'
|
|||
export const DEFAULT_VALUE_MAX = 100
|
||||
export const COLOR_TYPE_THRESHOLD = 'threshold'
|
||||
|
||||
export const SINGLE_STAT_TEXT = 'text'
|
||||
export const SINGLE_STAT_BG = 'background'
|
||||
export const SINGLE_STAT_BASE = 'base'
|
||||
export const THRESHOLD_TYPE_TEXT = 'text'
|
||||
export const THRESHOLD_TYPE_BG = 'background'
|
||||
export const THRESHOLD_TYPE_BASE = 'base'
|
||||
|
||||
export const GAUGE_COLORS = [
|
||||
export const TIME_FORMAT_DEFAULT = 'MM/DD/YYYY HH:mm:ss.ss'
|
||||
|
||||
export const THRESHOLD_COLORS = [
|
||||
{
|
||||
hex: '#BF3D5E',
|
||||
name: 'ruby',
|
||||
|
@ -95,49 +97,49 @@ export const GAUGE_COLORS = [
|
|||
export const DEFAULT_GAUGE_COLORS = [
|
||||
{
|
||||
type: COLOR_TYPE_MIN,
|
||||
hex: GAUGE_COLORS[11].hex,
|
||||
hex: THRESHOLD_COLORS[11].hex,
|
||||
id: '0',
|
||||
name: GAUGE_COLORS[11].name,
|
||||
name: THRESHOLD_COLORS[11].name,
|
||||
value: DEFAULT_VALUE_MIN,
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_MAX,
|
||||
hex: GAUGE_COLORS[14].hex,
|
||||
hex: THRESHOLD_COLORS[14].hex,
|
||||
id: '1',
|
||||
name: GAUGE_COLORS[14].name,
|
||||
name: THRESHOLD_COLORS[14].name,
|
||||
value: DEFAULT_VALUE_MAX,
|
||||
},
|
||||
]
|
||||
|
||||
export const DEFAULT_SINGLESTAT_COLORS = [
|
||||
export const DEFAULT_THRESHOLDS_LIST_COLORS = [
|
||||
{
|
||||
type: SINGLE_STAT_TEXT,
|
||||
hex: GAUGE_COLORS[11].hex,
|
||||
id: SINGLE_STAT_BASE,
|
||||
name: GAUGE_COLORS[11].name,
|
||||
type: THRESHOLD_TYPE_TEXT,
|
||||
hex: THRESHOLD_COLORS[11].hex,
|
||||
id: THRESHOLD_TYPE_BASE,
|
||||
name: THRESHOLD_COLORS[11].name,
|
||||
value: -999999999999999999,
|
||||
},
|
||||
]
|
||||
|
||||
export const DEFAULT_TABLE_COLORS = [
|
||||
{
|
||||
type: SINGLE_STAT_BG,
|
||||
hex: GAUGE_COLORS[18].hex,
|
||||
id: SINGLE_STAT_BASE,
|
||||
name: GAUGE_COLORS[18].name,
|
||||
type: THRESHOLD_TYPE_BG,
|
||||
hex: THRESHOLD_COLORS[18].hex,
|
||||
id: THRESHOLD_TYPE_BASE,
|
||||
name: THRESHOLD_COLORS[18].name,
|
||||
value: 0,
|
||||
},
|
||||
]
|
||||
|
||||
export const validateSingleStatColors = (colors, type) => {
|
||||
export const validateThresholdsListColors = (colors, type) => {
|
||||
if (!colors || colors.length === 0) {
|
||||
return DEFAULT_SINGLESTAT_COLORS
|
||||
return DEFAULT_THRESHOLDS_LIST_COLORS
|
||||
}
|
||||
|
||||
let containsBaseColor = false
|
||||
|
||||
const formattedColors = colors.map(color => {
|
||||
if (color.id === SINGLE_STAT_BASE) {
|
||||
if (color.id === THRESHOLD_TYPE_BASE) {
|
||||
// Check for existance of base color
|
||||
containsBaseColor = true
|
||||
return {...color, value: Number(color.value), type}
|
||||
|
@ -148,22 +150,22 @@ export const validateSingleStatColors = (colors, type) => {
|
|||
|
||||
const formattedColorsWithBase = [
|
||||
...formattedColors,
|
||||
DEFAULT_SINGLESTAT_COLORS[0],
|
||||
DEFAULT_THRESHOLDS_LIST_COLORS[0],
|
||||
]
|
||||
|
||||
return containsBaseColor ? formattedColors : formattedColorsWithBase
|
||||
}
|
||||
|
||||
export const getSingleStatType = colors => {
|
||||
export const getThresholdsListType = colors => {
|
||||
const type = _.get(colors, ['0', 'type'], false)
|
||||
|
||||
if (type) {
|
||||
if (_.includes([SINGLE_STAT_TEXT, SINGLE_STAT_BG], type)) {
|
||||
if (_.includes([THRESHOLD_TYPE_TEXT, THRESHOLD_TYPE_BG], type)) {
|
||||
return type
|
||||
}
|
||||
}
|
||||
|
||||
return SINGLE_STAT_TEXT
|
||||
return THRESHOLD_TYPE_TEXT
|
||||
}
|
||||
|
||||
export const validateGaugeColors = colors => {
|
||||
|
@ -185,7 +187,3 @@ export const validateGaugeColors = colors => {
|
|||
|
||||
return formattedColors
|
||||
}
|
||||
|
||||
export const stringifyColorValues = colors => {
|
||||
return colors.map(color => ({...color, value: `${color.value}`}))
|
||||
}
|
|
@ -0,0 +1,532 @@
|
|||
// All copy for notifications should be stored here for easy editing
|
||||
// and ensuring stylistic consistency
|
||||
|
||||
import {FIVE_SECONDS, TEN_SECONDS, INFINITE} from 'shared/constants/index'
|
||||
|
||||
const defaultErrorNotification = {
|
||||
type: 'error',
|
||||
icon: 'alert-triangle',
|
||||
duration: TEN_SECONDS,
|
||||
}
|
||||
|
||||
const defaultSuccessNotification = {
|
||||
type: 'success',
|
||||
icon: 'checkmark',
|
||||
duration: FIVE_SECONDS,
|
||||
}
|
||||
|
||||
// Misc Notifications
|
||||
// ----------------------------------------------------------------------------
|
||||
export const NOTIFY_GENERIC_FAIL = 'Could not communicate with server.'
|
||||
|
||||
export const NOTIFY_NEW_VERSION = message => ({
|
||||
type: 'info',
|
||||
icon: 'cubo-uniform',
|
||||
duration: INFINITE,
|
||||
message,
|
||||
})
|
||||
|
||||
export const NOTIFY_ERR_WITH_ALT_TEXT = (type, message) => ({
|
||||
type,
|
||||
icon: 'triangle',
|
||||
duration: TEN_SECONDS,
|
||||
message,
|
||||
})
|
||||
|
||||
export const NOTIFY_PRESENTATION_MODE = {
|
||||
type: 'primary',
|
||||
icon: 'expand-b',
|
||||
duration: 7500,
|
||||
message: 'Press ESC to exit Presentation Mode.',
|
||||
}
|
||||
|
||||
export const NOTIFY_DATA_WRITTEN = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'Data was written successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_SESSION_TIMED_OUT = {
|
||||
type: 'primary',
|
||||
icon: 'triangle',
|
||||
duration: INFINITE,
|
||||
message: 'Your session has timed out. Log in again to continue.',
|
||||
}
|
||||
|
||||
export const NOTIFY_SERVER_ERROR = {
|
||||
...defaultErrorNotification,
|
||||
mesasage: 'Internal Server Error. Check API Logs.',
|
||||
}
|
||||
|
||||
export const NOTIFY_COULD_NOT_RETRIEVE_KAPACITORS = sourceID => ({
|
||||
...defaultErrorNotification,
|
||||
mesasage: `Internal Server Error. Could not retrieve Kapacitor Connections for source ${sourceID}.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_COULD_NOT_DELETE_KAPACITOR = {
|
||||
...defaultErrorNotification,
|
||||
message: 'Internal Server Error. Could not delete Kapacitor Connection.',
|
||||
}
|
||||
|
||||
// Hosts Page Notifications
|
||||
// ----------------------------------------------------------------------------
|
||||
export const NOTIFY_UNABLE_TO_GET_HOSTS = {
|
||||
...defaultErrorNotification,
|
||||
message: 'Unable to get Hosts.',
|
||||
}
|
||||
|
||||
export const NOTIFY_UNABLE_TO_GET_APPS = {
|
||||
...defaultErrorNotification,
|
||||
message: 'Unable to get Apps for Hosts.',
|
||||
}
|
||||
|
||||
// InfluxDB Sources Notifications
|
||||
// ----------------------------------------------------------------------------
|
||||
export const NOTIFY_SOURCE_CREATION_SUCCEEDED = sourceName => ({
|
||||
...defaultSuccessNotification,
|
||||
icon: 'server2',
|
||||
message: `Connected to InfluxDB ${sourceName} successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_SOURCE_CREATION_FAILED = (sourceName, errorMessage) => ({
|
||||
...defaultErrorNotification,
|
||||
icon: 'server2',
|
||||
message: `Unable to connect to InfluxDB ${sourceName}: ${errorMessage}`,
|
||||
})
|
||||
|
||||
export const NOTIFY_SOURCE_UPDATED = sourceName => ({
|
||||
...defaultSuccessNotification,
|
||||
icon: 'server2',
|
||||
message: `Updated InfluxDB ${sourceName} Connection successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_SOURCE_UPDATE_FAILED = (sourceName, errorMessage) => ({
|
||||
...defaultErrorNotification,
|
||||
icon: 'server2',
|
||||
message: `Failed to update InfluxDB ${sourceName} Connection: ${errorMessage}`,
|
||||
})
|
||||
|
||||
export const NOTIFY_SOURCE_DELETED = sourceName => ({
|
||||
...defaultSuccessNotification,
|
||||
icon: 'server2',
|
||||
message: `${sourceName} deleted successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_SOURCE_DELETE_FAILED = sourceName => ({
|
||||
...defaultErrorNotification,
|
||||
icon: 'server2',
|
||||
message: `There was a problem deleting ${sourceName}.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_SOURCE_NO_LONGER_AVAILABLE = sourceName =>
|
||||
`Source ${sourceName} is no longer available. Successfully connected to another source.`
|
||||
|
||||
export const NOTIFY_NO_SOURCES_AVAILABLE = sourceName =>
|
||||
`Unable to connect to source ${sourceName}. No other sources available.`
|
||||
|
||||
export const NOTIFY_UNABLE_TO_RETRIEVE_SOURCES = 'Unable to retrieve sources.'
|
||||
|
||||
export const NOTIFY_UNABLE_TO_CONNECT_SOURCE = sourceName =>
|
||||
`Unable to connect to source ${sourceName}.`
|
||||
|
||||
export const NOTIFY_ERROR_CONNECTING_TO_SOURCE = errorMessage =>
|
||||
`Unable to connect to InfluxDB source: ${errorMessage}`
|
||||
|
||||
// Multitenancy User Notifications
|
||||
// ----------------------------------------------------------------------------
|
||||
export const NOTIFY_USER_REMOVED_FROM_ALL_ORGS = {
|
||||
...defaultErrorNotification,
|
||||
duration: INFINITE,
|
||||
message:
|
||||
'You have been removed from all organizations. Please contact your administrator.',
|
||||
}
|
||||
|
||||
export const NOTIFY_USER_REMOVED_FROM_CURRENT_ORG = {
|
||||
...defaultErrorNotification,
|
||||
duration: INFINITE,
|
||||
message: 'You were removed from your current organization.',
|
||||
}
|
||||
|
||||
export const NOTIFY_ORG_HAS_NO_SOURCES = {
|
||||
...defaultErrorNotification,
|
||||
duration: INFINITE,
|
||||
message: 'Organization has no sources configured.',
|
||||
}
|
||||
|
||||
export const NOTIFY_USER_SWITCHED_ORGS = (orgName, roleName) => ({
|
||||
...defaultSuccessNotification,
|
||||
type: 'primary',
|
||||
message: `Now logged in to '${orgName}' as '${roleName}'.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_ORG_IS_PRIVATE = {
|
||||
...defaultErrorNotification,
|
||||
duration: INFINITE,
|
||||
message:
|
||||
'This organization is private. To gain access, you must be explicitly added by an administrator.',
|
||||
}
|
||||
|
||||
export const NOTIFY_CURRENT_ORG_DELETED = {
|
||||
...defaultErrorNotification,
|
||||
duration: INFINITE,
|
||||
message: 'Your current organization was deleted.',
|
||||
}
|
||||
|
||||
// Chronograf Admin Notifications
|
||||
// ----------------------------------------------------------------------------
|
||||
export const NOTIFY_MAPPING_DELETED = (id, scheme) => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `Mapping ${id}/${scheme} deleted successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_CHRONOGRAF_USER_ADDED_TO_ORG = (user, organization) =>
|
||||
`${user} has been added to ${organization} successfully.`
|
||||
|
||||
export const NOTIFY_CHRONOGRAF_USER_REMOVED_FROM_ORG = (user, organization) =>
|
||||
`${user} has been removed from ${organization} successfully.`
|
||||
|
||||
export const NOTIFY_CHRONOGRAF_USER_UPDATED = message => ({
|
||||
...defaultSuccessNotification,
|
||||
message,
|
||||
})
|
||||
|
||||
export const NOTIFY_CHRONOGRAF_ORG_DELETED = orgName => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `Organization ${orgName} deleted successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_CHRONOGRAF_USER_DELETED = (user, isAbsoluteDelete) => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `${user} has been removed from ${isAbsoluteDelete
|
||||
? 'all organizations and deleted.'
|
||||
: 'the current organization.'}`,
|
||||
})
|
||||
|
||||
export const NOTIFY_CHRONOGRAF_USER_MISSING_NAME_AND_PROVIDER = {
|
||||
...defaultErrorNotification,
|
||||
type: 'warning',
|
||||
message: 'User must have a Name and Provider.',
|
||||
}
|
||||
|
||||
// InfluxDB Admin Notifications
|
||||
// ----------------------------------------------------------------------------
|
||||
export const NOTIFY_DB_USER_CREATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'User created successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_DB_USER_CREATION_FAILED = errorMessage =>
|
||||
`Failed to create User: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_DB_USER_DELETED = userName => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `User "${userName}" deleted successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_DB_USER_DELETION_FAILED = errorMessage =>
|
||||
`Failed to delete User: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_DB_USER_PERMISSIONS_UPDATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'User Permissions updated successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_DB_USER_PERMISSIONS_UPDATE_FAILED = errorMessage =>
|
||||
`Failed to update User Permissions: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_DB_USER_ROLES_UPDATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'User Roles updated successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_DB_USER_ROLES_UPDATE_FAILED = errorMessage =>
|
||||
`Failed to update User Roles: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_DB_USER_PASSWORD_UPDATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'User Password updated successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_DB_USER_PASSWORD_UPDATE_FAILED = errorMessage =>
|
||||
`Failed to update User Password: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_DATABASE_CREATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'Database created successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_DATABASE_CREATION_FAILED = errorMessage =>
|
||||
`Failed to create Database: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_DATABASE_DELETED = databaseName => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `Database "${databaseName}" deleted successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_DATABASE_DELETION_FAILED = errorMessage =>
|
||||
`Failed to delete Database: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_ROLE_CREATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'Role created successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_ROLE_CREATION_FAILED = errorMessage =>
|
||||
`Failed to create Role: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_ROLE_DELETED = roleName => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `Role "${roleName}" deleted successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_ROLE_DELETION_FAILED = errorMessage =>
|
||||
`Failed to delete Role: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_ROLE_USERS_UPDATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'Role Users updated successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_ROLE_USERS_UPDATE_FAILED = errorMessage =>
|
||||
`Failed to update Role Users: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_ROLE_PERMISSIONS_UPDATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'Role Permissions updated successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_ROLE_PERMISSIONS_UPDATE_FAILED = errorMessage =>
|
||||
`Failed to update Role Permissions: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_RETENTION_POLICY_CREATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'Retention Policy created successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_RETENTION_POLICY_CREATION_FAILED = errorMessage =>
|
||||
`Failed to create Retention Policy: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_RETENTION_POLICY_DELETED = rpName => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `Retention Policy "${rpName}" deleted successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_RETENTION_POLICY_DELETION_FAILED = errorMessage =>
|
||||
`Failed to delete Retention Policy: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_RETENTION_POLICY_UPDATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'Retention Policy updated successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_RETENTION_POLICY_UPDATE_FAILED = errorMessage =>
|
||||
`Failed to update Retention Policy: ${errorMessage}`
|
||||
|
||||
export const NOTIFY_QUERIES_ERROR = errorMessage => ({
|
||||
...defaultErrorNotification,
|
||||
errorMessage,
|
||||
})
|
||||
|
||||
export const NOTIFY_RETENTION_POLICY_CANT_HAVE_EMPTY_FIELDS = {
|
||||
...defaultErrorNotification,
|
||||
message: 'Fields cannot be empty.',
|
||||
}
|
||||
|
||||
export const NOTIFY_DATABASE_DELETE_CONFIRMATION_REQUIRED = databaseName => ({
|
||||
...defaultErrorNotification,
|
||||
message: `Type "DELETE ${databaseName}" to confirm.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_DB_USER_NAME_PASSWORD_INVALID = {
|
||||
...defaultErrorNotification,
|
||||
message: 'Username and/or Password too short.',
|
||||
}
|
||||
|
||||
export const NOTIFY_ROLE_NAME_INVALID = {
|
||||
...defaultErrorNotification,
|
||||
message: 'Role name is too short.',
|
||||
}
|
||||
|
||||
export const NOTIFY_DATABASE_NAME_INVALID = {
|
||||
...defaultErrorNotification,
|
||||
message: 'Database name cannot be blank.',
|
||||
}
|
||||
|
||||
export const NOTIFY_DATABASE_NAME_ALREADY_EXISTS = {
|
||||
...defaultErrorNotification,
|
||||
message: 'A Database by this name already exists.',
|
||||
}
|
||||
|
||||
// Dashboard Notifications
|
||||
// ----------------------------------------------------------------------------
|
||||
export const NOTIFY_TEMP_VAR_ALREADY_EXISTS = tempVarName => ({
|
||||
...defaultErrorNotification,
|
||||
icon: 'cube',
|
||||
message: `Variable '${tempVarName}' already exists. Please enter a new value.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_DASHBOARD_NOT_FOUND = dashboardID => ({
|
||||
...defaultErrorNotification,
|
||||
icon: 'dash-h',
|
||||
message: `Dashboard ${dashboardID} could not be found`,
|
||||
})
|
||||
|
||||
export const NOTIFY_DASHBOARD_DELETED = name => ({
|
||||
...defaultSuccessNotification,
|
||||
icon: 'dash-h',
|
||||
message: `Dashboard ${name} deleted successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_DASHBOARD_DELETE_FAILED = (name, errorMessage) =>
|
||||
`Failed to delete Dashboard ${name}: ${errorMessage}.`
|
||||
|
||||
// Rule Builder Notifications
|
||||
// ----------------------------------------------------------------------------
|
||||
export const NOTIFY_ALERT_RULE_CREATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'Alert Rule created successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_ALERT_RULE_CREATION_FAILED = {
|
||||
...defaultErrorNotification,
|
||||
message: 'Alert Rule could not be created.',
|
||||
}
|
||||
|
||||
export const NOTIFY_ALERT_RULE_UPDATED = ruleName => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `${ruleName} saved successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_ALERT_RULE_UPDATE_FAILED = (ruleName, errorMessage) => ({
|
||||
...defaultErrorNotification,
|
||||
message: `There was a problem saving ${ruleName}: ${errorMessage}`,
|
||||
})
|
||||
|
||||
export const NOTIFY_ALERT_RULE_DELETED = ruleName => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `${ruleName} deleted successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_ALERT_RULE_DELETION_FAILED = ruleName => ({
|
||||
...defaultErrorNotification,
|
||||
message: `${ruleName} could not be deleted.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_ALERT_RULE_STATUS_UPDATED = (ruleName, updatedStatus) => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `${ruleName} ${updatedStatus} successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_ALERT_RULE_STATUS_UPDATE_FAILED = (
|
||||
ruleName,
|
||||
updatedStatus
|
||||
) => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `${ruleName} could not be ${updatedStatus}.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_ALERT_RULE_REQUIRES_QUERY =
|
||||
'Please select a Database, Measurement, and Field.'
|
||||
|
||||
export const NOTIFY_ALERT_RULE_REQUIRES_CONDITION_VALUE =
|
||||
'Please enter a value in the Conditions section.'
|
||||
|
||||
export const NOTIFY_ALERT_RULE_DEADMAN_INVALID =
|
||||
'Deadman rules require a Database and Measurement.'
|
||||
|
||||
// Kapacitor Configuration Notifications
|
||||
// ----------------------------------------------------------------------------
|
||||
export const NOTIFY_KAPACITOR_NAME_ALREADY_TAKEN = kapacitorName => ({
|
||||
...defaultErrorNotification,
|
||||
message: `There is already a Kapacitor Connection named "${kapacitorName}".`,
|
||||
})
|
||||
|
||||
export const NOTIFY_COULD_NOT_FIND_KAPACITOR = {
|
||||
...defaultErrorNotification,
|
||||
message: 'We could not find a Kapacitor configuration for this source.',
|
||||
}
|
||||
|
||||
export const NOTIFY_REFRESH_KAPACITOR_FAILED = {
|
||||
...defaultErrorNotification,
|
||||
message: 'There was an error getting the Kapacitor configuration.',
|
||||
}
|
||||
|
||||
export const NOTIFY_ALERT_ENDPOINT_SAVED = endpoint => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `Alert configuration for ${endpoint} saved successfully.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_ALERT_ENDPOINT_SAVE_FAILED = (endpoint, errorMessage) => ({
|
||||
...defaultErrorNotification,
|
||||
message: `There was an error saving the alert configuration for ${endpoint}: ${errorMessage}`,
|
||||
})
|
||||
|
||||
export const NOTIFY_TEST_ALERT_SENT = endpoint => ({
|
||||
...defaultSuccessNotification,
|
||||
duration: TEN_SECONDS,
|
||||
message: `Test Alert sent to ${endpoint}. If the Alert does not reach its destination, please check your endpoint configuration settings.`,
|
||||
})
|
||||
|
||||
export const NOTIFY_TEST_ALERT_FAILED = (endpoint, errorMessage) => ({
|
||||
...defaultErrorNotification,
|
||||
message: `There was an error sending a Test Alert to ${endpoint}${errorMessage
|
||||
? `: ${errorMessage}`
|
||||
: '.'}`,
|
||||
})
|
||||
|
||||
export const NOTIFY_KAPACITOR_CONNECTION_FAILED = {
|
||||
...defaultErrorNotification,
|
||||
message:
|
||||
'Could not connect to Kapacitor. Check your connection settings in the Configuration page.',
|
||||
}
|
||||
|
||||
export const NOTIFY_KAPACITOR_CREATED = {
|
||||
...defaultSuccessNotification,
|
||||
message:
|
||||
'Connected to Kapacitor successfully! Configuring endpoints is optional.',
|
||||
}
|
||||
|
||||
export const NOTIFY_KAPACITOR_CREATION_FAILED = {
|
||||
...defaultErrorNotification,
|
||||
message: 'There was a problem connecting to Kapacitor.',
|
||||
}
|
||||
|
||||
export const NOTIFY_KAPACITOR_UPDATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'Kapacitor Connection updated successfully.',
|
||||
}
|
||||
|
||||
export const NOTIFY_KAPACITOR_UPDATE_FAILED = {
|
||||
...defaultErrorNotification,
|
||||
message: 'There was a problem updating the Kapacitor Connection.',
|
||||
}
|
||||
|
||||
// TICKscript Notifications
|
||||
// ----------------------------------------------------------------------------
|
||||
export const NOTIFY_TICKSCRIPT_CREATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'TICKscript successfully created.',
|
||||
}
|
||||
|
||||
export const NOTIFY_TICKSCRIPT_CREATION_FAILED = 'Failed to create TICKscript.'
|
||||
|
||||
export const NOTIFY_TICKSCRIPT_UPDATED = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'TICKscript successfully updated.',
|
||||
}
|
||||
|
||||
export const NOTIFY_TICKSCRIPT_UPDATE_FAILED = 'Failed to update TICKscript.'
|
||||
|
||||
export const NOTIFY_TICKSCRIPT_LOGGING_UNAVAILABLE = {
|
||||
type: 'warning',
|
||||
icon: 'alert-triangle',
|
||||
duration: INFINITE,
|
||||
message: 'Kapacitor version 1.4 required to view TICKscript logs',
|
||||
}
|
||||
|
||||
export const NOTIFY_TICKSCRIPT_LOGGING_ERROR = message => ({
|
||||
...defaultErrorNotification,
|
||||
message,
|
||||
})
|
||||
|
||||
export const NOTIFY_KAPACITOR_NOT_FOUND =
|
||||
'We could not find a Kapacitor configuration for this source.'
|
|
@ -1,34 +1,8 @@
|
|||
import {
|
||||
publishNotification,
|
||||
dismissNotification,
|
||||
} from 'shared/actions/notifications'
|
||||
import {notify} from 'shared/actions/notifications'
|
||||
import {delayEnablePresentationMode} from 'shared/actions/app'
|
||||
|
||||
import {PRESENTATION_MODE_NOTIFICATION_DELAY} from 'shared/constants'
|
||||
import {NOTIFICATION_DISMISS_DELAY} from 'shared/constants'
|
||||
|
||||
export function delayDismissNotification(type, delay) {
|
||||
return dispatch => {
|
||||
setTimeout(() => dispatch(dismissNotification(type)), delay)
|
||||
}
|
||||
}
|
||||
|
||||
export const publishAutoDismissingNotification = (
|
||||
type,
|
||||
message,
|
||||
delay = NOTIFICATION_DISMISS_DELAY
|
||||
) => dispatch => {
|
||||
dispatch(publishNotification(type, message))
|
||||
dispatch(delayDismissNotification(type, delay))
|
||||
}
|
||||
import {NOTIFY_PRESENTATION_MODE} from 'shared/copy/notifications'
|
||||
|
||||
export const presentationButtonDispatcher = dispatch => () => {
|
||||
dispatch(delayEnablePresentationMode())
|
||||
dispatch(
|
||||
publishAutoDismissingNotification(
|
||||
'success',
|
||||
'Press ESC to disable presentation mode.',
|
||||
PRESENTATION_MODE_NOTIFICATION_DELAY
|
||||
)
|
||||
)
|
||||
dispatch(notify(NOTIFY_PRESENTATION_MODE))
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue