Add updating devices to apiv1.
We needed a way to be able to update devices after they were created. This is
supported in the devices package, we just needed to expose it using apiv1
endpoints.
In doing so, it became apparent that allowing users to change the Owner of their
Devices wasn't properly thought through, and pending a reason to use it, I'm
just removing it. The biggest issue came when trying to return usable error
messages; we couldn't distinguish between "you don't own the device you're
trying to update" and "you're not allowed to change the owner of the device". I
also couldn't figure out _who should be able to_ change the owner of the device,
which is generally an indication that I'm building a feature before I have a use
case for it.
To support this change, the apiv1.DeviceChange type needed its Owner property
removed.
I also needed to add deviceFromAPI and devicesFromAPI helpers to return
devices.Device types from apiv1.Device types.
There's now a new validateDeviceUpdate helper that checks to ensure that a
device update request is valid and the user has the appropriate permissions.
The createRequest type now accepts a slice of Devices, not a slice of
DeviceChanges, because we want to pass the Owner in.
A new updateRequest type is created, which accepts a DeviceChange to apply.
A new handleUpdateDevice handler is created, which is assigned to the endpoint
for PATCH requests against a device ID. It checks that the user is logged in,
the Device they're trying to update exists, and that it's a valid update. If all
of that is true, the device is updated and the updated device is returned.
Finally, we had to add two new scopes to support new functionality:
ScopeUpdateOtherUserDevices allows a user to update other user's devices, and
ScopeUpdateLastSeen allows a user to update the LastSeen property of a device.
Pending some better error messages, this should be a full implementation of
updating a device, which leaves only the deletion endpoint to deal with.
1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package context defines the Context type, which carries deadlines,
6 // cancelation signals, and other request-scoped values across API boundaries
7 // and between processes.
9 // Incoming requests to a server should create a Context, and outgoing calls to
10 // servers should accept a Context. The chain of function calls between must
11 // propagate the Context, optionally replacing it with a modified copy created
12 // using WithDeadline, WithTimeout, WithCancel, or WithValue.
14 // Programs that use Contexts should follow these rules to keep interfaces
15 // consistent across packages and enable static analysis tools to check context
18 // Do not store Contexts inside a struct type; instead, pass a Context
19 // explicitly to each function that needs it. The Context should be the first
20 // parameter, typically named ctx:
22 // func DoSomething(ctx context.Context, arg Arg) error {
26 // Do not pass a nil Context, even if a function permits it. Pass context.TODO
27 // if you are unsure about which Context to use.
29 // Use context Values only for request-scoped data that transits processes and
30 // APIs, not for passing optional parameters to functions.
32 // The same Context may be passed to functions running in different goroutines;
33 // Contexts are safe for simultaneous use by multiple goroutines.
35 // See http://blog.golang.org/context for example code for a server that uses
46 // A Context carries a deadline, a cancelation signal, and other values across
49 // Context's methods may be called by multiple goroutines simultaneously.
50 type Context interface {
51 // Deadline returns the time when work done on behalf of this context
52 // should be canceled. Deadline returns ok==false when no deadline is
53 // set. Successive calls to Deadline return the same results.
54 Deadline() (deadline time.Time, ok bool)
56 // Done returns a channel that's closed when work done on behalf of this
57 // context should be canceled. Done may return nil if this context can
58 // never be canceled. Successive calls to Done return the same value.
60 // WithCancel arranges for Done to be closed when cancel is called;
61 // WithDeadline arranges for Done to be closed when the deadline
62 // expires; WithTimeout arranges for Done to be closed when the timeout
65 // Done is provided for use in select statements:
67 // // Stream generates values with DoSomething and sends them to out
68 // // until DoSomething returns an error or ctx.Done is closed.
69 // func Stream(ctx context.Context, out <-chan Value) error {
71 // v, err := DoSomething(ctx)
83 // See http://blog.golang.org/pipelines for more examples of how to use
84 // a Done channel for cancelation.
85 Done() <-chan struct{}
87 // Err returns a non-nil error value after Done is closed. Err returns
88 // Canceled if the context was canceled or DeadlineExceeded if the
89 // context's deadline passed. No other values for Err are defined.
90 // After Done is closed, successive calls to Err return the same value.
93 // Value returns the value associated with this context for key, or nil
94 // if no value is associated with key. Successive calls to Value with
95 // the same key returns the same result.
97 // Use context values only for request-scoped data that transits
98 // processes and API boundaries, not for passing optional parameters to
101 // A key identifies a specific value in a Context. Functions that wish
102 // to store values in Context typically allocate a key in a global
103 // variable then use that key as the argument to context.WithValue and
104 // Context.Value. A key can be any type that supports equality;
105 // packages should define keys as an unexported type to avoid
108 // Packages that define a Context key should provide type-safe accessors
109 // for the values stores using that key:
111 // // Package user defines a User type that's stored in Contexts.
114 // import "golang.org/x/net/context"
116 // // User is the type of value stored in the Contexts.
117 // type User struct {...}
119 // // key is an unexported type for keys defined in this package.
120 // // This prevents collisions with keys defined in other packages.
123 // // userKey is the key for user.User values in Contexts. It is
124 // // unexported; clients use user.NewContext and user.FromContext
125 // // instead of using this key directly.
126 // var userKey key = 0
128 // // NewContext returns a new Context that carries value u.
129 // func NewContext(ctx context.Context, u *User) context.Context {
130 // return context.WithValue(ctx, userKey, u)
133 // // FromContext returns the User value stored in ctx, if any.
134 // func FromContext(ctx context.Context) (*User, bool) {
135 // u, ok := ctx.Value(userKey).(*User)
138 Value(key interface{}) interface{}
141 // Canceled is the error returned by Context.Err when the context is canceled.
142 var Canceled = errors.New("context canceled")
144 // DeadlineExceeded is the error returned by Context.Err when the context's
146 var DeadlineExceeded = errors.New("context deadline exceeded")
148 // An emptyCtx is never canceled, has no values, and has no deadline. It is not
149 // struct{}, since vars of this type must have distinct addresses.
152 func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
156 func (*emptyCtx) Done() <-chan struct{} {
160 func (*emptyCtx) Err() error {
164 func (*emptyCtx) Value(key interface{}) interface{} {
168 func (e *emptyCtx) String() string {
171 return "context.Background"
173 return "context.TODO"
175 return "unknown empty Context"
179 background = new(emptyCtx)
183 // Background returns a non-nil, empty Context. It is never canceled, has no
184 // values, and has no deadline. It is typically used by the main function,
185 // initialization, and tests, and as the top-level Context for incoming
187 func Background() Context {
191 // TODO returns a non-nil, empty Context. Code should use context.TODO when
192 // it's unclear which Context to use or it's is not yet available (because the
193 // surrounding function has not yet been extended to accept a Context
194 // parameter). TODO is recognized by static analysis tools that determine
195 // whether Contexts are propagated correctly in a program.
196 func TODO() Context {
200 // A CancelFunc tells an operation to abandon its work.
201 // A CancelFunc does not wait for the work to stop.
202 // After the first call, subsequent calls to a CancelFunc do nothing.
203 type CancelFunc func()
205 // WithCancel returns a copy of parent with a new Done channel. The returned
206 // context's Done channel is closed when the returned cancel function is called
207 // or when the parent context's Done channel is closed, whichever happens first.
209 // Canceling this context releases resources associated with it, so code should
210 // call cancel as soon as the operations running in this Context complete.
211 func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
212 c := newCancelCtx(parent)
213 propagateCancel(parent, &c)
214 return &c, func() { c.cancel(true, Canceled) }
217 // newCancelCtx returns an initialized cancelCtx.
218 func newCancelCtx(parent Context) cancelCtx {
221 done: make(chan struct{}),
225 // propagateCancel arranges for child to be canceled when parent is.
226 func propagateCancel(parent Context, child canceler) {
227 if parent.Done() == nil {
228 return // parent is never canceled
230 if p, ok := parentCancelCtx(parent); ok {
233 // parent has already been canceled
234 child.cancel(false, p.err)
236 if p.children == nil {
237 p.children = make(map[canceler]bool)
239 p.children[child] = true
245 case <-parent.Done():
246 child.cancel(false, parent.Err())
253 // parentCancelCtx follows a chain of parent references until it finds a
254 // *cancelCtx. This function understands how each of the concrete types in this
255 // package represents its parent.
256 func parentCancelCtx(parent Context) (*cancelCtx, bool) {
258 switch c := parent.(type) {
262 return &c.cancelCtx, true
271 // removeChild removes a context from its parent.
272 func removeChild(parent Context, child canceler) {
273 p, ok := parentCancelCtx(parent)
278 if p.children != nil {
279 delete(p.children, child)
284 // A canceler is a context type that can be canceled directly. The
285 // implementations are *cancelCtx and *timerCtx.
286 type canceler interface {
287 cancel(removeFromParent bool, err error)
288 Done() <-chan struct{}
291 // A cancelCtx can be canceled. When canceled, it also cancels any children
292 // that implement canceler.
293 type cancelCtx struct {
296 done chan struct{} // closed by the first cancel call.
299 children map[canceler]bool // set to nil by the first cancel call
300 err error // set to non-nil by the first cancel call
303 func (c *cancelCtx) Done() <-chan struct{} {
307 func (c *cancelCtx) Err() error {
313 func (c *cancelCtx) String() string {
314 return fmt.Sprintf("%v.WithCancel", c.Context)
317 // cancel closes c.done, cancels each of c's children, and, if
318 // removeFromParent is true, removes c from its parent's children.
319 func (c *cancelCtx) cancel(removeFromParent bool, err error) {
321 panic("context: internal error: missing cancel error")
326 return // already canceled
330 for child := range c.children {
331 // NOTE: acquiring the child's lock while holding parent's lock.
332 child.cancel(false, err)
337 if removeFromParent {
338 removeChild(c.Context, c)
342 // WithDeadline returns a copy of the parent context with the deadline adjusted
343 // to be no later than d. If the parent's deadline is already earlier than d,
344 // WithDeadline(parent, d) is semantically equivalent to parent. The returned
345 // context's Done channel is closed when the deadline expires, when the returned
346 // cancel function is called, or when the parent context's Done channel is
347 // closed, whichever happens first.
349 // Canceling this context releases resources associated with it, so code should
350 // call cancel as soon as the operations running in this Context complete.
351 func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
352 if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
353 // The current deadline is already sooner than the new one.
354 return WithCancel(parent)
357 cancelCtx: newCancelCtx(parent),
360 propagateCancel(parent, c)
361 d := deadline.Sub(time.Now())
363 c.cancel(true, DeadlineExceeded) // deadline has already passed
364 return c, func() { c.cancel(true, Canceled) }
369 c.timer = time.AfterFunc(d, func() {
370 c.cancel(true, DeadlineExceeded)
373 return c, func() { c.cancel(true, Canceled) }
376 // A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
377 // implement Done and Err. It implements cancel by stopping its timer then
378 // delegating to cancelCtx.cancel.
379 type timerCtx struct {
381 timer *time.Timer // Under cancelCtx.mu.
386 func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
387 return c.deadline, true
390 func (c *timerCtx) String() string {
391 return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
394 func (c *timerCtx) cancel(removeFromParent bool, err error) {
395 c.cancelCtx.cancel(false, err)
396 if removeFromParent {
397 // Remove this timerCtx from its parent cancelCtx's children.
398 removeChild(c.cancelCtx.Context, c)
408 // WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
410 // Canceling this context releases resources associated with it, so code should
411 // call cancel as soon as the operations running in this Context complete:
413 // func slowOperationWithTimeout(ctx context.Context) (Result, error) {
414 // ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
415 // defer cancel() // releases resources if slowOperation completes before timeout elapses
416 // return slowOperation(ctx)
418 func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
419 return WithDeadline(parent, time.Now().Add(timeout))
422 // WithValue returns a copy of parent in which the value associated with key is
425 // Use context Values only for request-scoped data that transits processes and
426 // APIs, not for passing optional parameters to functions.
427 func WithValue(parent Context, key interface{}, val interface{}) Context {
428 return &valueCtx{parent, key, val}
431 // A valueCtx carries a key-value pair. It implements Value for that key and
432 // delegates all other calls to the embedded Context.
433 type valueCtx struct {
438 func (c *valueCtx) String() string {
439 return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
442 func (c *valueCtx) Value(key interface{}) interface{} {
446 return c.Context.Value(key)