File: graph.go

package info (click to toggle)
golang-github-circonus-labs-circonus-gometrics 2.3.1-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 824 kB
  • sloc: makefile: 2
file content (356 lines) | stat: -rw-r--r-- 14,358 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Graph API support - Fetch, Create, Update, Delete, and Search
// See: https://login.circonus.com/resources/api/calls/graph

package api

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"

	"github.com/circonus-labs/circonus-gometrics/api/config"
)

// GraphAccessKey defines an access key for a graph
type GraphAccessKey struct {
	Active         bool   `json:"active,omitempty"`           // boolean
	Height         uint   `json:"height,omitempty"`           // uint
	Key            string `json:"key,omitempty"`              // string
	Legend         bool   `json:"legend,omitempty"`           // boolean
	LockDate       bool   `json:"lock_date,omitempty"`        // boolean
	LockMode       string `json:"lock_mode,omitempty"`        // string
	LockRangeEnd   uint   `json:"lock_range_end,omitempty"`   // uint
	LockRangeStart uint   `json:"lock_range_start,omitempty"` // uint
	LockShowTimes  bool   `json:"lock_show_times,omitempty"`  // boolean
	LockZoom       string `json:"lock_zoom,omitempty"`        // string
	Nickname       string `json:"nickname,omitempty"`         // string
	Title          bool   `json:"title,omitempty"`            // boolean
	Width          uint   `json:"width,omitempty"`            // uint
	XLabels        bool   `json:"x_labels,omitempty"`         // boolean
	YLabels        bool   `json:"y_labels,omitempty"`         // boolean
}

// GraphComposite defines a composite
type GraphComposite struct {
	Axis          string  `json:"axis"`           // string
	Color         string  `json:"color"`          // string
	DataFormula   *string `json:"data_formula"`   // string or null
	Hidden        bool    `json:"hidden"`         // boolean
	LegendFormula *string `json:"legend_formula"` // string or null
	Name          string  `json:"name"`           // string
	Stack         *uint   `json:"stack"`          // uint or null
}

// GraphDatapoint defines a datapoint
type GraphDatapoint struct {
	Alpha         *float64    `json:"alpha,string,omitempty"` // float64
	Axis          string      `json:"axis,omitempty"`         // string
	CAQL          *string     `json:"caql,omitempty"`         // string or null
	CheckID       uint        `json:"check_id,omitempty"`     // uint
	Color         *string     `json:"color,omitempty"`        // string
	DataFormula   *string     `json:"data_formula"`           // string or null
	Derive        interface{} `json:"derive,omitempty"`       // BUG doc: string, api: string or boolean(for caql statements)
	Hidden        bool        `json:"hidden"`                 // boolean
	LegendFormula *string     `json:"legend_formula"`         // string or null
	MetricName    string      `json:"metric_name,omitempty"`  // string
	MetricType    string      `json:"metric_type,omitempty"`  // string
	Name          string      `json:"name"`                   // string
	Search        *string     `json:"search"`                 // string or null
	Stack         *uint       `json:"stack"`                  // uint or null
}

// GraphGuide defines a guide
type GraphGuide struct {
	Color         string  `json:"color"`          // string
	DataFormula   *string `json:"data_formula"`   // string or null
	Hidden        bool    `json:"hidden"`         // boolean
	LegendFormula *string `json:"legend_formula"` // string or null
	Name          string  `json:"name"`           // string
}

// GraphMetricCluster defines a metric cluster
type GraphMetricCluster struct {
	AggregateFunc string  `json:"aggregate_function,omitempty"` // string
	Axis          string  `json:"axis,omitempty"`               // string
	Color         *string `json:"color,omitempty"`              // string
	DataFormula   *string `json:"data_formula"`                 // string or null
	Hidden        bool    `json:"hidden"`                       // boolean
	LegendFormula *string `json:"legend_formula"`               // string or null
	MetricCluster string  `json:"metric_cluster,omitempty"`     // string
	Name          string  `json:"name,omitempty"`               // string
	Stack         *uint   `json:"stack"`                        // uint or null
}

// GraphOverlaySet defines an overlay set for a graph
type GraphOverlaySet struct {
	Overlays map[string]GraphOverlay `json:"overlays"`
	Title    string                  `json:"title"`
}

// GraphOverlay defines a single overlay in an overlay set
type GraphOverlay struct {
	DataOpts OverlayDataOptions `json:"data_opts,omitempty"` // OverlayDataOptions
	ID       string             `json:"id,omitempty"`        // string
	Title    string             `json:"title,omitempty"`     // string
	UISpecs  OverlayUISpecs     `json:"ui_specs,omitempty"`  // OverlayUISpecs
}

