From 248b31ea0d5bd6c916b87a27521702910b02ec2a Mon Sep 17 00:00:00 2001 From: Dan Esparza Date: Wed, 22 Aug 2018 21:08:18 -0400 Subject: [PATCH] Start adding methods and tests to support multiple devices --- api/device.go | 29 ----------- data/config.go | 118 ++++++++++++++++++++++++++++++++++++++++++++ data/config_test.go | 23 +++++++++ 3 files changed, 141 insertions(+), 29 deletions(-) delete mode 100644 api/device.go diff --git a/api/device.go b/api/device.go deleted file mode 100644 index 6b51273..0000000 --- a/api/device.go +++ /dev/null @@ -1,29 +0,0 @@ -package api - -import "time" - -// Device represents a device in the system. -type Device struct { - // ID is the unique device identifier - ID string `json:"id"` - - // Type is the type of device (amon / hs110 / etc) - Type string `json:"type"` - - // Name is the name given to the device by the customer - Name string `json:"name"` - - // MinimumMonitorTime is the amount of time required to monitor the device before - // it latches to the 'on' or 'off' state - MinimumMonitorTime time.Duration `json:"minimum_monitor_time"` - - // Threshold is the value that must be crossed (after the MinimumMonitorTime time) - // before the device latches to the 'on' or 'off' state - Threshold int `json:"monitor_threshold"` - - // IPAddress is the network address for the device - IPAddress string `json:"ipaddress"` - - // Running indicates whether the device is currently running (operating) or not - Running bool `json:"running"` -} diff --git a/data/config.go b/data/config.go index de60dc2..87b7432 100644 --- a/data/config.go +++ b/data/config.go @@ -7,6 +7,8 @@ import ( "strings" "time" + "github.com/rs/xid" + "github.com/boltdb/bolt" "github.com/spf13/viper" ) @@ -28,6 +30,35 @@ type ConfigItem struct { LastUpdated time.Time `sql:"updated" json:"updated"` } +// Device represents a device in the system. +type Device struct { + // ID is the unique device identifier + ID string `json:"id"` + + // Type is the type of device (amon / hs110 / etc) + Type string `json:"type"` + + // Name is the name given to the device by the customer + Name string `json:"name"` + + // MinimumMonitorTime is the amount of time required to monitor the device before + // it latches to the 'on' or 'off' state + MinimumMonitorTime time.Duration `json:"minimum_monitor_time"` + + // Threshold is the value that must be crossed (after the MinimumMonitorTime time) + // before the device latches to the 'on' or 'off' state + Threshold int `json:"monitor_threshold"` + + // IPAddress is the network address for the device + IPAddress string `json:"ipaddress"` + + // Running indicates whether the device is currently running (operating) or not + Running bool `json:"running"` + + // LastUpdated indicates when this device was last updated in the system + LastUpdated time.Time `sql:"updated" json:"updated"` +} + // InitStore initializes the database func (store ConfigDB) InitStore() error { // Open the database: @@ -37,6 +68,93 @@ func (store ConfigDB) InitStore() error { return err } +// GetAllDevices gets all devices in the system +func (store ConfigDB) GetAllDevices() ([]Device, error) { + // Our return item + retval := []Device{} + + // Open the database: + db, err := bolt.Open(store.Database, 0600, nil) + defer db.Close() + if err != nil { + return retval, err + } + + // Get all the items: + err = db.View(func(tx *bolt.Tx) error { + + b := tx.Bucket([]byte("devices")) + if b == nil { + return nil + } + + c := b.Cursor() + + for k, v := c.First(); k != nil; k, v = c.Next() { + // Unmarshal data into our config item + device := Device{} + if err := json.Unmarshal(v, &device); err != nil { + return err + } + + retval = append(retval, device) + } + + return nil + }) + + // Return our slice: + return retval, err +} + +// AddOrUpdateDevice adds a device to the system +func (store ConfigDB) AddOrUpdateDevice(device Device) (Device, error) { + + // Our return item: + retval := device + + // If there is no config name, throw an error: + if strings.TrimSpace(retval.Name) == "" { + return retval, errors.New("Device name can't be blank") + } + + // Open the database: + db, err := bolt.Open(store.Database, 0600, nil) + defer db.Close() + if err != nil { + return retval, err + } + + // Update the database: + err = db.Update(func(tx *bolt.Tx) error { + b, err := tx.CreateBucketIfNotExists([]byte("devices")) + if err != nil { + return err + } + + // If we don't have an id, generate an id for the configitem. + // This returns an error only if the Tx is closed or not writeable. + // That can't happen in an Update() call so I ignore the error check. + if retval.ID == "" { + retval.ID = xid.New().String() + } + + // Set the current datetime: + retval.LastUpdated = time.Now() + + // Serialize to JSON format + encoded, err := json.Marshal(retval) + if err != nil { + return err + } + + // Store it, with the 'id' as the key: + return b.Put([]byte(retval.ID), encoded) + }) + + return retval, err +} + // Set inserts or updates the config item func (store ConfigDB) Set(configItem ConfigItem) (ConfigItem, error) { diff --git a/data/config_test.go b/data/config_test.go index 9b0b37e..87315ed 100644 --- a/data/config_test.go +++ b/data/config_test.go @@ -464,3 +464,26 @@ func TestConfig_Remove_ItemDoesntExist_NoErrors(t *testing.T) { t.Errorf("Set then remove failed: Should have removed a config item without error: %s", err) } } + +func TestConfig_GetAllDevices_NoItems_Successful(t *testing.T) { + // Arrange + filename := "testing.db" + defer os.Remove(filename) + + db := data.ConfigDB{ + Database: filename} + + // NO ITEMS STORED + + // Act + response, err := db.GetAllDevices() + + // Assert + if err != nil { + t.Errorf("GetAllDevices failed: Should have returned config items without error: %s", err) + } + + if len(response) != 0 { + t.Errorf("GetAllDevices failed: Should have returned no devices but returned %v instead", len(response)) + } +}