ducky/devices

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

4:7bc6a84ac906 Go to Latest

ducky/devices/devices.go

Minimal Memstore implementation. Create an in-memory version of the Storer that meets the minimal requirements set by our Storer tests so far.

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