influxdb/view.go

507 lines
15 KiB
Go

package platform
import (
"context"
"encoding/json"
"fmt"
)
// ErrViewNotFound is the error msg for a missing View.
const ErrViewNotFound = "view not found"
// ops for view.
const (
OpFindViewByID = "FindViewByID"
OpFindViews = "FindViews"
OpCreateView = "CreateView"
OpUpdateView = "UpdateView"
OpDeleteView = "DeleteView"
)
// NOTE: This service has been DEPRECATED and should be removed. Views are now
// resources that are nested beneath dashboards.
//
// ViewService represents a service for managing View data.
type ViewService interface {
// FindViewByID returns a single View by ID.
FindViewByID(ctx context.Context, id ID) (*View, error)
// FindViews returns a list of Views that match filter and the total count of matching Views.
// Additional options provide pagination & sorting.
FindViews(ctx context.Context, filter ViewFilter) ([]*View, int, error)
// CreateView creates a new View and sets b.ID with the new identifier.
CreateView(ctx context.Context, b *View) error
// UpdateView updates a single View with changeset.
// Returns the new View state after update.
UpdateView(ctx context.Context, id ID, upd ViewUpdate) (*View, error)
// DeleteView removes a View by ID.
DeleteView(ctx context.Context, id ID) error
}
// ViewUpdate is a struct for updating Views.
type ViewUpdate struct {
ViewContentsUpdate
Properties ViewProperties
}
// Valid validates the update struct. It expects minimal values to be set.
func (u ViewUpdate) Valid() *Error {
_, ok := u.Properties.(EmptyViewProperties)
if u.Name == nil && ok {
return &Error{
Code: EInvalid,
Msg: "expected at least one attribute to be updated",
}
}
return nil
}
// Apply updates a view with the view updates properties.
func (u ViewUpdate) Apply(v *View) error {
if err := u.Valid(); err != nil {
return err
}
if u.Name != nil {
v.Name = *u.Name
}
if u.Properties != nil {
v.Properties = u.Properties
}
return nil
}
// ViewContentsUpdate is a struct for updating the non properties content of a View.
type ViewContentsUpdate struct {
Name *string `json:"name"`
}
// ViewFilter represents a set of filter that restrict the returned results.
type ViewFilter struct {
ID *ID
Types []string
}
// View holds positional and visual information for a View.
type View struct {
ViewContents
Properties ViewProperties
}
// ViewContents is the id and name of a specific view.
type ViewContents struct {
ID ID `json:"id,omitempty"`
Name string `json:"name"`
}
// ViewProperties is used to mark other structures as conforming to a View.
type ViewProperties interface {
viewProperties()
GetType() string
}
// EmptyViewProperties is visualization that has no values
type EmptyViewProperties struct{}
func (v EmptyViewProperties) viewProperties() {}
func (v EmptyViewProperties) GetType() string { return "" }
// UnmarshalViewPropertiesJSON unmarshals JSON bytes into a ViewProperties.
func UnmarshalViewPropertiesJSON(b []byte) (ViewProperties, error) {
var v struct {
B json.RawMessage `json:"properties"`
}
if err := json.Unmarshal(b, &v); err != nil {
return nil, err
}
if len(v.B) == 0 {
// Then there wasn't any visualization field, so there's no need unmarshal it
return EmptyViewProperties{}, nil
}
var t struct {
Shape string `json:"shape"`
Type string `json:"type"`
}
if err := json.Unmarshal(v.B, &t); err != nil {
return nil, err
}
var vis ViewProperties
switch t.Shape {
case "chronograf-v2":
switch t.Type {
case "xy":
var xyv XYViewProperties
if err := json.Unmarshal(v.B, &xyv); err != nil {
return nil, err
}
vis = xyv
case "single-stat":
var ssv SingleStatViewProperties
if err := json.Unmarshal(v.B, &ssv); err != nil {
return nil, err
}
vis = ssv
case "gauge":
var gv GaugeViewProperties
if err := json.Unmarshal(v.B, &gv); err != nil {
return nil, err
}
vis = gv
case "table":
var tv TableViewProperties
if err := json.Unmarshal(v.B, &tv); err != nil {
return nil, err
}
vis = tv
case "markdown":
var mv MarkdownViewProperties
if err := json.Unmarshal(v.B, &mv); err != nil {
return nil, err
}
vis = mv
case "log-viewer": // happens in log viewer stays in log viewer.
var lv LogViewProperties
if err := json.Unmarshal(v.B, &lv); err != nil {
return nil, err
}
vis = lv
case "line-plus-single-stat":
var lv LinePlusSingleStatProperties
if err := json.Unmarshal(v.B, &lv); err != nil {
return nil, err
}
vis = lv
}
case "empty":
var ev EmptyViewProperties
if err := json.Unmarshal(v.B, &ev); err != nil {
return nil, err
}
vis = ev
default:
return nil, fmt.Errorf("unknown type %v", t.Shape)
}
return vis, nil
}
// MarshalViewPropertiesJSON encodes a view into JSON bytes.
func MarshalViewPropertiesJSON(v ViewProperties) ([]byte, error) {
var s interface{}
switch vis := v.(type) {
case SingleStatViewProperties:
s = struct {
Shape string `json:"shape"`
SingleStatViewProperties
}{
Shape: "chronograf-v2",
SingleStatViewProperties: vis,
}
case TableViewProperties:
s = struct {
Shape string `json:"shape"`
TableViewProperties
}{
Shape: "chronograf-v2",
TableViewProperties: vis,
}
case GaugeViewProperties:
s = struct {
Shape string `json:"shape"`
GaugeViewProperties
}{
Shape: "chronograf-v2",
GaugeViewProperties: vis,
}
case XYViewProperties:
s = struct {
Shape string `json:"shape"`
XYViewProperties
}{
Shape: "chronograf-v2",
XYViewProperties: vis,
}
case LinePlusSingleStatProperties:
s = struct {
Shape string `json:"shape"`
LinePlusSingleStatProperties
}{
Shape: "chronograf-v2",
LinePlusSingleStatProperties: vis,
}
case MarkdownViewProperties:
s = struct {
Shape string `json:"shape"`
MarkdownViewProperties
}{
Shape: "chronograf-v2",
MarkdownViewProperties: vis,
}
case LogViewProperties:
s = struct {
Shape string `json:"shape"`
LogViewProperties
}{
Shape: "chronograf-v2",
LogViewProperties: vis,
}
default:
s = struct {
Shape string `json:"shape"`
EmptyViewProperties
}{
Shape: "empty",
EmptyViewProperties: EmptyViewProperties{},
}
}
return json.Marshal(s)
}
// MarshalJSON encodes a view to JSON bytes.
func (c View) MarshalJSON() ([]byte, error) {
vis, err := MarshalViewPropertiesJSON(c.Properties)
if err != nil {
return nil, err
}
return json.Marshal(struct {
ViewContents
ViewProperties json.RawMessage `json:"properties"`
}{
ViewContents: c.ViewContents,
ViewProperties: vis,
})
}
// UnmarshalJSON decodes JSON bytes into the corresponding view type (those that implement ViewProperties).
func (c *View) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &c.ViewContents); err != nil {
return err
}
v, err := UnmarshalViewPropertiesJSON(b)
if err != nil {
return err
}
c.Properties = v
return nil
}
// UnmarshalJSON decodes JSON bytes into the corresponding view update type (those that implement ViewProperties).
func (u *ViewUpdate) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &u.ViewContentsUpdate); err != nil {
return err
}
v, err := UnmarshalViewPropertiesJSON(b)
if err != nil {
return err
}
u.Properties = v
return nil
}
// MarshalJSON encodes a view to JSON bytes.
func (u ViewUpdate) MarshalJSON() ([]byte, error) {
vis, err := MarshalViewPropertiesJSON(u.Properties)
if err != nil {
return nil, err
}
return json.Marshal(struct {
ViewContentsUpdate
ViewProperties json.RawMessage `json:"properties,omitempty"`
}{
ViewContentsUpdate: u.ViewContentsUpdate,
ViewProperties: vis,
})
}
// LinePlusSingleStatProperties represents options for line plus single stat view in Chronograf
type LinePlusSingleStatProperties struct {
Queries []DashboardQuery `json:"queries"`
Axes map[string]Axis `json:"axes"`
Type string `json:"type"`
Legend Legend `json:"legend"`
ViewColors []ViewColor `json:"colors"`
Prefix string `json:"prefix"`
Suffix string `json:"suffix"`
DecimalPlaces DecimalPlaces `json:"decimalPlaces"`
Note string `json:"note"`
ShowNoteWhenEmpty bool `json:"showNoteWhenEmpty"`
}
// XYViewProperties represents options for line, bar, step, or stacked view in Chronograf
type XYViewProperties struct {
Queries []DashboardQuery `json:"queries"`
Axes map[string]Axis `json:"axes"`
Type string `json:"type"`
Legend Legend `json:"legend"`
Geom string `json:"geom"` // Either "line", "step", "stacked", or "bar"
ViewColors []ViewColor `json:"colors"`
Note string `json:"note"`
ShowNoteWhenEmpty bool `json:"showNoteWhenEmpty"`
}
// SingleStatViewProperties represents options for single stat view in Chronograf
type SingleStatViewProperties struct {
Type string `json:"type"`
Queries []DashboardQuery `json:"queries"`
Prefix string `json:"prefix"`
Suffix string `json:"suffix"`
ViewColors []ViewColor `json:"colors"`
DecimalPlaces DecimalPlaces `json:"decimalPlaces"`
Note string `json:"note"`
ShowNoteWhenEmpty bool `json:"showNoteWhenEmpty"`
}
// GaugeViewProperties represents options for gauge view in Chronograf
type GaugeViewProperties struct {
Type string `json:"type"`
Queries []DashboardQuery `json:"queries"`
Prefix string `json:"prefix"`
Suffix string `json:"suffix"`
ViewColors []ViewColor `json:"colors"`
DecimalPlaces DecimalPlaces `json:"decimalPlaces"`
Note string `json:"note"`
ShowNoteWhenEmpty bool `json:"showNoteWhenEmpty"`
}
// TableViewProperties represents options for table view in Chronograf
type TableViewProperties struct {
Type string `json:"type"`
Queries []DashboardQuery `json:"queries"`
ViewColors []ViewColor `json:"colors"`
TableOptions TableOptions `json:"tableOptions"`
FieldOptions []RenamableField `json:"fieldOptions"`
TimeFormat string `json:"timeFormat"`
DecimalPlaces DecimalPlaces `json:"decimalPlaces"`
Note string `json:"note"`
ShowNoteWhenEmpty bool `json:"showNoteWhenEmpty"`
}
type MarkdownViewProperties struct {
Type string `json:"type"`
Note string `json:"note"`
}
// LogViewProperties represents options for log viewer in Chronograf.
type LogViewProperties struct {
Type string `json:"type"`
Columns []LogViewerColumn `json:"columns"`
}
// LogViewerColumn represents a specific column in a Log Viewer.
type LogViewerColumn struct {
Name string `json:"name"`
Position int32 `json:"position"`
Settings []LogColumnSetting `json:"settings"`
}
// LogColumnSetting represent the settings for a specific column of a Log Viewer.
type LogColumnSetting struct {
Type string `json:"type"`
Value string `json:"value"`
Name string `json:"name,omitempty"`
}
func (XYViewProperties) viewProperties() {}
func (LinePlusSingleStatProperties) viewProperties() {}
func (SingleStatViewProperties) viewProperties() {}
func (GaugeViewProperties) viewProperties() {}
func (TableViewProperties) viewProperties() {}
func (MarkdownViewProperties) viewProperties() {}
func (LogViewProperties) viewProperties() {}
func (v XYViewProperties) GetType() string { return v.Type }
func (v LinePlusSingleStatProperties) GetType() string { return v.Type }
func (v SingleStatViewProperties) GetType() string { return v.Type }
func (v GaugeViewProperties) GetType() string { return v.Type }
func (v TableViewProperties) GetType() string { return v.Type }
func (v MarkdownViewProperties) GetType() string { return v.Type }
func (v LogViewProperties) GetType() string { return v.Type }
/////////////////////////////
// Old Chronograf Types
/////////////////////////////
// DashboardQuery represents a query used in a dashboard cell
type DashboardQuery struct {
Text string `json:"text"`
Type string `json:"type"`
SourceID string `json:"sourceID"`
EditMode string `json:"editMode"` // Either "builder" or "advanced"
Name string `json:"name"` // Term or phrase that refers to the query
BuilderConfig BuilderConfig `json:"builderConfig"`
}
type BuilderConfig struct {
Buckets []string `json:"buckets"`
Tags []struct {
Key string `json:"key"`
Values []string `json:"values"`
} `json:"tags"`
Functions []struct {
Name string `json:"name"`
} `json:"functions"`
}
// Axis represents the visible extents of a visualization
type Axis struct {
Bounds []string `json:"bounds"` // bounds are an arbitrary list of client-defined strings that specify the viewport for a View
LegacyBounds [2]int64 `json:"-"` // legacy bounds are for testing a migration from an earlier version of axis
Label string `json:"label"` // label is a description of this Axis
Prefix string `json:"prefix"` // Prefix represents a label prefix for formatting axis values
Suffix string `json:"suffix"` // Suffix represents a label suffix for formatting axis values
Base string `json:"base"` // Base represents the radix for formatting axis values
Scale string `json:"scale"` // Scale is the axis formatting scale. Supported: "log", "linear"
}
// ViewColor represents the encoding of data into visualizations
type ViewColor struct {
ID string `json:"id"` // ID is the unique id of the View color
Type string `json:"type"` // Type is how the color is used. Accepted (min,max,threshold)
Hex string `json:"hex"` // Hex is the hex number of the color
Name string `json:"name"` // Name is the user-facing name of the hex color
Value float64 `json:"value"` // Value is the data value mapped to this color
}
// Legend represents the encoding of data into a legend
type Legend struct {
Type string `json:"type,omitempty"`
Orientation string `json:"orientation,omitempty"`
}
// TableOptions is a type of options for a DashboardView with type Table
type TableOptions struct {
VerticalTimeAxis bool `json:"verticalTimeAxis"`
SortBy RenamableField `json:"sortBy"`
Wrapping string `json:"wrapping"`
FixFirstColumn bool `json:"fixFirstColumn"`
}
// RenamableField is a column/row field in a DashboardView of type Table
type RenamableField struct {
InternalName string `json:"internalName"`
DisplayName string `json:"displayName"`
Visible bool `json:"visible"`
}
// DecimalPlaces indicates whether decimal places should be enforced, and how many digits it should show.
type DecimalPlaces struct {
IsEnforced bool `json:"isEnforced"`
Digits int32 `json:"digits"`
}