Read runtime configuration from datastore.
[kraftakt.git] / app / user.go
index 86c033c..d5b8452 100644 (file)
@@ -3,15 +3,19 @@ package app
 import (
        "context"
        "fmt"
+       "net/http"
+       "sync"
 
        "github.com/google/uuid"
        legacy_context "golang.org/x/net/context"
        "golang.org/x/oauth2"
        "google.golang.org/appengine/datastore"
+       "google.golang.org/appengine/log"
 )
 
 type User struct {
-       key *datastore.Key
+       Email string
+       key   *datastore.Key
 }
 
 type dbUser struct {
@@ -35,7 +39,8 @@ func NewUser(ctx context.Context, email string) (*User, error) {
        }
 
        return &User{
-               key: datastore.NewKey(ctx, "User", email, 0, nil),
+               Email: email,
+               key:   datastore.NewKey(ctx, "User", email, 0, nil),
        }, nil
 }
 
@@ -50,7 +55,8 @@ func UserByID(ctx context.Context, id string) (*User, error) {
        }
 
        return &User{
-               key: keys[0],
+               Email: keys[0].StringID(),
+               key:   keys[0],
        }, nil
 }
 
@@ -75,7 +81,55 @@ func (u *User) Token(ctx context.Context, svc string) (*oauth2.Token, error) {
 }
 
 func (u *User) SetToken(ctx context.Context, svc string, tok *oauth2.Token) error {
-       key := datastore.NewKey(ctx, "Token", "Fitbit", 0, u.key)
+       key := datastore.NewKey(ctx, "Token", svc, 0, u.key)
        _, err := datastore.Put(ctx, key, tok)
        return err
 }
+
+func (u *User) OAuthClient(ctx context.Context, svc string, cfg *oauth2.Config) (*http.Client, 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
+       }
+
+       src := cfg.TokenSource(ctx, &tok)
+       return oauth2.NewClient(ctx, &persistingTokenSource{
+               ctx: ctx,
+               t:   &tok,
+               src: src,
+               key: key,
+       }), nil
+}
+
+type persistingTokenSource struct {
+       ctx context.Context
+       t   *oauth2.Token
+       src oauth2.TokenSource
+       key *datastore.Key
+
+       sync.Mutex
+}
+
+func (s *persistingTokenSource) Token() (*oauth2.Token, error) {
+       s.Lock()
+       defer s.Unlock()
+
+       tok, err := s.src.Token()
+       if err != nil {
+               return nil, err
+       }
+
+       if s.t.AccessToken != tok.AccessToken ||
+               s.t.TokenType != tok.TokenType ||
+               s.t.RefreshToken != tok.RefreshToken ||
+               !s.t.Expiry.Equal(tok.Expiry) {
+               if _, err := datastore.Put(s.ctx, s.key, tok); err != nil {
+                       log.Errorf(s.ctx, "persisting OAuth token in datastore failed: %v", err)
+               }
+       }
+
+       s.t = tok
+       return tok, nil
+}