influxdb/fs/proto.go

162 lines
4.0 KiB
Go

package fs
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
platform "github.com/influxdata/influxdb"
"go.uber.org/zap"
)
const protoFileExt = ".json"
// ProtoService implements platform.ProtoService on the file system.
type ProtoService struct {
Dir string
DashboardService platform.DashboardService
Logger *zap.Logger
protos []*platform.Proto
}
// NewProtoService creates an instance of a ProtoService.
func NewProtoService(dir string, logger *zap.Logger, s platform.DashboardService) *ProtoService {
return &ProtoService{
Dir: dir,
Logger: logger.With(zap.String("service", "proto")),
DashboardService: s,
}
}
// WithProtos is used for testing the ProtoService. It will overwrite the protos on the service.
func (s *ProtoService) WithProtos(ps []*platform.Proto) {
s.protos = ps
}
// Open loads the protos from the file system and sets them on the service.
func (s *ProtoService) Open(ctx context.Context) error {
if _, err := os.Stat(s.Dir); os.IsNotExist(err) {
if err := os.Mkdir(s.Dir, 0600); err != nil {
return err
}
}
files, err := ioutil.ReadDir(s.Dir)
if err != nil {
return err
}
protos := []*platform.Proto{}
for _, file := range files {
filename := file.Name()
if path.Ext(filename) != protoFileExt {
s.Logger.Info("file extention did not match proto file extension", zap.String("file", filename))
continue
}
octets, err := ioutil.ReadFile(filepath.Join(s.Dir, filename))
if err != nil {
s.Logger.Info("error openeing file", zap.String("file", filename), zap.Error(err))
continue
}
proto := &platform.Proto{}
if err := json.Unmarshal(octets, proto); err != nil {
s.Logger.Info("error unmarshalling file into proto", zap.String("file", filename), zap.Error(err))
continue
}
// TODO(desa): ensure that the proto provided is a valid proto (e.g. that all viewID exists for all cells in a dashboard).
protos = append(protos, proto)
}
s.protos = protos
return nil
}
// FindProtos returns a list of protos from the file system.
func (s *ProtoService) FindProtos(ctx context.Context) ([]*platform.Proto, error) {
protos := []*platform.Proto{}
for _, proto := range s.protos {
// easy way to make a deep copy
octets, err := json.Marshal(proto)
if err != nil {
return nil, err
}
p := &platform.Proto{}
if err := json.Unmarshal(octets, p); err != nil {
return nil, err
}
protos = append(protos, p)
}
return protos, nil
}
func (s *ProtoService) findProto(ctx context.Context, id platform.ID) (*platform.Proto, error) {
for _, proto := range s.protos {
if proto.ID == id {
return proto, nil
}
}
return nil, &platform.Error{Msg: "proto not found"}
}
// CreateDashboardsFromProtos creates instances of each dashboard in a proto.
func (s *ProtoService) CreateDashboardsFromProto(ctx context.Context, protoID platform.ID, orgID platform.ID) ([]*platform.Dashboard, error) {
// TODO(desa): this should be done transactionally.
proto, err := s.findProto(ctx, protoID)
if err != nil {
return nil, err
}
dashes := []*platform.Dashboard{}
for _, protodash := range proto.Dashboards {
dash := &platform.Dashboard{}
*dash = protodash.Dashboard
// TODO(desa): add organization id here
dash.Cells = nil
if err := s.DashboardService.CreateDashboard(ctx, dash); err != nil {
return nil, err
}
cells := []*platform.Cell{}
for _, protocell := range protodash.Dashboard.Cells {
cell := &platform.Cell{}
*cell = *protocell
protoview, ok := protodash.Views[cell.ID]
if !ok {
return nil, &platform.Error{Msg: fmt.Sprintf("view for ID %q does not exist", cell.ID)}
}
view := &platform.View{}
*view = protoview
opts := platform.AddDashboardCellOptions{View: view}
if err := s.DashboardService.AddDashboardCell(ctx, dash.ID, cell, opts); err != nil {
return nil, err
}
cells = append(cells, cell)
}
dash.Cells = cells
dashes = append(dashes, dash)
}
return dashes, nil
}