From 46bd722667e02d6c3568a81f64052e280c13e860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 7 Nov 2021 00:19:16 +0100 Subject: [PATCH] Initial support for timeseries panels --- panel.go | 140 ++++++++++++++++++++++++++++++++++++++++++++++---- panel_test.go | 25 +++++++++ 2 files changed, 154 insertions(+), 11 deletions(-) diff --git a/panel.go b/panel.go index 0c5c93c7..4249b11b 100644 --- a/panel.go +++ b/panel.go @@ -39,6 +39,7 @@ const ( RowType BarGaugeType HeatmapType + TimeseriesType ) const MixedSource = "-- Mixed --" @@ -60,6 +61,7 @@ type ( *AlertlistPanel *BarGaugePanel *HeatmapPanel + *TimeseriesPanel *CustomPanel } panelType int8 @@ -164,17 +166,7 @@ type ( FieldConfig *FieldConfig `json:"fieldConfig,omitempty"` } FieldConfig struct { - Defaults struct { - Unit string `json:"unit"` - Threshold struct { - Mode string `json:"mode"` - Steps []struct { - Color string `json:"color"` - Value string `json:"value"` - } `json:"steps"` - } `json:"threshold"` - Links []Link `json:"links,omitempty"` - } `json:"defaults"` + Defaults FieldConfigDefaults `json:"defaults"` } Options struct { Orientation string `json:"orientation"` @@ -361,6 +353,80 @@ type ( YBucketNumber *float64 `json:"yBucketNumber"` YBucketSize *float64 `json:"yBucketSize"` } + TimeseriesPanel struct { + Targets []Target `json:"targets,omitempty"` + Options TimeseriesOptions `json:"options"` + FieldConfig FieldConfig `json:"fieldConfig"` + } + TimeseriesOptions struct { + Legend TimeseriesLegendOptions `json:"legend,omitempty"` + Tooltip TimeseriesTooltipOptions `json:"tooltip,omitempty"` + } + TimeseriesLegendOptions struct { + Calcs []string `json:"calcs"` + DisplayMode string `json:"displayMode"` + Placement string `json:"placement"` + } + TimeseriesTooltipOptions struct { + Mode string `json:"mode"` + } + FieldConfigDefaults struct { + Unit string `json:"unit"` + Decimals *int `json:"decimals,omitempty"` + Min *int `json:"min,omitempty"` + Max *int `json:"max,omitempty"` + Color FieldConfigColor `json:"color"` + Thresholds Thresholds `json:"thresholds"` + Custom FieldConfigCustom `json:"custom"` + Links []Link `json:"links,omitempty"` + } + FieldConfigCustom struct { + AxisLabel string `json:"axisLabel,omitempty"` + AxisPlacement string `json:"axisPlacement"` + AxisSoftMin *int `json:"axisSoftMin,omitempty"` + AxisSoftMax *int `json:"axisSoftMax,omitempty"` + BarAlignment int `json:"barAlignment"` + DrawStyle string `json:"drawStyle"` + FillOpacity int `json:"fillOpacity"` + GradientMode string `json:"gradientMode"` + LineInterpolation string `json:"lineInterpolation"` + LineWidth int `json:"lineWidth"` + PointSize int `json:"pointSize"` + ShowPoints string `json:"showPoints"` + SpanNulls bool `json:"spanNulls"` + HideFrom struct { + Legend bool `json:"legend"` + Tooltip bool `json:"tooltip"` + Viz bool `json:"viz"` + } `json:"hideFrom"` + LineStyle struct { + Fill string `json:"fill"` + } `json:"lineStyle"` + ScaleDistribution struct { + Type string `json:"type"` + Log int `json:"log,omitempty"` + } `json:"scaleDistribution"` + Stacking struct { + Group string `json:"group"` + Mode string `json:"mode"` + } `json:"stacking"` + ThresholdsStyle struct { + Mode string `json:"mode"` + } `json:"thresholdsStyle"` + } + Thresholds struct { + Mode string `json:"mode"` + Steps []ThresholdStep `json:"steps"` + } + ThresholdStep struct { + Color string `json:"color"` + Value *int `json:"value"` + } + FieldConfigColor struct { + Mode string `json:"mode"` + FixedColor string `json:"fixedColor,omitempty"` + SeriesBy string `json:"seriesBy,omitempty"` + } CustomPanel map[string]interface{} ) @@ -641,6 +707,34 @@ func NewGraph(title string) *Panel { }} } +// NewTimeseries initializes panel with a timeseries panel. +func NewTimeseries(title string) *Panel { + if title == "" { + title = "Panel Title" + } + + return &Panel{ + CommonPanel: CommonPanel{ + OfType: TimeseriesType, + Title: title, + Type: "timeseries", + Span: 12, + IsNew: true, + }, + TimeseriesPanel: &TimeseriesPanel{ + FieldConfig: FieldConfig{ + Defaults: FieldConfigDefaults{ + Color: FieldConfigColor{ + Mode: "palette-classic", + FixedColor: "green", + SeriesBy: "last", + }, + }, + }, + }, + } +} + // NewTable initializes panel with a table panel. func NewTable(title string) *Panel { if title == "" { @@ -782,6 +876,8 @@ func (p *Panel) ResetTargets() { p.BarGaugePanel.Targets = nil case HeatmapType: p.HeatmapPanel.Targets = nil + case TimeseriesType: + p.TimeseriesPanel.Targets = nil } } @@ -801,6 +897,8 @@ func (p *Panel) AddTarget(t *Target) { p.TablePanel.Targets = append(p.TablePanel.Targets, *t) case HeatmapType: p.HeatmapPanel.Targets = append(p.HeatmapPanel.Targets, *t) + case TimeseriesType: + p.TimeseriesPanel.Targets = append(p.TimeseriesPanel.Targets, *t) } // TODO check for existing refID } @@ -828,6 +926,8 @@ func (p *Panel) SetTarget(t *Target) { setTarget(t, &p.TablePanel.Targets) case HeatmapType: setTarget(t, &p.HeatmapPanel.Targets) + case TimeseriesType: + setTarget(t, &p.TimeseriesPanel.Targets) } } @@ -859,6 +959,8 @@ func (p *Panel) RepeatDatasourcesForEachTarget(dsNames ...string) { repeatDS(dsNames, &p.TablePanel.Targets) case HeatmapType: repeatDS(dsNames, &p.HeatmapPanel.Targets) + case TimeseriesType: + repeatDS(dsNames, &p.TimeseriesPanel.Targets) } } @@ -893,6 +995,8 @@ func (p *Panel) RepeatTargetsForDatasources(dsNames ...string) { repeatTarget(dsNames, &p.TablePanel.Targets) case HeatmapType: repeatTarget(dsNames, &p.HeatmapPanel.Targets) + case TimeseriesType: + repeatTarget(dsNames, &p.TimeseriesPanel.Targets) } } @@ -912,6 +1016,8 @@ func (p *Panel) GetTargets() *[]Target { return &p.BarGaugePanel.Targets case HeatmapType: return &p.HeatmapPanel.Targets + case TimeseriesType: + return &p.TimeseriesPanel.Targets default: return nil } @@ -975,6 +1081,12 @@ func (p *Panel) UnmarshalJSON(b []byte) (err error) { if err = json.Unmarshal(b, &heatmap); err == nil { p.HeatmapPanel = &heatmap } + case "timeseries": + var timeseries TimeseriesPanel + p.OfType = TimeseriesType + if err = json.Unmarshal(b, ×eries); err == nil { + p.TimeseriesPanel = ×eries + } case "row": var rowpanel RowPanel p.OfType = RowType @@ -1060,6 +1172,12 @@ func (p *Panel) MarshalJSON() ([]byte, error) { HeatmapPanel }{p.CommonPanel, *p.HeatmapPanel} return json.Marshal(outHeatmap) + case TimeseriesType: + var outTimeseries = struct { + CommonPanel + TimeseriesPanel + }{p.CommonPanel, *p.TimeseriesPanel} + return json.Marshal(outTimeseries) case CustomType: var outCustom = customPanelOutput{ p.CommonPanel, diff --git a/panel_test.go b/panel_test.go index 1d2b7004..4f70246e 100644 --- a/panel_test.go +++ b/panel_test.go @@ -263,6 +263,31 @@ func TestNewGraph(t *testing.T) { } } +func TestNewTimeseries(t *testing.T) { + var title = "Sample Title" + + timeseries := sdk.NewTimeseries(title) + + if timeseries.TimeseriesPanel == nil { + t.Error("should be not nil") + } + if timeseries.GraphPanel != nil { + t.Error("should be nil") + } + if timeseries.TextPanel != nil { + t.Error("should be nil") + } + if timeseries.DashlistPanel != nil { + t.Error("should be nil") + } + if timeseries.SinglestatPanel != nil { + t.Error("should be nil") + } + if timeseries.Title != title { + t.Errorf("title should be %s but %s", title, timeseries.Title) + } +} + func TestGraph_AddTarget(t *testing.T) { var target = sdk.Target{ RefID: "A",