+package app
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/google/uuid"
+ legacy_context "golang.org/x/net/context"
+ "golang.org/x/oauth2"
+ "google.golang.org/appengine/datastore"
+)
+
+type User struct {
+ key *datastore.Key
+}
+
+type dbUser struct {
+ ID string
+}
+
+func NewUser(ctx context.Context, email string) (*User, error) {
+ err := datastore.RunInTransaction(ctx, func(ctx legacy_context.Context) error {
+ key := datastore.NewKey(ctx, "User", email, 0, nil)
+ if err := datastore.Get(ctx, key, &dbUser{}); err != datastore.ErrNoSuchEntity {
+ return err // may be nil
+ }
+
+ _, err := datastore.Put(ctx, key, &dbUser{
+ ID: uuid.New().String(),
+ })
+ return err
+ }, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return &User{
+ key: datastore.NewKey(ctx, "User", email, 0, nil),
+ }, nil
+}
+
+func UserByID(ctx context.Context, id string) (*User, error) {
+ q := datastore.NewQuery("User").Filter("ID=", id).KeysOnly()
+ keys, err := q.GetAll(ctx, nil)
+ if err != nil {
+ return nil, fmt.Errorf("datastore.Query.GetAll(): %v", err)
+ }
+ if len(keys) != 1 {
+ return nil, fmt.Errorf("len(keys) = %d, want 1", len(keys))
+ }
+
+ return &User{
+ key: keys[0],
+ }, nil
+}
+
+func (u *User) ID(ctx context.Context) (string, error) {
+ var db dbUser
+ if err := datastore.Get(ctx, u.key, &db); err != nil {
+ return "", err
+ }
+
+ return db.ID, nil
+}
+
+func (u *User) Token(ctx context.Context, svc string) (*oauth2.Token, error) {
+ key := datastore.NewKey(ctx, "Token", svc, 0, u.key)
+
+ var tok oauth2.Token
+ if err := datastore.Get(ctx, key, &tok); err != nil {
+ return nil, err
+ }
+
+ return &tok, nil
+}
+
+func (u *User) SetToken(ctx context.Context, svc string, tok *oauth2.Token) error {
+ key := datastore.NewKey(ctx, "Token", "Fitbit", 0, u.key)
+ _, err := datastore.Put(ctx, key, tok)
+ return err
+}