// OverlayUISpecs defines UI specs for overlay
type OverlayUISpecs struct {
	Decouple bool   `json:"decouple,omitempty"` // boolean
	ID       string `json:"id,omitempty"`       // string
	Label    string `json:"label,omitempty"`    // string
	Type     string `json:"type,omitempty"`     // string
	Z        string `json:"z,omitempty"`        // int encoded as string BUG doc: numeric, api: string
}

// OverlayDataOptions defines overlay options for data. Note, each overlay type requires
// a _subset_ of the options. See Graph API documentation (URL above) for details.
type OverlayDataOptions struct {
	Alerts        string `json:"alerts,omitempty"`         // int encoded as string BUG doc: numeric, api: string
	ArrayOutput   string `json:"array_output,omitempty"`   // int encoded as string BUG doc: numeric, api: string
	BasePeriod    string `json:"base_period,omitempty"`    // int encoded as string BUG doc: numeric, api: string
	Delay         string `json:"delay,omitempty"`          // int encoded as string BUG doc: numeric, api: string
	Extension     string `json:"extension,omitempty"`      // string
	GraphTitle    string `json:"graph_title,omitempty"`    // string
	GraphUUID     string `json:"graph_id,omitempty"`       // string
	InPercent     string `json:"in_percent,omitempty"`     // boolean encoded as string BUG doc: boolean, api: string
	Inverse       string `json:"inverse,omitempty"`        // int encoded as string BUG doc: numeric, api: string
	Method        string `json:"method,omitempty"`         // string
	Model         string `json:"model,omitempty"`          // string
	ModelEnd      string `json:"model_end,omitempty"`      // string
	ModelPeriod   string `json:"model_period,omitempty"`   // string
	ModelRelative string `json:"model_relative,omitempty"` // int encoded as string BUG doc: numeric, api: string
	Out           string `json:"out,omitempty"`            // string
	Prequel       string `json:"prequel,omitempty"`        // int
	Presets       string `json:"presets,omitempty"`        // string
	Quantiles     string `json:"quantiles,omitempty"`      // string
	SeasonLength  string `json:"season_length,omitempty"`  // int encoded as string BUG doc: numeric, api: string
	Sensitivity   string `json:"sensitivity,omitempty"`    // int encoded as string BUG doc: numeric, api: string
	SingleValue   string `json:"single_value,omitempty"`   // int encoded as string BUG doc: numeric, api: string
	TargetPeriod  string `json:"target_period,omitempty"`  // string
	TimeOffset    string `json:"time_offset,omitempty"`    // string
	TimeShift     string `json:"time_shift,omitempty"`     // int encoded as string BUG doc: numeric, api: string
	Transform     string `json:"transform,omitempty"`      // string
	Version       string `json:"version,omitempty"`        // int encoded as string BUG doc: numeric, api: string
	Window        string `json:"window,omitempty"`         // int encoded as string BUG doc: numeric, api: string
	XShift        string `json:"x_shift,omitempty"`        // string
}

// Graph defines a graph. See https://login.circonus.com/resources/api/calls/graph for more information.
type Graph struct {
	AccessKeys     []GraphAccessKey            `json:"access_keys,omitempty"`                // [] len >= 0
	CID            string                      `json:"_cid,omitempty"`                       // string
	Composites     []GraphComposite            `json:"composites,omitempty"`                 // [] len >= 0
	Datapoints     []GraphDatapoint            `json:"datapoints,omitempt"`                  // [] len >= 0
	Description    string                      `json:"description,omitempty"`                // string
	Guides         []GraphGuide                `json:"guides,omitempty"`                     // [] len >= 0
	LineStyle      *string                     `json:"line_style"`                           // string or null
	LogLeftY       *int                        `json:"logarithmic_left_y,string,omitempty"`  // int encoded as string or null BUG doc: number (not string)
	LogRightY      *int                        `json:"logarithmic_right_y,string,omitempty"` // int encoded as string or null BUG doc: number (not string)
	MaxLeftY       *float64                    `json:"max_left_y,string,omitempty"`          // float64 encoded as string or null BUG doc: number (not string)
	MaxRightY      *float64                    `json:"max_right_y,string,omitempty"`         // float64 encoded as string or null BUG doc: number (not string)
	MetricClusters []GraphMetricCluster        `json:"metric_clusters,omitempty"`            // [] len >= 0
	MinLeftY       *float64                    `json:"min_left_y,string,omitempty"`          // float64 encoded as string or null BUG doc: number (not string)
	MinRightY      *float64                    `json:"min_right_y,string,omitempty"`         // float64 encoded as string or null BUG doc: number (not string)
	Notes          *string                     `json:"notes,omitempty"`                      // string or null
	OverlaySets    *map[string]GraphOverlaySet `json:"overlay_sets,omitempty"`               // GroupOverLaySets or null
	Style          *string                     `json:"style"`                                // string or null
	Tags           []string                    `json:"tags,omitempty"`                       // [] len >= 0
	Title          string                      `json:"title,omitempty"`                      // string
}

