import (
"context"
+ "sync"
"google.golang.org/appengine/datastore"
"google.golang.org/appengine/log"
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)
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
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
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())
// 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