Don't use context.Background().
authorFlorian Forster <ff@octo.it>
Mon, 29 Jan 2018 09:53:30 +0000 (10:53 +0100)
committerFlorian Forster <ff@octo.it>
Mon, 29 Jan 2018 09:53:30 +0000 (10:53 +0100)
AppEngine panics when you use a non-AppEngine context. Instead, we need
to load the config at request time.

app/config.go
kraftakt.go

index f05a9e6..5b2d867 100644 (file)
@@ -2,6 +2,7 @@ package app
 
 import (
        "context"
+       "sync"
 
        "google.golang.org/appengine/datastore"
        "google.golang.org/appengine/log"
@@ -18,7 +19,16 @@ var Config struct {
        GoogleClientSecret string
 }
 
+var mu sync.Mutex
+
 func LoadConfig(ctx context.Context) error {
+       mu.Lock()
+       defer mu.Unlock()
+
+       if Config.ProjectNumber != "" {
+               return nil
+       }
+
        key := datastore.NewKey(ctx, "Config", "Production", 0, nil)
        if err := datastore.Get(ctx, key, &Config); err != nil {
                log.Errorf(ctx, `datastore.Get("Config", "Production") = %v`, err)
index 69815c2..66668a6 100644 (file)
@@ -28,10 +28,6 @@ func init() {
        http.HandleFunc("/google/setup", googleSetupHandler)
        http.Handle("/google/grant", AuthenticatedHandler(googleGrantHandler))
        http.Handle("/", AuthenticatedHandler(indexHandler))
-
-       if err := app.LoadConfig(context.Background()); err != nil {
-               panic(err)
-       }
 }
 
 // ContextHandler implements http.Handler
@@ -40,6 +36,11 @@ type ContextHandler func(context.Context, http.ResponseWriter, *http.Request) er
 func (hndl ContextHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        ctx := appengine.NewContext(r)
 
+       if err := app.LoadConfig(ctx); err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
        if err := hndl(ctx, w, r); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
@@ -51,6 +52,11 @@ type AuthenticatedHandler func(context.Context, http.ResponseWriter, *http.Reque
 func (hndl AuthenticatedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        ctx := appengine.NewContext(r)
 
+       if err := app.LoadConfig(ctx); err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
        gaeUser := user.Current(ctx)
        if gaeUser == nil {
                url, err := user.LoginURL(ctx, r.URL.String())
@@ -206,6 +212,10 @@ func fitbitNotifyHandler(ctx context.Context, w http.ResponseWriter, r *http.Req
 // handleNotifications parses fitbit notifications and requests the individual
 // activities from Fitbit. It is executed asynchronously via the delay package.
 func handleNotifications(ctx context.Context, payload []byte) error {
+       if err := app.LoadConfig(ctx); err != nil {
+               return err
+       }
+
        var subscriptions []fitbit.Subscription
        if err := json.Unmarshal(payload, &subscriptions); err != nil {
                return err