2018-01-09 23:15:12 +00:00
package influx
import (
"context"
"encoding/json"
"fmt"
"github.com/influxdata/chronograf"
)
const (
// AllAnnotations returns all annotations from the chronograf database
2018-01-12 02:49:10 +00:00
AllAnnotations = ` SELECT "duration_ns", "text", "type", "name" FROM "chronograf"."autogen"."annotations" WHERE "deleted"=false ORDER BY time DESC `
2018-01-09 23:15:12 +00:00
// GetAnnotationID returns all annotations from the chronograf database where id is %s
2018-01-12 02:49:10 +00:00
GetAnnotationID = ` SELECT "duration_ns", "text", "type", "name" FROM "chronograf"."autogen"."annotations" WHERE "name"='%s' AND "deleted"=false ORDER BY time DESC `
// DefaultDB is chronograf. Perhaps later we allow this to be changed
DefaultDB = "chronograf"
// DefaultRP is autogen. Perhaps later we allow this to be changed
DefaultRP = "autogen"
// DefaultMeasurement is annotations.
DefaultMeasurement = "annotations"
2018-01-09 23:15:12 +00:00
)
2018-01-12 23:17:14 +00:00
var _ chronograf . AnnotationStore = & AnnotationStore { }
2018-01-09 23:15:12 +00:00
// AnnotationStore stores annotations within InfluxDB
type AnnotationStore struct {
2018-01-12 23:17:14 +00:00
client chronograf . TimeSeries
}
// NewAnnotationStore constructs an annoation store with a client
func NewAnnotationStore ( client chronograf . TimeSeries ) * AnnotationStore {
return & AnnotationStore {
client : client ,
}
2018-01-09 23:15:12 +00:00
}
// All lists all Annotations
func ( a * AnnotationStore ) All ( ctx context . Context ) ( [ ] chronograf . Annotation , error ) {
2018-01-12 23:17:14 +00:00
return a . allAnnotations ( ctx )
2018-01-09 23:15:12 +00:00
}
// Get retrieves an annotation
func ( a * AnnotationStore ) Get ( ctx context . Context , id string ) ( chronograf . Annotation , error ) {
2018-01-12 23:17:14 +00:00
annos , err := a . getAnnotations ( ctx , id )
2018-01-09 23:15:12 +00:00
if err != nil {
return chronograf . Annotation { } , err
}
if len ( annos ) == 0 {
// TODO: change this to a chronograf.Error
return chronograf . Annotation { } , fmt . Errorf ( "Unknown annotation id %s" , id )
}
return annos [ 0 ] , nil
}
// Add creates a new annotation in the store
func ( a * AnnotationStore ) Add ( ctx context . Context , anno chronograf . Annotation ) ( chronograf . Annotation , error ) {
2018-01-12 23:17:14 +00:00
return anno , a . client . Write ( ctx , & chronograf . Point {
Database : DefaultDB ,
RetentionPolicy : DefaultRP ,
} )
2018-01-09 23:15:12 +00:00
}
// Delete removes the annotation from the store
func ( a * AnnotationStore ) Delete ( ctx context . Context , anno chronograf . Annotation ) error {
2018-01-12 23:17:14 +00:00
return a . client . Write ( ctx , & chronograf . Point {
Database : DefaultDB ,
RetentionPolicy : DefaultRP ,
} )
2018-01-09 23:15:12 +00:00
}
2018-01-12 02:49:10 +00:00
// Update replaces annotation (just a call through to Add)
func ( a * AnnotationStore ) Update ( ctx context . Context , anno chronograf . Annotation ) error {
_ , err := a . Add ( ctx , anno )
return err
2018-01-09 23:15:12 +00:00
}
type value [ ] interface { }
func ( v value ) Int64 ( idx int ) ( int64 , error ) {
if idx >= len ( v ) {
return 0 , fmt . Errorf ( "index %d does not exist in values" , idx )
}
i , ok := v [ idx ] . ( int64 )
if ! ok {
return 0 , fmt . Errorf ( "value at index %d is not int64, but, %T" , idx , v [ idx ] )
}
return i , nil
}
func ( v value ) String ( idx int ) ( string , error ) {
if idx >= len ( v ) {
return "" , fmt . Errorf ( "index %d does not exist in values" , idx )
}
str , ok := v [ idx ] . ( string )
if ! ok {
return "" , fmt . Errorf ( "value at index %d is not string, but, %T" , idx , v [ idx ] )
}
return str , nil
}
type annotationResults [ ] struct {
Series [ ] struct {
Values [ ] value ` json:"values" `
} ` json:"series" `
}
2018-01-12 02:49:10 +00:00
// Annotations converts `SELECT "duration_ns", "text", "type", "name" FROM "chronograf"."autogen"."annotations"` to annotations
2018-01-09 23:15:12 +00:00
func ( r * annotationResults ) Annotations ( ) ( res [ ] chronograf . Annotation , err error ) {
res = [ ] chronograf . Annotation { }
for _ , u := range * r {
for _ , s := range u . Series {
for _ , v := range s . Values {
anno := chronograf . Annotation { }
if anno . Time , err = v . Int64 ( 0 ) ; err != nil {
return
}
if anno . Duration , err = v . Int64 ( 1 ) ; err != nil {
return
}
if anno . Text , err = v . String ( 2 ) ; err != nil {
return
}
if anno . Type , err = v . String ( 3 ) ; err != nil {
return
}
if anno . Name , err = v . String ( 4 ) ; err != nil {
return
}
res = append ( res , anno )
}
}
}
return res , err
}
// queryAnnotations queries the chronograf db and produces all annotations
2018-01-12 23:17:14 +00:00
func ( a * AnnotationStore ) queryAnnotations ( ctx context . Context , query string ) ( [ ] chronograf . Annotation , error ) {
res , err := a . client . Query ( ctx , chronograf . Query {
2018-01-09 23:15:12 +00:00
Command : query ,
2018-01-12 05:05:23 +00:00
DB : DefaultDB ,
Epoch : "ns" ,
2018-01-09 23:15:12 +00:00
} )
if err != nil {
return nil , err
}
octets , err := res . MarshalJSON ( )
if err != nil {
return nil , err
}
results := annotationResults { }
if err := json . Unmarshal ( octets , & results ) ; err != nil {
return nil , err
}
return results . Annotations ( )
}
// allAnnotations returns all annotations from the chronograf.annotations measurement
2018-01-12 23:17:14 +00:00
func ( a * AnnotationStore ) allAnnotations ( ctx context . Context ) ( [ ] chronograf . Annotation , error ) {
2018-01-09 23:15:12 +00:00
// TODO: dedup all ids
2018-01-12 23:17:14 +00:00
return a . queryAnnotations ( ctx , AllAnnotations )
2018-01-09 23:15:12 +00:00
}
// getAnnotations returns all annotations with id
2018-01-12 23:17:14 +00:00
func ( a * AnnotationStore ) getAnnotations ( ctx context . Context , id string ) ( [ ] chronograf . Annotation , error ) {
return a . queryAnnotations ( ctx , fmt . Sprintf ( GetAnnotationID , id ) )
2018-01-09 23:15:12 +00:00
}