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