2018-08-10 21:19:34 +00:00
package filestore
import (
"context"
"encoding/json"
"io/ioutil"
"os"
"path"
"github.com/influxdata/chronograf"
)
// ProtoboardExt is the the file extension searched for in the directory for protoboard files
const ProtoboardExt = ".json"
2020-01-16 21:09:56 +00:00
// Verify Protoboards implements protoboardStore interface.
var _ chronograf . ProtoboardsStore = ( * Protoboards ) ( nil )
2018-08-10 21:19:34 +00:00
// Protoboards are instantiable JSON representation of dashbards. Implements ProtoboardsStore.
type Protoboards struct {
Dir string // Dir is the directory containing protoboard json definitions
Load func ( string ) ( chronograf . Protoboard , error ) // Load receives filename, returns a Protoboard from json file
ReadDir func ( dirname string ) ( [ ] os . FileInfo , error ) // ReadDir reads the directory named by dirname and returns a list of directory entries sorted by filename.
IDs chronograf . ID // ID generates unique ids for new protoboards
Logger chronograf . Logger
}
// NewProtoboards constructs a protoboard store wrapping a file system directory
func NewProtoboards ( dir string , ids chronograf . ID , logger chronograf . Logger ) chronograf . ProtoboardsStore {
return & Protoboards {
Dir : dir ,
Load : protoboardLoadFile ,
ReadDir : ioutil . ReadDir ,
IDs : ids ,
Logger : logger ,
}
}
func protoboardLoadFile ( name string ) ( chronograf . Protoboard , error ) {
octets , err := ioutil . ReadFile ( name )
if err != nil {
return chronograf . Protoboard { } , chronograf . ErrProtoboardNotFound
}
var protoboard chronograf . Protoboard
if err = json . Unmarshal ( octets , & protoboard ) ; err != nil {
return chronograf . Protoboard { } , chronograf . ErrProtoboardInvalid
}
return protoboard , nil
}
// All returns all protoboards from the directory
func ( a * Protoboards ) All ( ctx context . Context ) ( [ ] chronograf . Protoboard , error ) {
files , err := a . ReadDir ( a . Dir )
if err != nil {
return nil , err
}
protoboards := [ ] chronograf . Protoboard { }
for _ , file := range files {
if path . Ext ( file . Name ( ) ) != ProtoboardExt {
continue
}
if protoboard , err := a . Load ( path . Join ( a . Dir , file . Name ( ) ) ) ; err != nil {
continue // We want to load all files we can.
} else {
protoboards = append ( protoboards , protoboard )
}
}
return protoboards , nil
}
// Get returns a protoboard file from the protoboard directory
func ( a * Protoboards ) Get ( ctx context . Context , ID string ) ( chronograf . Protoboard , error ) {
l , file , err := a . idToFile ( ID )
if err != nil {
return chronograf . Protoboard { } , err
}
if err != nil {
if err == chronograf . ErrProtoboardNotFound {
a . Logger .
WithField ( "component" , "protoboards" ) .
WithField ( "name" , file ) .
Error ( "Unable to read file" )
} else if err == chronograf . ErrProtoboardInvalid {
a . Logger .
WithField ( "component" , "protoboards" ) .
WithField ( "name" , file ) .
Error ( "File is not a protoboard" )
}
return chronograf . Protoboard { } , err
}
return l , nil
}
// idToFile takes an id and finds the associated filename
func ( a * Protoboards ) idToFile ( ID string ) ( chronograf . Protoboard , string , error ) {
// Find the name of the file through matching the ID in the protoboard
// content with the ID passed.
files , err := a . ReadDir ( a . Dir )
if err != nil {
return chronograf . Protoboard { } , "" , err
}
for _ , f := range files {
if path . Ext ( f . Name ( ) ) != ProtoboardExt {
continue
}
file := path . Join ( a . Dir , f . Name ( ) )
protoboard , err := a . Load ( file )
if err != nil {
return chronograf . Protoboard { } , "" , err
}
if protoboard . ID == ID {
return protoboard , file , nil
}
}
return chronograf . Protoboard { } , "" , chronograf . ErrProtoboardNotFound
}