2018-07-16 15:23:54 +00:00
package platform
2018-07-05 22:07:53 +00:00
import (
"context"
"encoding/json"
"fmt"
)
2018-08-07 20:10:05 +00:00
// ErrViewNotFound is the error for a missing View.
2018-08-27 17:09:17 +00:00
const ErrViewNotFound = ChronografError ( "View not found" )
2018-07-16 15:23:54 +00:00
2018-08-07 20:10:05 +00:00
// 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 )
2018-07-09 21:22:34 +00:00
2018-08-07 20:10:05 +00:00
// FindViews returns a list of Views that match filter and the total count of matching Views.
2018-07-05 22:07:53 +00:00
// Additional options provide pagination & sorting.
2018-08-07 20:10:05 +00:00
FindViews ( ctx context . Context , filter ViewFilter ) ( [ ] * View , int , error )
2018-07-05 22:07:53 +00:00
2018-08-07 20:10:05 +00:00
// CreateView creates a new View and sets b.ID with the new identifier.
CreateView ( ctx context . Context , b * View ) error
2018-07-05 22:07:53 +00:00
2018-08-07 20:10:05 +00:00
// UpdateView updates a single View with changeset.
// Returns the new View state after update.
UpdateView ( ctx context . Context , id ID , upd ViewUpdate ) ( * View , error )
2018-07-05 22:07:53 +00:00
2018-08-07 20:10:05 +00:00
// DeleteView removes a View by ID.
DeleteView ( ctx context . Context , id ID ) error
2018-07-05 22:07:53 +00:00
}
2018-08-07 20:10:05 +00:00
// ViewUpdate is a struct for updating Views.
type ViewUpdate struct {
ViewContentsUpdate
Properties ViewProperties
2018-07-09 21:22:34 +00:00
}
2018-07-16 15:23:54 +00:00
// Valid validates the update struct. It expects minimal values to be set.
2018-08-07 20:10:05 +00:00
func ( u ViewUpdate ) Valid ( ) error {
_ , ok := u . Properties . ( EmptyViewProperties )
2018-07-16 15:23:54 +00:00
if u . Name == nil && ok {
return fmt . Errorf ( "expected at least one attribute to be updated" )
}
return nil
}
2018-08-07 20:10:05 +00:00
// ViewContentsUpdate is a struct for updating the non properties content of a View.
type ViewContentsUpdate struct {
2018-07-09 21:22:34 +00:00
Name * string ` json:"name" `
2018-07-05 22:07:53 +00:00
}
2018-08-07 20:10:05 +00:00
// ViewFilter represents a set of filter that restrict the returned results.
type ViewFilter struct {
2018-07-09 22:19:34 +00:00
ID * ID
2018-07-05 22:07:53 +00:00
}
2018-08-07 20:10:05 +00:00
// View holds positional and visual information for a View.
type View struct {
ViewContents
Properties ViewProperties
2018-07-05 22:07:53 +00:00
}
2018-08-07 20:10:05 +00:00
type ViewContents struct {
2018-07-09 21:22:34 +00:00
ID ID ` json:"id" `
Name string ` json:"name" `
2018-07-05 22:07:53 +00:00
}
2018-08-07 20:10:05 +00:00
type ViewProperties interface {
ViewProperties ( )
2018-07-05 22:07:53 +00:00
}
2018-08-07 20:10:05 +00:00
// EmptyViewProperties is visualization that has no values
type EmptyViewProperties struct { }
2018-07-09 22:19:34 +00:00
2018-08-07 20:10:05 +00:00
func ( v EmptyViewProperties ) ViewProperties ( ) { }
2018-07-09 22:19:34 +00:00
2018-08-07 20:10:05 +00:00
func UnmarshalViewPropertiesJSON ( b [ ] byte ) ( ViewProperties , error ) {
2018-07-05 22:07:53 +00:00
var v struct {
2018-08-07 20:10:05 +00:00
B json . RawMessage ` json:"properties" `
2018-07-05 22:07:53 +00:00
}
if err := json . Unmarshal ( b , & v ) ; err != nil {
return nil , err
}
2018-07-09 22:19:34 +00:00
if len ( v . B ) == 0 {
2018-08-07 20:10:05 +00:00
// Then there wasn't any visualization field, so there's no need unmarshal it
return EmptyViewProperties { } , nil
2018-07-09 22:19:34 +00:00
}
2018-07-05 22:07:53 +00:00
var t struct {
2018-08-07 20:10:05 +00:00
Shape string ` json:"shape" `
2018-07-05 22:07:53 +00:00
}
if err := json . Unmarshal ( v . B , & t ) ; err != nil {
return nil , err
}
2018-08-07 20:10:05 +00:00
var vis ViewProperties
switch t . Shape {
2018-07-05 22:07:53 +00:00
case "chronograf-v1" :
2018-08-07 20:10:05 +00:00
var qv V1ViewProperties
2018-07-05 22:07:53 +00:00
if err := json . Unmarshal ( v . B , & qv ) ; err != nil {
return nil , err
}
vis = qv
2018-07-09 22:19:34 +00:00
case "empty" :
2018-08-07 20:10:05 +00:00
var ev EmptyViewProperties
2018-07-09 22:19:34 +00:00
if err := json . Unmarshal ( v . B , & ev ) ; err != nil {
return nil , err
}
vis = ev
2018-07-05 22:07:53 +00:00
default :
2018-08-07 20:10:05 +00:00
return nil , fmt . Errorf ( "unknown type %v" , t . Shape )
2018-07-05 22:07:53 +00:00
}
return vis , nil
}
2018-08-07 20:10:05 +00:00
func MarshalViewPropertiesJSON ( v ViewProperties ) ( [ ] byte , error ) {
2018-07-05 22:07:53 +00:00
var s interface { }
switch vis := v . ( type ) {
2018-08-07 20:10:05 +00:00
case V1ViewProperties :
2018-07-05 22:07:53 +00:00
s = struct {
2018-08-07 20:10:05 +00:00
Shape string ` json:"shape" `
V1ViewProperties
2018-07-05 22:07:53 +00:00
} {
2018-08-07 20:10:05 +00:00
Shape : "chronograf-v1" ,
V1ViewProperties : vis ,
2018-07-05 22:07:53 +00:00
}
2018-07-10 23:55:32 +00:00
default :
2018-07-09 22:19:34 +00:00
s = struct {
2018-08-07 20:10:05 +00:00
Shape string ` json:"shape" `
EmptyViewProperties
2018-07-09 22:19:34 +00:00
} {
2018-08-07 20:10:05 +00:00
Shape : "empty" ,
EmptyViewProperties : EmptyViewProperties { } ,
2018-07-09 22:19:34 +00:00
}
2018-07-05 22:07:53 +00:00
}
return json . Marshal ( s )
}
2018-08-07 20:10:05 +00:00
func ( c View ) MarshalJSON ( ) ( [ ] byte , error ) {
vis , err := MarshalViewPropertiesJSON ( c . Properties )
2018-07-05 22:07:53 +00:00
if err != nil {
return nil , err
}
return json . Marshal ( struct {
2018-08-07 20:10:05 +00:00
ViewContents
ViewProperties json . RawMessage ` json:"properties" `
2018-07-05 22:07:53 +00:00
} {
2018-08-07 20:10:05 +00:00
ViewContents : c . ViewContents ,
ViewProperties : vis ,
2018-07-05 22:07:53 +00:00
} )
}
2018-08-07 20:10:05 +00:00
func ( c * View ) UnmarshalJSON ( b [ ] byte ) error {
if err := json . Unmarshal ( b , & c . ViewContents ) ; err != nil {
2018-07-05 22:07:53 +00:00
return err
}
2018-08-07 20:10:05 +00:00
v , err := UnmarshalViewPropertiesJSON ( b )
2018-07-05 22:07:53 +00:00
if err != nil {
return err
}
2018-08-07 20:10:05 +00:00
c . Properties = v
2018-07-05 22:07:53 +00:00
return nil
}
2018-08-07 20:10:05 +00:00
func ( u * ViewUpdate ) UnmarshalJSON ( b [ ] byte ) error {
if err := json . Unmarshal ( b , & u . ViewContentsUpdate ) ; err != nil {
2018-07-09 21:22:34 +00:00
return err
}
2018-08-07 20:10:05 +00:00
v , err := UnmarshalViewPropertiesJSON ( b )
2018-07-09 21:22:34 +00:00
if err != nil {
return err
}
2018-08-07 20:10:05 +00:00
u . Properties = v
2018-07-09 21:22:34 +00:00
return nil
}
2018-08-07 20:10:05 +00:00
func ( u ViewUpdate ) MarshalJSON ( ) ( [ ] byte , error ) {
vis , err := MarshalViewPropertiesJSON ( u . Properties )
2018-07-10 23:55:32 +00:00
if err != nil {
return nil , err
}
return json . Marshal ( struct {
2018-08-07 20:10:05 +00:00
ViewContentsUpdate
ViewProperties json . RawMessage ` json:"properties,omitempty" `
2018-07-10 23:55:32 +00:00
} {
2018-08-07 20:10:05 +00:00
ViewContentsUpdate : u . ViewContentsUpdate ,
ViewProperties : vis ,
2018-07-10 23:55:32 +00:00
} )
}
2018-07-09 21:22:34 +00:00
2018-08-07 20:10:05 +00:00
type V1ViewProperties struct {
Queries [ ] DashboardQuery ` json:"queries" `
Axes map [ string ] Axis ` json:"axes" `
Type string ` json:"type" `
ViewColors [ ] ViewColor ` json:"colors" `
2018-07-05 22:07:53 +00:00
Legend Legend ` json:"legend" `
TableOptions TableOptions ` json:"tableOptions,omitempty" `
FieldOptions [ ] RenamableField ` json:"fieldOptions" `
TimeFormat string ` json:"timeFormat" `
DecimalPlaces DecimalPlaces ` json:"decimalPlaces" `
}
2018-08-07 20:10:05 +00:00
func ( V1ViewProperties ) ViewProperties ( ) { }
2018-07-05 22:07:53 +00:00
/////////////////////////////
// Old Chronograf Types
/////////////////////////////
// DashboardQuery includes state for the query builder. This is a transition
// struct while we move to the full InfluxQL AST
type DashboardQuery struct {
Command string ` json:"query" ` // Command is the query itself
Label string ` json:"label,omitempty" ` // Label is the Y-Axis label for the data
Range * Range ` json:"range,omitempty" ` // Range is the default Y-Axis range for the data
QueryConfig QueryConfig ` json:"queryConfig,omitempty" ` // QueryConfig represents the query state that is understood by the data explorer
2018-08-07 20:10:05 +00:00
// TODO(desa): this should be platform.ID
Source string ` json:"source" ` // Source is the optional URI to the data source for this queryConfig
Shifts [ ] TimeShift ` json:"-" ` // Shifts represents shifts to apply to an influxql query's time range. Clients expect the shift to be in the generated QueryConfig
2018-07-05 22:07:53 +00:00
}
// Range represents an upper and lower bound for data
type Range struct {
Upper int64 ` json:"upper" ` // Upper is the upper bound
Lower int64 ` json:"lower" ` // Lower is the lower bound
}
// QueryConfig represents UI query from the data explorer
type QueryConfig struct {
ID string ` json:"id,omitempty" `
Database string ` json:"database" `
Measurement string ` json:"measurement" `
RetentionPolicy string ` json:"retentionPolicy" `
Fields [ ] Field ` json:"fields" `
Tags map [ string ] [ ] string ` json:"tags" `
GroupBy GroupBy ` json:"groupBy" `
AreTagsAccepted bool ` json:"areTagsAccepted" `
Fill string ` json:"fill,omitempty" `
RawText * string ` json:"rawText" `
Range * DurationRange ` json:"range" `
Shifts [ ] TimeShift ` json:"shifts" `
}
// TimeShift represents a shift to apply to an influxql query's time range
type TimeShift struct {
Label string ` json:"label" ` // Label user facing description
Unit string ` json:"unit" ` // Unit influxql time unit representation i.e. ms, s, m, h, d
Quantity string ` json:"quantity" ` // Quantity number of units
}
// Field represent influxql fields and functions from the UI
type Field struct {
Value interface { } ` json:"value" `
Type string ` json:"type" `
Alias string ` json:"alias" `
Args [ ] Field ` json:"args,omitempty" `
}
// GroupBy represents influxql group by tags from the UI
type GroupBy struct {
Time string ` json:"time" `
Tags [ ] string ` json:"tags" `
}
// DurationRange represents the lower and upper durations of the query config
type DurationRange struct {
Upper string ` json:"upper" `
Lower string ` json:"lower" `
}
// Axis represents the visible extents of a visualization
type Axis struct {
2018-08-07 20:10:05 +00:00
Bounds [ ] string ` json:"bounds" ` // bounds are an arbitrary list of client-defined strings that specify the viewport for a View
2018-07-05 22:07:53 +00:00
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"
}
2018-08-07 20:10:05 +00:00
// ViewColor represents the encoding of data into visualizations
type ViewColor struct {
ID string ` json:"id" ` // ID is the unique id of the View color
2018-07-05 22:07:53 +00:00
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 string ` 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" `
}
2018-08-07 20:10:05 +00:00
// TableOptions is a type of options for a DashboardView with type Table
2018-07-05 22:07:53 +00:00
type TableOptions struct {
VerticalTimeAxis bool ` json:"verticalTimeAxis" `
SortBy RenamableField ` json:"sortBy" `
Wrapping string ` json:"wrapping" `
FixFirstColumn bool ` json:"fixFirstColumn" `
}
2018-08-07 20:10:05 +00:00
// RenamableField is a column/row field in a DashboardView of type Table
2018-07-05 22:07:53 +00:00
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" `
}