// NewGraph returns a Graph (with defaults, if applicable)
func NewGraph() *Graph {
	return &Graph{}
}

// FetchGraph retrieves graph with passed cid.
func (a *API) FetchGraph(cid CIDType) (*Graph, error) {
	if cid == nil || *cid == "" {
		return nil, fmt.Errorf("Invalid graph CID [none]")
	}

	graphCID := string(*cid)

	matched, err := regexp.MatchString(config.GraphCIDRegex, graphCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid graph CID [%s]", graphCID)
	}

	result, err := a.Get(graphCID)
	if err != nil {
		return nil, err
	}
	if a.Debug {
		a.Log.Printf("[DEBUG] fetch graph, received JSON: %s", string(result))
	}

	graph := new(Graph)
	if err := json.Unmarshal(result, graph); err != nil {
		return nil, err
	}

	return graph, nil
}

// FetchGraphs retrieves all graphs available to the API Token.
func (a *API) FetchGraphs() (*[]Graph, error) {
	result, err := a.Get(config.GraphPrefix)
	if err != nil {
		return nil, err
	}

	var graphs []Graph
	if err := json.Unmarshal(result, &graphs); err != nil {
		return nil, err
	}

	return &graphs, nil
}

// UpdateGraph updates passed graph.
func (a *API) UpdateGraph(cfg *Graph) (*Graph, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid graph config [nil]")
	}

	graphCID := string(cfg.CID)

	matched, err := regexp.MatchString(config.GraphCIDRegex, graphCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid graph CID [%s]", graphCID)
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] update graph, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Put(graphCID, jsonCfg)
	if err != nil {
		return nil, err
	}

	graph := &Graph{}
	if err := json.Unmarshal(result, graph); err != nil {
		return nil, err
	}

	return graph, nil
}

// CreateGraph creates a new graph.
func (a *API) CreateGraph(cfg *Graph) (*Graph, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid graph config [nil]")
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] update graph, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Post(config.GraphPrefix, jsonCfg)
	if err != nil {
		return nil, err
	}

	graph := &Graph{}
	if err := json.Unmarshal(result, graph); err != nil {
		return nil, err
	}

	return graph, nil
}

// DeleteGraph deletes passed graph.
func (a *API) DeleteGraph(cfg *Graph) (bool, error) {
	if cfg == nil {
		return false, fmt.Errorf("Invalid graph config [nil]")
	}
	return a.DeleteGraphByCID(CIDType(&cfg.CID))
}

// DeleteGraphByCID deletes graph with passed cid.
func (a *API) DeleteGraphByCID(cid CIDType) (bool, error) {
	if cid == nil || *cid == "" {
		return false, fmt.Errorf("Invalid graph CID [none]")
	}

	graphCID := string(*cid)

	matched, err := regexp.MatchString(config.GraphCIDRegex, graphCID)
	if err != nil {
		return false, err
	}
	if !matched {
		return false, fmt.Errorf("Invalid graph CID [%s]", graphCID)
	}

	_, err = a.Delete(graphCID)
	if err != nil {
		return false, err
	}

	return true, nil
}

// SearchGraphs returns graphs matching the specified search query
// and/or filter. If nil is passed for both parameters all graphs
// will be returned.
func (a *API) SearchGraphs(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]Graph, error) {
	q := url.Values{}

	if searchCriteria != nil && *searchCriteria != "" {
		q.Set("search", string(*searchCriteria))
	}

	if filterCriteria != nil && len(*filterCriteria) > 0 {
		for filter, criteria := range *filterCriteria {
			for _, val := range criteria {
				q.Add(filter, val)
			}
		}
	}

	if q.Encode() == "" {
		return a.FetchGraphs()
	}

	reqURL := url.URL{
		Path:     config.GraphPrefix,
		RawQuery: q.Encode(),
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
	}

	var graphs []Graph
	if err := json.Unmarshal(result, &graphs); err != nil {
		return nil, err
	}

	return &graphs, nil
}