1// Copyright 2016 Circonus, Inc. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Annotation API support - Fetch, Create, Update, Delete, and Search
6// See: https://login.circonus.com/resources/api/calls/annotation
7
8package api
9
10import (
11	"encoding/json"
12	"fmt"
13	"net/url"
14	"regexp"
15
16	"github.com/circonus-labs/circonus-gometrics/api/config"
17)
18
19// Annotation defines a annotation. See https://login.circonus.com/resources/api/calls/annotation for more information.
20type Annotation struct {
21	Category       string   `json:"category"`                    // string
22	CID            string   `json:"_cid,omitempty"`              // string
23	Created        uint     `json:"_created,omitempty"`          // uint
24	Description    string   `json:"description"`                 // string
25	LastModified   uint     `json:"_last_modified,omitempty"`    // uint
26	LastModifiedBy string   `json:"_last_modified_by,omitempty"` // string
27	RelatedMetrics []string `json:"rel_metrics"`                 // [] len >= 0
28	Start          uint     `json:"start"`                       // uint
29	Stop           uint     `json:"stop"`                        // uint
30	Title          string   `json:"title"`                       // string
31}
32
33// NewAnnotation returns a new Annotation (with defaults, if applicable)
34func NewAnnotation() *Annotation {
35	return &Annotation{}
36}
37
38// FetchAnnotation retrieves annotation with passed cid.
39func (a *API) FetchAnnotation(cid CIDType) (*Annotation, error) {
40	if cid == nil || *cid == "" {
41		return nil, fmt.Errorf("Invalid annotation CID [none]")
42	}
43
44	annotationCID := string(*cid)
45
46	matched, err := regexp.MatchString(config.AnnotationCIDRegex, annotationCID)
47	if err != nil {
48		return nil, err
49	}
50	if !matched {
51		return nil, fmt.Errorf("Invalid annotation CID [%s]", annotationCID)
52	}
53
54	result, err := a.Get(annotationCID)
55	if err != nil {
56		return nil, err
57	}
58
59	if a.Debug {
60		a.Log.Printf("[DEBUG] fetch annotation, received JSON: %s", string(result))
61	}
62
63	annotation := &Annotation{}
64	if err := json.Unmarshal(result, annotation); err != nil {
65		return nil, err
66	}
67
68	return annotation, nil
69}
70
71// FetchAnnotations retrieves all annotations available to the API Token.
72func (a *API) FetchAnnotations() (*[]Annotation, error) {
73	result, err := a.Get(config.AnnotationPrefix)
74	if err != nil {
75		return nil, err
76	}
77
78	var annotations []Annotation
79	if err := json.Unmarshal(result, &annotations); err != nil {
80		return nil, err
81	}
82
83	return &annotations, nil
84}
85
86// UpdateAnnotation updates passed annotation.
87func (a *API) UpdateAnnotation(cfg *Annotation) (*Annotation, error) {
88	if cfg == nil {
89		return nil, fmt.Errorf("Invalid annotation config [nil]")
90	}
91
92	annotationCID := string(cfg.CID)
93
94	matched, err := regexp.MatchString(config.AnnotationCIDRegex, annotationCID)
95	if err != nil {
96		return nil, err
97	}
98	if !matched {
99		return nil, fmt.Errorf("Invalid annotation CID [%s]", annotationCID)
100	}
101
102	jsonCfg, err := json.Marshal(cfg)
103	if err != nil {
104		return nil, err
105	}
106
107	if a.Debug {
108		a.Log.Printf("[DEBUG] update annotation, sending JSON: %s", string(jsonCfg))
109	}
110
111	result, err := a.Put(annotationCID, jsonCfg)
112	if err != nil {
113		return nil, err
114	}
115
116	annotation := &Annotation{}
117	if err := json.Unmarshal(result, annotation); err != nil {
118		return nil, err
119	}
120
121	return annotation, nil
122}
123
124// CreateAnnotation creates a new annotation.
125func (a *API) CreateAnnotation(cfg *Annotation) (*Annotation, error) {
126	if cfg == nil {
127		return nil, fmt.Errorf("Invalid annotation config [nil]")
128	}
129
130	jsonCfg, err := json.Marshal(cfg)
131	if err != nil {
132		return nil, err
133	}
134
135	if a.Debug {
136		a.Log.Printf("[DEBUG] create annotation, sending JSON: %s", string(jsonCfg))
137	}
138
139	result, err := a.Post(config.AnnotationPrefix, jsonCfg)
140	if err != nil {
141		return nil, err
142	}
143
144	annotation := &Annotation{}
145	if err := json.Unmarshal(result, annotation); err != nil {
146		return nil, err
147	}
148
149	return annotation, nil
150}
151
152// DeleteAnnotation deletes passed annotation.
153func (a *API) DeleteAnnotation(cfg *Annotation) (bool, error) {
154	if cfg == nil {
155		return false, fmt.Errorf("Invalid annotation config [nil]")
156	}
157
158	return a.DeleteAnnotationByCID(CIDType(&cfg.CID))
159}
160
161// DeleteAnnotationByCID deletes annotation with passed cid.
162func (a *API) DeleteAnnotationByCID(cid CIDType) (bool, error) {
163	if cid == nil || *cid == "" {
164		return false, fmt.Errorf("Invalid annotation CID [none]")
165	}
166
167	annotationCID := string(*cid)
168
169	matched, err := regexp.MatchString(config.AnnotationCIDRegex, annotationCID)
170	if err != nil {
171		return false, err
172	}
173	if !matched {
174		return false, fmt.Errorf("Invalid annotation CID [%s]", annotationCID)
175	}
176
177	_, err = a.Delete(annotationCID)
178	if err != nil {
179		return false, err
180	}
181
182	return true, nil
183}
184
185// SearchAnnotations returns annotations matching the specified
186// search query and/or filter. If nil is passed for both parameters
187// all annotations will be returned.
188func (a *API) SearchAnnotations(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]Annotation, error) {
189	q := url.Values{}
190
191	if searchCriteria != nil && *searchCriteria != "" {
192		q.Set("search", string(*searchCriteria))
193	}
194
195	if filterCriteria != nil && len(*filterCriteria) > 0 {
196		for filter, criteria := range *filterCriteria {
197			for _, val := range criteria {
198				q.Add(filter, val)
199			}
200		}
201	}
202
203	if q.Encode() == "" {
204		return a.FetchAnnotations()
205	}
206
207	reqURL := url.URL{
208		Path:     config.AnnotationPrefix,
209		RawQuery: q.Encode(),
210	}
211
212	result, err := a.Get(reqURL.String())
213	if err != nil {
214		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
215	}
216
217	var annotations []Annotation
218	if err := json.Unmarshal(result, &annotations); err != nil {
219		return nil, err
220	}
221
222	return &annotations, nil
223}
224