From 2b56cae2211cf4a62dc4b501cb1b504613367309 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Fri, 30 Sep 2016 15:39:27 -0500 Subject: [PATCH 1/2] Add persistence to sources and use bolt by default --- .gitignore | 1 + bolt/client.go | 8 ++ bolt/client_test.go | 40 ++++++++++ bolt/exploration_test.go | 36 --------- bolt/internal/internal.go | 30 +++++++ bolt/internal/internal.pb.go | 61 +++++++++----- bolt/internal/internal.proto | 20 +++-- bolt/internal/internal_test.go | 21 +++++ bolt/sources.go | 116 +++++++++++++++++++++++++++ bolt/sources_test.go | 95 ++++++++++++++++++++++ errors.go | 1 + handlers/exploration.go | 25 +++--- handlers/sources.go | 141 +++++++++++++++++++++++++++++++++ handlers/store.go | 9 +++ models/explorations.go | 11 ++- restapi/configure_mr_fusion.go | 120 +++++++++++++--------------- swagger.yaml | 2 + 17 files changed, 596 insertions(+), 141 deletions(-) create mode 100644 bolt/client_test.go create mode 100644 bolt/sources.go create mode 100644 bolt/sources_test.go create mode 100644 handlers/sources.go create mode 100644 handlers/store.go diff --git a/.gitignore b/.gitignore index b0859f97b7..b6f665f651 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules mrfusion node_modules/ build/ +chronograf.db diff --git a/bolt/client.go b/bolt/client.go index bb94723bf5..92503ff4be 100644 --- a/bolt/client.go +++ b/bolt/client.go @@ -13,11 +13,13 @@ type Client struct { Now func() time.Time ExplorationStore *ExplorationStore + SourcesStore *SourcesStore } func NewClient() *Client { c := &Client{Now: time.Now} c.ExplorationStore = &ExplorationStore{client: c} + c.SourcesStore = &SourcesStore{client: c} return c } @@ -35,12 +37,18 @@ func (c *Client) Open() error { if _, err := tx.CreateBucketIfNotExists(ExplorationBucket); err != nil { return err } + // Always create Sources bucket. + if _, err := tx.CreateBucketIfNotExists(SourcesBucket); err != nil { + return err + } + return nil }); err != nil { return err } c.ExplorationStore = &ExplorationStore{client: c} + c.SourcesStore = &SourcesStore{client: c} return nil } diff --git a/bolt/client_test.go b/bolt/client_test.go new file mode 100644 index 0000000000..8c3cd58403 --- /dev/null +++ b/bolt/client_test.go @@ -0,0 +1,40 @@ +package bolt_test + +import ( + "errors" + "io/ioutil" + "os" + "time" + + "github.com/influxdata/mrfusion/bolt" +) + +// TestNow is a set time for testing. +var TestNow = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) + +// TestClient wraps *bolt.Client. +type TestClient struct { + *bolt.Client +} + +// NewTestClient creates new *bolt.Client with a set time and temp path. +func NewTestClient() (*TestClient, error) { + f, err := ioutil.TempFile("", "mrfusion-bolt-") + if err != nil { + return nil, errors.New("unable to open temporary boltdb file") + } + f.Close() + + c := &TestClient{ + Client: bolt.NewClient(), + } + c.Path = f.Name() + c.Now = func() time.Time { return TestNow } + + return c, nil +} + +func (c *TestClient) Close() error { + defer os.Remove(c.Path) + return c.Client.Close() +} diff --git a/bolt/exploration_test.go b/bolt/exploration_test.go index 01cdaaba22..6e7ff7cd34 100644 --- a/bolt/exploration_test.go +++ b/bolt/exploration_test.go @@ -1,47 +1,11 @@ package bolt_test import ( - "errors" - // "fmt" - "io/ioutil" - "os" "testing" - "time" "github.com/influxdata/mrfusion" - "github.com/influxdata/mrfusion/bolt" ) -// TestNow is a set time for testing. -var TestNow = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) - -// TestClient wraps *bolt.Client. -type TestClient struct { - *bolt.Client -} - -// NewTestClient creates new *bolt.Client with a set time and temp path. -func NewTestClient() (*TestClient, error) { - f, err := ioutil.TempFile("", "mrfusion-bolt-") - if err != nil { - return nil, errors.New("unable to open temporary boltdb file") - } - f.Close() - - c := &TestClient{ - Client: bolt.NewClient(), - } - c.Path = f.Name() - c.Now = func() time.Time { return TestNow } - - return c, nil -} - -func (c *TestClient) Close() error { - defer os.Remove(c.Path) - return c.Client.Close() -} - // Ensure an ExplorationStore can store, retrieve, update, and delete explorations. func TestExplorationStore_CRUD(t *testing.T) { c, err := NewTestClient() diff --git a/bolt/internal/internal.go b/bolt/internal/internal.go index 92c07dd046..1bacacff52 100644 --- a/bolt/internal/internal.go +++ b/bolt/internal/internal.go @@ -39,3 +39,33 @@ func UnmarshalExploration(data []byte, e *mrfusion.Exploration) error { return nil } + +// MarshalSource encodes an source to binary protobuf format. +func MarshalSource(s mrfusion.Source) ([]byte, error) { + return proto.Marshal(&Source{ + ID: int64(s.ID), + Name: s.Name, + Type: s.Type, + Username: s.Username, + Password: s.Password, + URLs: s.URL, + Default: s.Default, + }) +} + +// UnmarshalSource decodes an source from binary protobuf data. +func UnmarshalSource(data []byte, s *mrfusion.Source) error { + var pb Source + if err := proto.Unmarshal(data, &pb); err != nil { + return err + } + + s.ID = int(pb.ID) + s.Name = pb.Name + s.Type = pb.Type + s.Username = pb.Username + s.Password = pb.Password + s.URL = pb.URLs + s.Default = pb.Default + return nil +} diff --git a/bolt/internal/internal.pb.go b/bolt/internal/internal.pb.go index 3c6a6f9172..6039dca713 100644 --- a/bolt/internal/internal.pb.go +++ b/bolt/internal/internal.pb.go @@ -10,6 +10,7 @@ It is generated from these files: It has these top-level messages: Exploration + Source */ package internal @@ -29,13 +30,13 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package type Exploration struct { - ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` - Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` - UserID int64 `protobuf:"varint,3,opt,name=UserID,proto3" json:"UserID,omitempty"` - Data string `protobuf:"bytes,4,opt,name=Data,proto3" json:"Data,omitempty"` - CreatedAt int64 `protobuf:"varint,5,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"` - UpdatedAt int64 `protobuf:"varint,6,opt,name=UpdatedAt,proto3" json:"UpdatedAt,omitempty"` - Default bool `protobuf:"varint,7,opt,name=Default,proto3" json:"Default,omitempty"` + ID int64 `protobuf:"varint,1,opt,name=ID,json=iD,proto3" json:"ID,omitempty"` + Name string `protobuf:"bytes,2,opt,name=Name,json=name,proto3" json:"Name,omitempty"` + UserID int64 `protobuf:"varint,3,opt,name=UserID,json=userID,proto3" json:"UserID,omitempty"` + Data string `protobuf:"bytes,4,opt,name=Data,json=data,proto3" json:"Data,omitempty"` + CreatedAt int64 `protobuf:"varint,5,opt,name=CreatedAt,json=createdAt,proto3" json:"CreatedAt,omitempty"` + UpdatedAt int64 `protobuf:"varint,6,opt,name=UpdatedAt,json=updatedAt,proto3" json:"UpdatedAt,omitempty"` + Default bool `protobuf:"varint,7,opt,name=Default,json=default,proto3" json:"Default,omitempty"` } func (m *Exploration) Reset() { *m = Exploration{} } @@ -43,23 +44,45 @@ func (m *Exploration) String() string { return proto.CompactTextStrin func (*Exploration) ProtoMessage() {} func (*Exploration) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{0} } +type Source struct { + ID int64 `protobuf:"varint,1,opt,name=ID,json=iD,proto3" json:"ID,omitempty"` + Name string `protobuf:"bytes,2,opt,name=Name,json=name,proto3" json:"Name,omitempty"` + Type string `protobuf:"bytes,3,opt,name=Type,json=type,proto3" json:"Type,omitempty"` + Username string `protobuf:"bytes,4,opt,name=Username,json=username,proto3" json:"Username,omitempty"` + Password string `protobuf:"bytes,5,opt,name=Password,json=password,proto3" json:"Password,omitempty"` + URLs []string `protobuf:"bytes,6,rep,name=URLs,json=uRLs" json:"URLs,omitempty"` + Default bool `protobuf:"varint,7,opt,name=Default,json=default,proto3" json:"Default,omitempty"` +} + +func (m *Source) Reset() { *m = Source{} } +func (m *Source) String() string { return proto.CompactTextString(m) } +func (*Source) ProtoMessage() {} +func (*Source) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{1} } + func init() { proto.RegisterType((*Exploration)(nil), "internal.Exploration") + proto.RegisterType((*Source)(nil), "internal.Source") } func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) } var fileDescriptorInternal = []byte{ - // 176 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xcb, 0xcc, 0x2b, 0x49, - 0x2d, 0xca, 0x4b, 0xcc, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x80, 0xf1, 0x95, 0x36, - 0x33, 0x72, 0x71, 0xbb, 0x56, 0x14, 0xe4, 0xe4, 0x17, 0x25, 0x96, 0x64, 0xe6, 0xe7, 0x09, 0xf1, - 0x71, 0x31, 0x79, 0xba, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x30, 0x07, 0x31, 0x79, 0xba, 0x08, 0x09, - 0x71, 0xb1, 0xf8, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x29, 0x30, 0x6a, 0x70, 0x06, 0x81, 0xd9, 0x42, - 0x62, 0x5c, 0x6c, 0xa1, 0xc5, 0xa9, 0x45, 0x9e, 0x2e, 0x12, 0xcc, 0x60, 0x75, 0x50, 0x1e, 0x48, - 0xad, 0x4b, 0x62, 0x49, 0xa2, 0x04, 0x0b, 0x44, 0x2d, 0x88, 0x2d, 0x24, 0xc3, 0xc5, 0xe9, 0x5c, - 0x94, 0x9a, 0x58, 0x92, 0x9a, 0xe2, 0x58, 0x22, 0xc1, 0x0a, 0x56, 0x8e, 0x10, 0x00, 0xc9, 0x86, - 0x16, 0xa4, 0x40, 0x65, 0xd9, 0x20, 0xb2, 0x70, 0x01, 0x21, 0x09, 0x2e, 0x76, 0x97, 0xd4, 0xb4, - 0xc4, 0xd2, 0x9c, 0x12, 0x09, 0x76, 0x05, 0x46, 0x0d, 0x8e, 0x20, 0x18, 0x37, 0x89, 0x0d, 0xec, - 0x0d, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8b, 0x69, 0xb2, 0xe7, 0xd8, 0x00, 0x00, 0x00, + // 257 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x90, 0xcf, 0x4a, 0xf4, 0x30, + 0x14, 0xc5, 0x49, 0xdb, 0x2f, 0x6d, 0xef, 0x07, 0xb3, 0xc8, 0x42, 0x82, 0xb8, 0x28, 0xb3, 0xea, + 0xca, 0x8d, 0x4f, 0x20, 0xd6, 0xc5, 0xc0, 0x20, 0x12, 0xed, 0x03, 0x5c, 0xa7, 0x57, 0x28, 0x74, + 0x9a, 0x90, 0x3f, 0xe8, 0xbc, 0x90, 0x2f, 0xe0, 0x0b, 0x4a, 0xd2, 0x76, 0x29, 0xb8, 0x3c, 0xf7, + 0x77, 0x38, 0xfc, 0x12, 0xd8, 0x8d, 0xb3, 0x27, 0x3b, 0xe3, 0x74, 0x6b, 0xac, 0xf6, 0x5a, 0x54, + 0x5b, 0xde, 0x7f, 0x33, 0xf8, 0xff, 0xf8, 0x69, 0x26, 0x6d, 0xd1, 0x8f, 0x7a, 0x16, 0x3b, 0xc8, + 0x0e, 0x9d, 0x64, 0x0d, 0x6b, 0x73, 0x95, 0x8d, 0x9d, 0x10, 0x50, 0x3c, 0xe1, 0x99, 0x64, 0xd6, + 0xb0, 0xb6, 0x56, 0xc5, 0x8c, 0x67, 0x12, 0x57, 0xc0, 0x7b, 0x47, 0xf6, 0xd0, 0xc9, 0x3c, 0xf5, + 0x78, 0x48, 0x29, 0x76, 0x3b, 0xf4, 0x28, 0x8b, 0xa5, 0x3b, 0xa0, 0x47, 0x71, 0x03, 0xf5, 0x83, + 0x25, 0xf4, 0x34, 0xdc, 0x7b, 0xf9, 0x2f, 0xd5, 0xeb, 0xd3, 0x76, 0x88, 0xb4, 0x37, 0xc3, 0x4a, + 0xf9, 0x42, 0xc3, 0x76, 0x10, 0x12, 0xca, 0x8e, 0xde, 0x31, 0x4c, 0x5e, 0x96, 0x0d, 0x6b, 0x2b, + 0x55, 0x0e, 0x4b, 0xdc, 0x7f, 0x31, 0xe0, 0x2f, 0x3a, 0xd8, 0x13, 0xfd, 0x49, 0x58, 0x40, 0xf1, + 0x7a, 0x31, 0x94, 0x74, 0x6b, 0x55, 0xf8, 0x8b, 0x21, 0x71, 0x0d, 0x55, 0x7c, 0x44, 0xe4, 0xab, + 0x70, 0x15, 0xd6, 0x1c, 0xd9, 0x33, 0x3a, 0xf7, 0xa1, 0xed, 0x90, 0x9c, 0x6b, 0x55, 0x99, 0x35, + 0xc7, 0xad, 0x5e, 0x1d, 0x9d, 0xe4, 0x4d, 0x1e, 0xb7, 0x82, 0x3a, 0xba, 0xdf, 0x45, 0xdf, 0x78, + 0xfa, 0xef, 0xbb, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x58, 0x30, 0x04, 0x81, 0x01, 0x00, + 0x00, } diff --git a/bolt/internal/internal.proto b/bolt/internal/internal.proto index e2b7488c9d..6a7d453732 100644 --- a/bolt/internal/internal.proto +++ b/bolt/internal/internal.proto @@ -2,11 +2,21 @@ syntax = "proto3"; package internal; message Exploration { - int64 ID = 1; // ExplorationID is a unique ID for an Exploration. - string Name = 2; // User provided name of the Exploration. - int64 UserID = 3; // UserID is the owner of this Exploration. - string Data = 4; // Opaque blob of JSON data. + int64 ID = 1; // ExplorationID is a unique ID for an Exploration. + string Name = 2; // User provided name of the Exploration. + int64 UserID = 3; // UserID is the owner of this Exploration. + string Data = 4; // Opaque blob of JSON data. int64 CreatedAt = 5; // Time the exploration was first created. int64 UpdatedAt = 6; // Latest time the exploration was updated. - bool Default = 7; // Flags an exploration as the default. + bool Default = 7; // Flags an exploration as the default. +} + +message Source { + int64 ID = 1; // ID is the unique ID of the source + string Name = 2; // Name is the user-defined name for the source + string Type = 3; // Type specifies which kinds of source (enterprise vs oss) + string Username = 4; // Username is the username to connect to the source + string Password = 5; + repeated string URLs = 6; // URL are the connections to the source + bool Default = 7; // Flags an exploration as the default. } diff --git a/bolt/internal/internal_test.go b/bolt/internal/internal_test.go index b3210d6594..27812b3afc 100644 --- a/bolt/internal/internal_test.go +++ b/bolt/internal/internal_test.go @@ -29,3 +29,24 @@ func TestMarshalExploration(t *testing.T) { t.Fatalf("exploration protobuf copy error: got %#v, expected %#v", vv, v) } } + +func TestMarshalSource(t *testing.T) { + v := mrfusion.Source{ + ID: 12, + Name: "Fountain of Truth", + Type: "influx", + Username: "docbrown", + Password: "1 point twenty-one g1g@w@tts", + URL: []string{"http://twin-pines.mall.io:8086", "https://lonepine.mall.io:8086"}, + Default: true, + } + + var vv mrfusion.Source + if buf, err := internal.MarshalSource(v); err != nil { + t.Fatal(err) + } else if err := internal.UnmarshalSource(buf, &vv); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(v, vv) { + t.Fatalf("source protobuf copy error: got %#v, expected %#v", vv, v) + } +} diff --git a/bolt/sources.go b/bolt/sources.go new file mode 100644 index 0000000000..668e620f78 --- /dev/null +++ b/bolt/sources.go @@ -0,0 +1,116 @@ +package bolt + +import ( + "github.com/boltdb/bolt" + "github.com/influxdata/mrfusion" + "github.com/influxdata/mrfusion/bolt/internal" + "golang.org/x/net/context" +) + +// Ensure ExplorationStore implements mrfusion.ExplorationStore. +var _ mrfusion.SourcesStore = &SourcesStore{} + +var SourcesBucket = []byte("Sources") + +type SourcesStore struct { + client *Client +} + +// All returns all known sources +func (s *SourcesStore) All(ctx context.Context) ([]mrfusion.Source, error) { + var srcs []mrfusion.Source + if err := s.client.db.View(func(tx *bolt.Tx) error { + if err := tx.Bucket(SourcesBucket).ForEach(func(k, v []byte) error { + var src mrfusion.Source + if err := internal.UnmarshalSource(v, &src); err != nil { + return err + } + srcs = append(srcs, src) + return nil + }); err != nil { + return err + } + return nil + }); err != nil { + return nil, err + } + + return srcs, nil + +} + +// Add creates a new Source in the SourceStore. +func (s *SourcesStore) Add(ctx context.Context, src mrfusion.Source) (mrfusion.Source, error) { + if err := s.client.db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket(SourcesBucket) + seq, err := b.NextSequence() + if err != nil { + return err + } + src.ID = int(seq) + + if v, err := internal.MarshalSource(src); err != nil { + return err + } else if err := b.Put(itob(src.ID), v); err != nil { + return err + } + return nil + }); err != nil { + return mrfusion.Source{}, err + } + + return src, nil +} + +// Delete removes the Source from the SourcesStore +func (s *SourcesStore) Delete(ctx context.Context, src mrfusion.Source) error { + if err := s.client.db.Update(func(tx *bolt.Tx) error { + if err := tx.Bucket(SourcesBucket).Delete(itob(src.ID)); err != nil { + return err + } + return nil + }); err != nil { + return err + } + + return nil +} + +// Get returns a Source if the id exists. +func (s *SourcesStore) Get(ctx context.Context, id int) (mrfusion.Source, error) { + var src mrfusion.Source + if err := s.client.db.View(func(tx *bolt.Tx) error { + if v := tx.Bucket(SourcesBucket).Get(itob(id)); v == nil { + return mrfusion.ErrSourceNotFound + } else if err := internal.UnmarshalSource(v, &src); err != nil { + return err + } + return nil + }); err != nil { + return mrfusion.Source{}, err + } + + return src, nil +} + +// Update a Source +func (s *SourcesStore) Update(ctx context.Context, src mrfusion.Source) error { + if err := s.client.db.Update(func(tx *bolt.Tx) error { + // Get an existing soource with the same ID. + b := tx.Bucket(SourcesBucket) + if v := b.Get(itob(src.ID)); v == nil { + return mrfusion.ErrSourceNotFound + } + + if v, err := internal.MarshalSource(src); err != nil { + return err + } else if err := b.Put(itob(src.ID), v); err != nil { + return err + } + return nil + }); err != nil { + return err + } + + return nil +} diff --git a/bolt/sources_test.go b/bolt/sources_test.go new file mode 100644 index 0000000000..cad5a1214a --- /dev/null +++ b/bolt/sources_test.go @@ -0,0 +1,95 @@ +package bolt_test + +import ( + + // "fmt" + + "reflect" + "testing" + + "github.com/influxdata/mrfusion" +) + +// Ensure an SourceStore can store, retrieve, update, and delete explorations. +func TestSourceStore(t *testing.T) { + c, err := NewTestClient() + if err != nil { + t.Fatal(err) + } + if err := c.Open(); err != nil { + t.Fatal(err) + } + defer c.Close() + s := c.SourcesStore + + srcs := []mrfusion.Source{ + mrfusion.Source{ + Name: "Of Truth", + Type: "influx", + Username: "marty", + Password: "I❤️ jennifer parker", + URL: []string{"toyota-hilux.lyon-estates.local", "lake.hilldale.local"}, + Default: true, + }, + mrfusion.Source{ + Name: "HipToBeSquare", + Type: "influx", + Username: "calvinklein", + Password: "chuck b3rry", + URL: []string{"toyota-hilux.lyon-estates.local", "lake.hilldale.local"}, + Default: true, + }, + } + + // Add new srcs. + for i, src := range srcs { + if srcs[i], err = s.Add(nil, src); err != nil { + t.Fatal(err) + } + // Confirm first src in the store is the same as the original. + if actual, err := s.Get(nil, srcs[i].ID); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(actual, srcs[i]) { + t.Fatal("source loaded is different then source saved; actual: %v, expected %v", actual, srcs[i]) + } + } + + // Update source. + srcs[0].Username = "calvinklein" + srcs[1].Name = "Enchantment Under the Sea Dance" + if err := s.Update(nil, srcs[0]); err != nil { + t.Fatal(err) + } else if err := s.Update(nil, srcs[1]); err != nil { + t.Fatal(err) + } + + // Confirm sources have updated. + if src, err := s.Get(nil, srcs[0].ID); err != nil { + t.Fatal(err) + } else if src.Username != "calvinklein" { + t.Fatalf("source 0 update error: got %v, expected %v", src.Username, "calvinklein") + } + if src, err := s.Get(nil, srcs[1].ID); err != nil { + t.Fatal(err) + } else if src.Name != "Enchantment Under the Sea Dance" { + t.Fatalf("source 1 update error: got %v, expected %v", src.Name, "Enchantment Under the Sea Dance") + } + + // Delete an source. + if err := s.Delete(nil, srcs[0]); err != nil { + t.Fatal(err) + } + + // Confirm source has been deleted. + if _, err := s.Get(nil, srcs[0].ID); err != mrfusion.ErrSourceNotFound { + t.Fatalf("source delete error: got %v, expected %v", err, mrfusion.ErrSourceNotFound) + } + + if bsrcs, err := s.All(nil); err != nil { + t.Fatal(err) + } else if len(bsrcs) != 1 { + t.Fatalf("After delete All returned incorrect number of srcs; got %d, expected %d", len(bsrcs), 1) + } else if !reflect.DeepEqual(bsrcs[0], srcs[1]) { + t.Fatalf("After delete All returned incorrect source; got %v, expected %v", bsrcs[0], srcs[1]) + } +} diff --git a/errors.go b/errors.go index a32a5aa0fa..c47e451ea4 100644 --- a/errors.go +++ b/errors.go @@ -4,6 +4,7 @@ package mrfusion const ( ErrUpstreamTimeout = Error("request to backend timed out") ErrExplorationNotFound = Error("exploration not found") + ErrSourceNotFound = Error("source not found") ) // Error is a domain error encountered while processing mrfusion requests diff --git a/handlers/exploration.go b/handlers/exploration.go index a54eb2f0d6..daff9ca4e5 100644 --- a/handlers/exploration.go +++ b/handlers/exploration.go @@ -14,11 +14,7 @@ import ( op "github.com/influxdata/mrfusion/restapi/operations" ) -type ExplorationStore struct { - ExplorationStore mrfusion.ExplorationStore -} - -func (h *ExplorationStore) Explorations(ctx context.Context, params op.GetSourcesIDUsersUserIDExplorationsParams) middleware.Responder { +func (h *Store) Explorations(ctx context.Context, params op.GetSourcesIDUsersUserIDExplorationsParams) middleware.Responder { uID, err := strconv.Atoi(params.UserID) if err != nil { log.Printf("Error: Unable to convert UserID: %s: %v", params.UserID, err) @@ -26,18 +22,18 @@ func (h *ExplorationStore) Explorations(ctx context.Context, params op.GetSource return op.NewGetSourcesIDUsersUserIDExplorationsDefault(500).WithPayload(errMsg) } - exs, err := h.ExplorationStore.Query(ctx, mrfusion.UserID(uID)) + mrExs, err := h.ExplorationStore.Query(ctx, mrfusion.UserID(uID)) if err != nil { log.Printf("Error: Unknown response from store while querying UserID: %s: %v", params.UserID, err) errMsg := &models.Error{Code: 500, Message: "Error: Unknown response from store while querying UserID"} return op.NewGetSourcesIDUsersUserIDExplorationsDefault(500).WithPayload(errMsg) } - res := &models.Explorations{} - for _, e := range exs { + exs := make([]*models.Exploration, len(mrExs)) + for _, e := range mrExs { rel := "self" href := fmt.Sprintf("/chronograf/v1/sources/1/users/%d/explorations/%d", uID, e.ID) - res.Explorations = append(res.Explorations, &models.Exploration{ + exs = append(exs, &models.Exploration{ Data: e.Data, Name: e.Name, UpdatedAt: strfmt.DateTime(e.UpdatedAt), @@ -49,10 +45,13 @@ func (h *ExplorationStore) Explorations(ctx context.Context, params op.GetSource }, ) } + res := &models.Explorations{ + Explorations: exs, + } return op.NewGetSourcesIDUsersUserIDExplorationsOK().WithPayload(res) } -func (h *ExplorationStore) Exploration(ctx context.Context, params op.GetSourcesIDUsersUserIDExplorationsExplorationIDParams) middleware.Responder { +func (h *Store) Exploration(ctx context.Context, params op.GetSourcesIDUsersUserIDExplorationsExplorationIDParams) middleware.Responder { eID, err := strconv.Atoi(params.ExplorationID) if err != nil { log.Printf("Error: Unable to convert ExplorationID: %s: %v", params.ExplorationID, err) @@ -95,7 +94,7 @@ func (h *ExplorationStore) Exploration(ctx context.Context, params op.GetSources return op.NewGetSourcesIDUsersUserIDExplorationsExplorationIDOK().WithPayload(res) } -func (h *ExplorationStore) UpdateExploration(ctx context.Context, params op.PatchSourcesIDUsersUserIDExplorationsExplorationIDParams) middleware.Responder { +func (h *Store) UpdateExploration(ctx context.Context, params op.PatchSourcesIDUsersUserIDExplorationsExplorationIDParams) middleware.Responder { if params.Exploration == nil { log.Printf("Error: Exploration is nil") errMsg := &models.Error{Code: 400, Message: "Error: Exploration is nil"} @@ -140,7 +139,7 @@ func (h *ExplorationStore) UpdateExploration(ctx context.Context, params op.Patc return op.NewPatchSourcesIDUsersUserIDExplorationsExplorationIDNoContent() } -func (h *ExplorationStore) NewExploration(ctx context.Context, params op.PostSourcesIDUsersUserIDExplorationsParams) middleware.Responder { +func (h *Store) NewExploration(ctx context.Context, params op.PostSourcesIDUsersUserIDExplorationsParams) middleware.Responder { if params.Exploration == nil { log.Printf("Error: Exploration is nil") errMsg := &models.Error{Code: 400, Message: "Error: Exploration is nil"} @@ -185,7 +184,7 @@ func (h *ExplorationStore) NewExploration(ctx context.Context, params op.PostSou } -func (h *ExplorationStore) DeleteExploration(ctx context.Context, params op.DeleteSourcesIDUsersUserIDExplorationsExplorationIDParams) middleware.Responder { +func (h *Store) DeleteExploration(ctx context.Context, params op.DeleteSourcesIDUsersUserIDExplorationsExplorationIDParams) middleware.Responder { eID, err := strconv.Atoi(params.ExplorationID) if err != nil { log.Printf("Error: Unable to convert ExplorationID: %s: %v", params.ExplorationID, err) diff --git a/handlers/sources.go b/handlers/sources.go new file mode 100644 index 0000000000..26dde9990c --- /dev/null +++ b/handlers/sources.go @@ -0,0 +1,141 @@ +package handlers + +import ( + "fmt" + "strconv" + + "github.com/go-openapi/runtime/middleware" + "github.com/influxdata/mrfusion" + "github.com/influxdata/mrfusion/models" + + op "github.com/influxdata/mrfusion/restapi/operations" + "golang.org/x/net/context" +) + +func (h *Store) NewSource(ctx context.Context, params op.PostSourcesParams) middleware.Responder { + src := mrfusion.Source{ + Name: *params.Source.Name, + Type: params.Source.Type, + Username: params.Source.Username, + Password: params.Source.Password, + URL: []string{*params.Source.URL}, + Default: params.Source.Default, + } + var err error + if src, err = h.SourcesStore.Add(ctx, src); err != nil { + errMsg := &models.Error{Code: 500, Message: fmt.Sprintf("Error storing source %v: %v", params.Source, err)} + return op.NewPostSourcesDefault(500).WithPayload(errMsg) + } + mSrc := mrToModel(src) + return op.NewPostSourcesCreated().WithPayload(mSrc).WithLocation(mSrc.Links.Self) +} + +func srcLinks(id int) *models.SourceLinks { + return &models.SourceLinks{ + Self: fmt.Sprintf("/chronograf/v1/sources/%d", id), + Proxy: fmt.Sprintf("/chronograf/v1/sources/%d/proxy", id), + Users: fmt.Sprintf("/chronograf/v1/sources/%d/users", id), + Roles: fmt.Sprintf("/chronograf/v1/sources/%d/roles", id), + Permissions: fmt.Sprintf("/chronograf/v1/sources/%d/permissions", id), + } +} + +func mrToModel(src mrfusion.Source) *models.Source { + return &models.Source{ + ID: strconv.Itoa(src.ID), + Links: srcLinks(src.ID), + Name: &src.Name, + Type: src.Type, + Username: src.Username, + Password: src.Password, + URL: &src.URL[0], + Default: src.Default, + } +} + +func (h *Store) Sources(ctx context.Context, params op.GetSourcesParams) middleware.Responder { + mrSrcs, err := h.SourcesStore.All(ctx) + if err != nil { + errMsg := &models.Error{Code: 500, Message: "Error loading sources"} + return op.NewGetSourcesDefault(500).WithPayload(errMsg) + } + + srcs := make([]*models.Source, len(mrSrcs)) + for i, src := range mrSrcs { + srcs[i] = mrToModel(src) + } + + res := &models.Sources{ + Sources: srcs, + } + + return op.NewGetSourcesOK().WithPayload(res) +} + +func (h *Store) SourcesID(ctx context.Context, params op.GetSourcesIDParams) middleware.Responder { + id, err := strconv.Atoi(params.ID) + if err != nil { + errMsg := &models.Error{Code: 500, Message: fmt.Sprintf("Error converting ID %s", params.ID)} + return op.NewGetSourcesIDDefault(500).WithPayload(errMsg) + } + + src, err := h.SourcesStore.Get(ctx, id) + if err != nil { + errMsg := &models.Error{Code: 404, Message: fmt.Sprintf("Unknown ID %s", params.ID)} + return op.NewGetSourcesIDNotFound().WithPayload(errMsg) + } + + return op.NewGetSourcesIDOK().WithPayload(mrToModel(src)) +} + +func (h *Store) RemoveSource(ctx context.Context, params op.DeleteSourcesIDParams) middleware.Responder { + id, err := strconv.Atoi(params.ID) + if err != nil { + errMsg := &models.Error{Code: 500, Message: fmt.Sprintf("Error converting ID %s", params.ID)} + return op.NewDeleteSourcesIDDefault(500).WithPayload(errMsg) + } + src := mrfusion.Source{ + ID: id, + } + if err = h.SourcesStore.Delete(ctx, src); err != nil { + errMsg := &models.Error{Code: 500, Message: fmt.Sprintf("Unknown error deleting source %s", params.ID)} + return op.NewDeleteSourcesIDDefault(500).WithPayload(errMsg) + } + + return op.NewDeleteSourcesIDNoContent() +} + +func (h *Store) UpdateSource(ctx context.Context, params op.PatchSourcesIDParams) middleware.Responder { + id, err := strconv.Atoi(params.ID) + if err != nil { + errMsg := &models.Error{Code: 500, Message: fmt.Sprintf("Error converting ID %s", params.ID)} + return op.NewPatchSourcesIDDefault(500).WithPayload(errMsg) + } + src, err := h.SourcesStore.Get(ctx, id) + if err != nil { + errMsg := &models.Error{Code: 404, Message: fmt.Sprintf("Unknown ID %s", params.ID)} + return op.NewPatchSourcesIDNotFound().WithPayload(errMsg) + } + src.Default = params.Config.Default + if params.Config.Name != nil { + src.Name = *params.Config.Name + } + if params.Config.Password != "" { + src.Password = params.Config.Password + } + if params.Config.Username != "" { + // TODO: Change to bolt when finished + src.Username = params.Config.Username + } + if params.Config.URL != nil { + src.URL = []string{*params.Config.URL} + } + if params.Config.Type != "" { + src.Type = params.Config.Type + } + if err := h.SourcesStore.Update(ctx, src); err != nil { + errMsg := &models.Error{Code: 500, Message: fmt.Sprintf("Error updating source ID %s", params.ID)} + return op.NewPatchSourcesIDDefault(500).WithPayload(errMsg) + } + return op.NewPatchSourcesIDNoContent() +} diff --git a/handlers/store.go b/handlers/store.go new file mode 100644 index 0000000000..82aa650a94 --- /dev/null +++ b/handlers/store.go @@ -0,0 +1,9 @@ +package handlers + +import "github.com/influxdata/mrfusion" + +// Store handles REST calls to the persistence +type Store struct { + ExplorationStore mrfusion.ExplorationStore + SourcesStore mrfusion.SourcesStore +} diff --git a/models/explorations.go b/models/explorations.go index 10aacfcf15..6616b802a0 100644 --- a/models/explorations.go +++ b/models/explorations.go @@ -8,6 +8,7 @@ import ( "github.com/go-openapi/swag" "github.com/go-openapi/errors" + "github.com/go-openapi/validate" ) /*Explorations explorations @@ -17,8 +18,10 @@ swagger:model Explorations type Explorations struct { /* explorations - */ - Explorations []*Exploration `json:"explorations,omitempty"` + + Required: true + */ + Explorations []*Exploration `json:"explorations"` } // Validate validates this explorations @@ -38,8 +41,8 @@ func (m *Explorations) Validate(formats strfmt.Registry) error { func (m *Explorations) validateExplorations(formats strfmt.Registry) error { - if swag.IsZero(m.Explorations) { // not required - return nil + if err := validate.Required("explorations", "body", m.Explorations); err != nil { + return err } for i := 0; i < len(m.Explorations); i++ { diff --git a/restapi/configure_mr_fusion.go b/restapi/configure_mr_fusion.go index f50fd2f881..2ea0132a90 100644 --- a/restapi/configure_mr_fusion.go +++ b/restapi/configure_mr_fusion.go @@ -18,7 +18,7 @@ import ( "github.com/influxdata/mrfusion/handlers" "github.com/influxdata/mrfusion/influx" "github.com/influxdata/mrfusion/mock" - "github.com/influxdata/mrfusion/restapi/operations" + op "github.com/influxdata/mrfusion/restapi/operations" ) // This file is safe to edit. Once it exists it will not be overwritten @@ -29,26 +29,17 @@ var devFlags = struct { Develop bool `short:"d" long:"develop" description:"Run server in develop mode."` }{} -var influxFlags = struct { - Server string `short:"s" long:"server" description:"Full URL of InfluxDB server (http://localhost:8086)" env:"INFLUX_HOST"` -}{} - var storeFlags = struct { - BoltPath string `short:"b" long:"bolt-path" description:"Full path to boltDB file (/Users/somebody/mrfusion.db)" env:"BOLT_PATH"` + BoltPath string `short:"b" long:"bolt-path" description:"Full path to boltDB file (/Users/somebody/mrfusion.db)" env:"BOLT_PATH" default:"chronograf.db"` }{} -func configureFlags(api *operations.MrFusionAPI) { +func configureFlags(api *op.MrFusionAPI) { api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ swag.CommandLineOptionsGroup{ ShortDescription: "Develop Mode server", LongDescription: "Server will use the ui/build directory directly.", Options: &devFlags, }, - swag.CommandLineOptionsGroup{ - ShortDescription: "Default Time Series Backend", - LongDescription: "Specify the url of an InfluxDB server", - Options: &influxFlags, - }, swag.CommandLineOptionsGroup{ ShortDescription: "Default Store Backend", LongDescription: "Specify the path to a BoltDB file", @@ -70,7 +61,7 @@ func assets() mrfusion.Assets { } } -func configureAPI(api *operations.MrFusionAPI) http.Handler { +func configureAPI(api *op.MrFusionAPI) http.Handler { // configure the api here api.ServeError = errors.ServeError @@ -86,7 +77,7 @@ func configureAPI(api *operations.MrFusionAPI) http.Handler { mockHandler := mock.NewHandler() - api.GetHandler = operations.GetHandlerFunc(mockHandler.AllRoutes) + api.GetHandler = op.GetHandlerFunc(mockHandler.AllRoutes) if len(storeFlags.BoltPath) > 0 { c := bolt.NewClient() @@ -94,102 +85,103 @@ func configureAPI(api *operations.MrFusionAPI) http.Handler { if err := c.Open(); err != nil { panic(err) } - h := handlers.ExplorationStore{ + h := handlers.Store{ ExplorationStore: c.ExplorationStore, + SourcesStore: c.SourcesStore, } - api.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(h.DeleteExploration) - api.GetSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.GetSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(h.Exploration) - api.GetSourcesIDUsersUserIDExplorationsHandler = operations.GetSourcesIDUsersUserIDExplorationsHandlerFunc(h.Explorations) - api.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(h.UpdateExploration) - api.PostSourcesIDUsersUserIDExplorationsHandler = operations.PostSourcesIDUsersUserIDExplorationsHandlerFunc(h.NewExploration) + api.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandler = op.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(h.DeleteExploration) + api.GetSourcesIDUsersUserIDExplorationsExplorationIDHandler = op.GetSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(h.Exploration) + api.GetSourcesIDUsersUserIDExplorationsHandler = op.GetSourcesIDUsersUserIDExplorationsHandlerFunc(h.Explorations) + api.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandler = op.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(h.UpdateExploration) + api.PostSourcesIDUsersUserIDExplorationsHandler = op.PostSourcesIDUsersUserIDExplorationsHandlerFunc(h.NewExploration) + + api.DeleteSourcesIDHandler = op.DeleteSourcesIDHandlerFunc(h.RemoveSource) + api.PatchSourcesIDHandler = op.PatchSourcesIDHandlerFunc(h.UpdateSource) + + api.GetSourcesHandler = op.GetSourcesHandlerFunc(h.Sources) + api.GetSourcesIDHandler = op.GetSourcesIDHandlerFunc(h.SourcesID) + api.PostSourcesHandler = op.PostSourcesHandlerFunc(h.NewSource) + + ts := influx.Client{} + p := handlers.InfluxProxy{ + Srcs: c.SourcesStore, + TimeSeries: &ts, + } + api.PostSourcesIDProxyHandler = op.PostSourcesIDProxyHandlerFunc(p.Proxy) } else { - api.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(mockHandler.DeleteExploration) - api.GetSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.GetSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(mockHandler.Exploration) - api.GetSourcesIDUsersUserIDExplorationsHandler = operations.GetSourcesIDUsersUserIDExplorationsHandlerFunc(mockHandler.Explorations) - api.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(mockHandler.UpdateExploration) - api.PostSourcesIDUsersUserIDExplorationsHandler = operations.PostSourcesIDUsersUserIDExplorationsHandlerFunc(mockHandler.NewExploration) + api.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandler = op.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(mockHandler.DeleteExploration) + api.GetSourcesIDUsersUserIDExplorationsExplorationIDHandler = op.GetSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(mockHandler.Exploration) + api.GetSourcesIDUsersUserIDExplorationsHandler = op.GetSourcesIDUsersUserIDExplorationsHandlerFunc(mockHandler.Explorations) + api.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandler = op.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(mockHandler.UpdateExploration) + api.PostSourcesIDUsersUserIDExplorationsHandler = op.PostSourcesIDUsersUserIDExplorationsHandlerFunc(mockHandler.NewExploration) + + api.DeleteSourcesIDHandler = op.DeleteSourcesIDHandlerFunc(mockHandler.RemoveSource) + api.PatchSourcesIDHandler = op.PatchSourcesIDHandlerFunc(mockHandler.UpdateSource) + + api.GetSourcesHandler = op.GetSourcesHandlerFunc(mockHandler.Sources) + api.GetSourcesIDHandler = op.GetSourcesIDHandlerFunc(mockHandler.SourcesID) + api.PostSourcesHandler = op.PostSourcesHandlerFunc(mockHandler.NewSource) + api.PostSourcesIDProxyHandler = op.PostSourcesIDProxyHandlerFunc(mockHandler.Proxy) } - api.DeleteSourcesIDHandler = operations.DeleteSourcesIDHandlerFunc(mockHandler.RemoveSource) - api.PatchSourcesIDHandler = operations.PatchSourcesIDHandlerFunc(mockHandler.UpdateSource) - - api.GetSourcesHandler = operations.GetSourcesHandlerFunc(mockHandler.Sources) - api.GetSourcesIDHandler = operations.GetSourcesIDHandlerFunc(mockHandler.SourcesID) - api.PostSourcesHandler = operations.PostSourcesHandlerFunc(mockHandler.NewSource) - - if len(influxFlags.Server) > 0 { - c, err := influx.NewClient(influxFlags.Server) - if err != nil { - panic(err) - } - // TODO: Change to bolt when finished - h := handlers.InfluxProxy{ - Srcs: mock.DefaultSourcesStore, - TimeSeries: c, - } - api.PostSourcesIDProxyHandler = operations.PostSourcesIDProxyHandlerFunc(h.Proxy) - } else { - api.PostSourcesIDProxyHandler = operations.PostSourcesIDProxyHandlerFunc(mockHandler.Proxy) - } - - api.DeleteSourcesIDRolesRoleIDHandler = operations.DeleteSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params operations.DeleteSourcesIDRolesRoleIDParams) middleware.Responder { + api.DeleteSourcesIDRolesRoleIDHandler = op.DeleteSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params op.DeleteSourcesIDRolesRoleIDParams) middleware.Responder { return middleware.NotImplemented("operation .DeleteSourcesIDRolesRoleID has not yet been implemented") }) - api.DeleteSourcesIDUsersUserIDHandler = operations.DeleteSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params operations.DeleteSourcesIDUsersUserIDParams) middleware.Responder { + api.DeleteSourcesIDUsersUserIDHandler = op.DeleteSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params op.DeleteSourcesIDUsersUserIDParams) middleware.Responder { return middleware.NotImplemented("operation .DeleteSourcesIDUsersUserID has not yet been implemented") }) - api.DeleteDashboardsIDHandler = operations.DeleteDashboardsIDHandlerFunc(func(ctx context.Context, params operations.DeleteDashboardsIDParams) middleware.Responder { + api.DeleteDashboardsIDHandler = op.DeleteDashboardsIDHandlerFunc(func(ctx context.Context, params op.DeleteDashboardsIDParams) middleware.Responder { return middleware.NotImplemented("operation .DeleteDashboardsID has not yet been implemented") }) - api.GetDashboardsHandler = operations.GetDashboardsHandlerFunc(func(ctx context.Context, params operations.GetDashboardsParams) middleware.Responder { + api.GetDashboardsHandler = op.GetDashboardsHandlerFunc(func(ctx context.Context, params op.GetDashboardsParams) middleware.Responder { return middleware.NotImplemented("operation .GetDashboards has not yet been implemented") }) - api.GetDashboardsIDHandler = operations.GetDashboardsIDHandlerFunc(func(ctx context.Context, params operations.GetDashboardsIDParams) middleware.Responder { + api.GetDashboardsIDHandler = op.GetDashboardsIDHandlerFunc(func(ctx context.Context, params op.GetDashboardsIDParams) middleware.Responder { return middleware.NotImplemented("operation .GetDashboardsID has not yet been implemented") }) - api.GetSourcesIDPermissionsHandler = operations.GetSourcesIDPermissionsHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDPermissionsParams) middleware.Responder { + api.GetSourcesIDPermissionsHandler = op.GetSourcesIDPermissionsHandlerFunc(func(ctx context.Context, params op.GetSourcesIDPermissionsParams) middleware.Responder { return middleware.NotImplemented("operation .GetSourcesIDPermissions has not yet been implemented") }) - api.GetSourcesIDRolesHandler = operations.GetSourcesIDRolesHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDRolesParams) middleware.Responder { + api.GetSourcesIDRolesHandler = op.GetSourcesIDRolesHandlerFunc(func(ctx context.Context, params op.GetSourcesIDRolesParams) middleware.Responder { return middleware.NotImplemented("operation .GetSourcesIDRoles has not yet been implemented") }) - api.GetSourcesIDRolesRoleIDHandler = operations.GetSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDRolesRoleIDParams) middleware.Responder { + api.GetSourcesIDRolesRoleIDHandler = op.GetSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params op.GetSourcesIDRolesRoleIDParams) middleware.Responder { return middleware.NotImplemented("operation .GetSourcesIDRolesRoleID has not yet been implemented") }) - api.GetSourcesIDUsersHandler = operations.GetSourcesIDUsersHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDUsersParams) middleware.Responder { + api.GetSourcesIDUsersHandler = op.GetSourcesIDUsersHandlerFunc(func(ctx context.Context, params op.GetSourcesIDUsersParams) middleware.Responder { return middleware.NotImplemented("operation .GetSourcesIDUsers has not yet been implemented") }) - api.GetSourcesIDUsersUserIDHandler = operations.GetSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDUsersUserIDParams) middleware.Responder { + api.GetSourcesIDUsersUserIDHandler = op.GetSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params op.GetSourcesIDUsersUserIDParams) middleware.Responder { return middleware.NotImplemented("operation .GetSourcesIDUsersUserID has not yet been implemented") }) - api.PatchSourcesIDRolesRoleIDHandler = operations.PatchSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params operations.PatchSourcesIDRolesRoleIDParams) middleware.Responder { + api.PatchSourcesIDRolesRoleIDHandler = op.PatchSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params op.PatchSourcesIDRolesRoleIDParams) middleware.Responder { return middleware.NotImplemented("operation .PatchSourcesIDRolesRoleID has not yet been implemented") }) - api.PatchSourcesIDUsersUserIDHandler = operations.PatchSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params operations.PatchSourcesIDUsersUserIDParams) middleware.Responder { + api.PatchSourcesIDUsersUserIDHandler = op.PatchSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params op.PatchSourcesIDUsersUserIDParams) middleware.Responder { return middleware.NotImplemented("operation .PatchSourcesIDUsersUserID has not yet been implemented") }) - api.PostDashboardsHandler = operations.PostDashboardsHandlerFunc(func(ctx context.Context, params operations.PostDashboardsParams) middleware.Responder { + api.PostDashboardsHandler = op.PostDashboardsHandlerFunc(func(ctx context.Context, params op.PostDashboardsParams) middleware.Responder { return middleware.NotImplemented("operation .PostDashboards has not yet been implemented") }) - api.PostSourcesIDRolesHandler = operations.PostSourcesIDRolesHandlerFunc(func(ctx context.Context, params operations.PostSourcesIDRolesParams) middleware.Responder { + api.PostSourcesIDRolesHandler = op.PostSourcesIDRolesHandlerFunc(func(ctx context.Context, params op.PostSourcesIDRolesParams) middleware.Responder { return middleware.NotImplemented("operation .PostSourcesIDRoles has not yet been implemented") }) - api.PostSourcesIDUsersHandler = operations.PostSourcesIDUsersHandlerFunc(func(ctx context.Context, params operations.PostSourcesIDUsersParams) middleware.Responder { + api.PostSourcesIDUsersHandler = op.PostSourcesIDUsersHandlerFunc(func(ctx context.Context, params op.PostSourcesIDUsersParams) middleware.Responder { return middleware.NotImplemented("operation .PostSourcesIDUsers has not yet been implemented") }) - api.PutDashboardsIDHandler = operations.PutDashboardsIDHandlerFunc(func(ctx context.Context, params operations.PutDashboardsIDParams) middleware.Responder { + api.PutDashboardsIDHandler = op.PutDashboardsIDHandlerFunc(func(ctx context.Context, params op.PutDashboardsIDParams) middleware.Responder { return middleware.NotImplemented("operation .PutDashboardsID has not yet been implemented") }) - api.GetSourcesIDMonitoredHandler = operations.GetSourcesIDMonitoredHandlerFunc(mockHandler.MonitoredServices) + api.GetSourcesIDMonitoredHandler = op.GetSourcesIDMonitoredHandlerFunc(mockHandler.MonitoredServices) api.ServerShutdown = func() {} diff --git a/swagger.yaml b/swagger.yaml index 7b1e3b5c7f..9e72cd390b 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -820,6 +820,8 @@ definitions: type: object Explorations: type: object + required: + - explorations properties: explorations: type: array From b39593c8c4211188b48934204361fe100d76131f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 30 Sep 2016 15:30:09 -0700 Subject: [PATCH 2/2] Fix no first explorer --- ui/src/chronograf/actions/view/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/chronograf/actions/view/index.js b/ui/src/chronograf/actions/view/index.js index 9e85e9973b..05f0a220d3 100644 --- a/ui/src/chronograf/actions/view/index.js +++ b/ui/src/chronograf/actions/view/index.js @@ -238,7 +238,7 @@ export function fetchExplorers({source, userID, explorerURI, push}) { // Create a new explorer session for a user if they don't have any // saved (e.g. when they visit for the first time). if (!explorers.length) { - dispatch(createExploration(push)); + dispatch(createExploration(source, push)); return; }