ducky/devices

Paddy 2015-11-12 Parent:600326d50e74 Child:408abf6e48d3

3:8d40c0e5f4d9 Go to Latest

ducky/devices/devices.go

Fix happy path test for getting devices. We forgot to create the Devices in the Storer tests, so our GetDevices happy path test was invalid. Add the call, and we're ok.

History
paddy@0 1 package devices
paddy@0 2
paddy@0 3 import (
paddy@0 4 "errors"
paddy@0 5 "log"
paddy@0 6 "time"
paddy@0 7
paddy@0 8 "golang.org/x/net/context"
paddy@0 9
paddy@0 10 "code.secondbit.org/uuid.hg"
paddy@0 11 )
paddy@0 12
paddy@0 13 var (
paddy@0 14 // ErrDeviceNotFound is returned when the specified device couldn't be found.
paddy@0 15 ErrDeviceNotFound = errors.New("device not found")
paddy@0 16 )
paddy@0 17
paddy@0 18 // Device represents a specific device that updates can be pushed to.
paddy@0 19 type Device struct {
paddy@0 20 ID uuid.ID
paddy@0 21 Name string
paddy@0 22 Owner uuid.ID
paddy@0 23 Type DeviceType
paddy@0 24 Created time.Time
paddy@0 25 LastSeen time.Time
paddy@0 26 PushToken string
paddy@0 27 }
paddy@0 28
paddy@0 29 // ApplyChange returns a Device that is a copy of the passed Device,
paddy@0 30 // but with the passed DeviceChange applied.
paddy@0 31 func ApplyChange(d Device, change DeviceChange) Device {
paddy@0 32 result := d
paddy@0 33 if change.Name != nil {
paddy@0 34 result.Name = *change.Name
paddy@0 35 }
paddy@0 36 if change.Owner != nil {
paddy@0 37 result.Owner = *change.Owner
paddy@0 38 } else {
paddy@0 39 // We don't want to accidentally leave a slice that
paddy@0 40 // is owned by both behind.
paddy@0 41 result.Owner = d.Owner.Copy()
paddy@0 42 }
paddy@0 43 if change.Type != nil {
paddy@0 44 result.Type = *change.Type
paddy@0 45 }
paddy@0 46 if change.Created != nil {
paddy@0 47 result.Created = *change.Created
paddy@0 48 }
paddy@0 49 if change.LastSeen != nil {
paddy@0 50 result.LastSeen = *change.LastSeen
paddy@0 51 }
paddy@0 52 if change.PushToken != nil {
paddy@0 53 result.PushToken = *change.PushToken
paddy@0 54 }
paddy@0 55 return result
paddy@0 56 }
paddy@0 57
paddy@0 58 // DeviceChange represents a set of changes to a Device that will be used
paddy@0 59 // to update a Device.
paddy@0 60 type DeviceChange struct {
paddy@0 61 DeviceID uuid.ID
paddy@0 62 Name *string
paddy@0 63 Owner *uuid.ID
paddy@0 64 Type *DeviceType
paddy@0 65 Created *time.Time
paddy@0 66 LastSeen *time.Time
paddy@0 67 PushToken *string
paddy@0 68 }
paddy@0 69
paddy@0 70 // Storer is an interface to control how data is stored in and retrieved from
paddy@0 71 // the datastore.
paddy@0 72 type Storer interface {
paddy@0 73 GetDevices(ids []uuid.ID, c context.Context) (map[string]Device, error)
paddy@0 74 UpdateDevice(change DeviceChange, c context.Context) error
paddy@0 75 DeleteDevices(ids []uuid.ID, c context.Context) error
paddy@0 76 CreateDevices(devices []Device, c context.Context) error
paddy@0 77 ListDevicesByOwner(user uuid.ID, c context.Context) ([]Device, error)
paddy@0 78
paddy@0 79 // These are used for testing only.
paddy@0 80 Factory(c context.Context) (Storer, error)
paddy@0 81 Destroy(c context.Context) error
paddy@0 82 }
paddy@0 83
paddy@0 84 // GetMany returns as many of the Devices specified by the passed IDs as possible.
paddy@0 85 // They are returned as a map, with the key being the string version of the ID.
paddy@0 86 // No error will be returned if a Device can't be found.
paddy@0 87 func GetMany(ids []uuid.ID, c context.Context) (map[string]Device, error) {
paddy@0 88 results := map[string]Device{}
paddy@0 89 storer, err := getStorer(c)
paddy@0 90 if err != nil {
paddy@0 91 log.Printf("Error retrieving Storer: %+v\n", err)
paddy@0 92 return results, err
paddy@0 93 }
paddy@0 94 results, err = storer.GetDevices(ids, c)
paddy@0 95 if err != nil {
paddy@0 96 log.Printf("Error retrieving Devices from %T: %+v\n", storer, err)
paddy@0 97 return results, err
paddy@0 98 }
paddy@0 99 return results, nil
paddy@0 100 }
paddy@0 101
paddy@0 102 // Get returns the Device specified by the passed ID. If the Device can't be found,
paddy@0 103 // an ErrDeviceNotFound error is returned.
paddy@0 104 func Get(id uuid.ID, c context.Context) (Device, error) {
paddy@0 105 results, err := GetMany([]uuid.ID{id}, c)
paddy@0 106 if err != nil {
paddy@0 107 return Device{}, err
paddy@0 108 }
paddy@0 109 result, ok := results[id.String()]
paddy@0 110 if !ok {
paddy@0 111 return Device{}, ErrDeviceNotFound
paddy@0 112 }
paddy@0 113 return result, nil
paddy@0 114 }
paddy@0 115
paddy@0 116 // Update applies the DeviceChange to the passed Device, and returns the result. If
paddy@0 117 // the Device can't be found, an ErrDeviceNotFound error was returned.
paddy@0 118 func Update(device Device, change DeviceChange, c context.Context) (Device, error) {
paddy@0 119 storer, err := getStorer(c)
paddy@0 120 if err != nil {
paddy@0 121 log.Printf("Error retrieving Storer: %+v\n", err)
paddy@0 122 return Device{}, err
paddy@0 123 }
paddy@0 124 change.DeviceID = device.ID
paddy@0 125 err = storer.UpdateDevice(change, c)
paddy@0 126 if err != nil {
paddy@0 127 return Device{}, err
paddy@0 128 }
paddy@0 129 return ApplyChange(device, change), nil
paddy@0 130 }
paddy@0 131
paddy@0 132 // DeleteMany removes the passed IDs from the datastore. No error is returned if the
paddy@0 133 // ID doesn't correspond to a Device in the datastore.
paddy@0 134 func DeleteMany(ids []uuid.ID, c context.Context) error {
paddy@0 135 storer, err := getStorer(c)
paddy@0 136 if err != nil {
paddy@0 137 log.Printf("Error retrieving Storer: %+v\n", err)
paddy@0 138 return err
paddy@0 139 }
paddy@0 140 return storer.DeleteDevices(ids, c)
paddy@0 141 }
paddy@0 142
paddy@0 143 // Delete removes the passed ID from the datastore. No error is returned if the ID doesn't
paddy@0 144 // correspond to a Device in the datastore.
paddy@0 145 func Delete(id uuid.ID, c context.Context) error {
paddy@0 146 return DeleteMany([]uuid.ID{id}, c)
paddy@0 147 }
paddy@0 148
paddy@0 149 // CreateMany stores the passed Devices in the datastore, assigning default values if
paddy@0 150 // necessary. The Devices that were ultimately stored (including any default values, if
paddy@0 151 // applicable) are returned.
paddy@0 152 func CreateMany(devices []Device, c context.Context) ([]Device, error) {
paddy@0 153 storer, err := getStorer(c)
paddy@0 154 if err != nil {
paddy@0 155 log.Printf("Error retrieving Storer: %+v\n", err)
paddy@0 156 return []Device{}, err
paddy@0 157 }
paddy@0 158 modified := make([]Device, 0, len(devices))
paddy@0 159 for _, device := range devices {
paddy@0 160 if device.ID.IsZero() {
paddy@0 161 device.ID = uuid.NewID()
paddy@0 162 }
paddy@0 163 if device.Created.IsZero() {
paddy@0 164 device.Created = time.Now()
paddy@0 165 }
paddy@0 166 if device.LastSeen.IsZero() {
paddy@0 167 device.LastSeen = time.Now()
paddy@0 168 }
paddy@0 169 modified = append(modified, device)
paddy@0 170 }
paddy@0 171 err = storer.CreateDevices(devices, c)
paddy@0 172 if err != nil {
paddy@0 173 return []Device{}, err
paddy@0 174 }
paddy@0 175 return modified, nil
paddy@0 176 }
paddy@0 177
paddy@0 178 // Create stores the passed Device in the datastore, assigning default values if
paddy@0 179 // necessary. The Devices that were ultimately stored (including any default values, if
paddy@0 180 // applicable) are returned.
paddy@0 181 func Create(device Device, c context.Context) (Device, error) {
paddy@0 182 devices, err := CreateMany([]Device{device}, c)
paddy@0 183 if err != nil {
paddy@0 184 return Device{}, err
paddy@0 185 }
paddy@0 186 // There should never be a case where we don't return a result.
paddy@0 187 // Ideally, we'd return an error here instead of letting the panic
paddy@0 188 // happen, but seeing as I can't come up with a reason the error would
paddy@0 189 // occur, I'm having trouble coming up with a reasonable error to return.
paddy@0 190 return devices[0], nil
paddy@0 191 }
paddy@0 192
paddy@0 193 // ListByOwner returns a slice of all the Devices with an Owner property that
paddy@0 194 // matches the passed ID. There's no guarantee on the order the Devices will be
paddy@0 195 // returned in.
paddy@0 196 func ListByOwner(user uuid.ID, c context.Context) ([]Device, error) {
paddy@0 197 // BUG(paddy): Eventually, we'll need to support paging for devices. But right now, I don't foresee any user creating enough of them to make pagination worthwhile.
paddy@0 198 storer, err := getStorer(c)
paddy@0 199 if err != nil {
paddy@0 200 log.Printf("Error retrieving Storer: %+v\n", err)
paddy@0 201 return []Device{}, err
paddy@0 202 }
paddy@0 203 devices, err := storer.ListDevicesByOwner(user, c)
paddy@0 204 return devices, err
paddy@0 205 }