-
Notifications
You must be signed in to change notification settings - Fork 155
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
devapp: put appengine code behind build flag
Per discussion on the mailing list we want to make this app run on more environments than App Engine and hopefully with a backing datastore that is just in-memory, or at the very least is not App Engine specific. Puts the datastore implementation detail behind an appengine build flag. Adds an in-memory datastore if you are not running on App Engine; it's slow since you have to fetch issues every time, but you can get all of the issues and browse them. Adds a single test so we have the most basic of protections against a regression. Add a cmd/devapp main package so we can run the server outside of an App Engine context. Rename gg.Percentile to gg.Quantile to match the change in the latest version of the downstream library. Change-Id: Icbdef29676ecbf7078b0fb8c3920f61df60a5e2e Reviewed-on: https://go-review.googlesource.com/34928 Reviewed-by: Brad Fitzpatrick <[email protected]>
- Loading branch information
1 parent
d559b30
commit 310c021
Showing
9 changed files
with
459 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright 2017 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// Devapp generates the dashboard that powers dev.golang.org. | ||
// | ||
// Usage: | ||
// | ||
// devapp --port=8081 | ||
// | ||
// By default devapp listens on port 8081. | ||
// | ||
// Github issues and Gerrit CL's are stored in memory in the running process. | ||
// To trigger an initial download, visit http://localhost:8081/update or | ||
// http://localhost:8081/update/stats in your browser. | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"log" | ||
"net" | ||
"net/http" | ||
"os" | ||
|
||
_ "golang.org/x/build/devapp" | ||
) | ||
|
||
func init() { | ||
flag.Usage = func() { | ||
os.Stderr.WriteString(`usage: devapp [-port=port] | ||
Devapp generates the dashboard that powers dev.golang.org. | ||
`) | ||
os.Exit(2) | ||
} | ||
} | ||
|
||
func main() { | ||
port := flag.Uint("port", 8081, "Port to listen on") | ||
flag.Parse() | ||
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
fmt.Fprintf(os.Stderr, "Listening on port %d\n", *port) | ||
log.Fatal(http.Serve(ln, nil)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// Copyright 2015 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// +build appengine | ||
|
||
package devapp | ||
|
||
import ( | ||
"net/http" | ||
"os" | ||
|
||
"appengine" | ||
|
||
"golang.org/x/build/godash" | ||
"golang.org/x/net/context" | ||
"google.golang.org/appengine/datastore" | ||
applog "google.golang.org/appengine/log" | ||
"google.golang.org/appengine/urlfetch" | ||
"google.golang.org/appengine/user" | ||
) | ||
|
||
func init() { | ||
onAppengine = !appengine.IsDevAppServer() | ||
log = &appengineLogger{} | ||
|
||
http.HandleFunc("/setToken", setTokenHandler) | ||
} | ||
|
||
type appengineLogger struct{} | ||
|
||
func (a *appengineLogger) Infof(ctx context.Context, format string, args ...interface{}) { | ||
applog.Infof(ctx, format, args...) | ||
} | ||
|
||
func (a *appengineLogger) Errorf(ctx context.Context, format string, args ...interface{}) { | ||
applog.Errorf(ctx, format, args...) | ||
} | ||
|
||
func (a *appengineLogger) Criticalf(ctx context.Context, format string, args ...interface{}) { | ||
applog.Criticalf(ctx, format, args...) | ||
} | ||
|
||
func newTransport(ctx context.Context) http.RoundTripper { | ||
return &urlfetch.Transport{Context: ctx} | ||
} | ||
|
||
func currentUserEmail(ctx context.Context) string { | ||
u := user.Current(ctx) | ||
if u == nil { | ||
return "" | ||
} | ||
return u.Email | ||
} | ||
|
||
// loginURL returns a URL that, when visited, prompts the user to sign in, | ||
// then redirects the user to the URL specified by dest. | ||
func loginURL(ctx context.Context, path string) (string, error) { | ||
return user.LoginURL(ctx, path) | ||
} | ||
|
||
func logoutURL(ctx context.Context, path string) (string, error) { | ||
return user.LogoutURL(ctx, path) | ||
} | ||
|
||
func loadData(ctx context.Context) (*godash.Data, error) { | ||
os.Stderr.WriteString("appengine load data") | ||
cache, err := getCache(ctx, "gzdata") | ||
if err != nil { | ||
return nil, err | ||
} | ||
return parseData(cache) | ||
} | ||
|
||
func newHTTPClient(ctx context.Context) *http.Client { | ||
return urlfetch.Client(ctx) | ||
} | ||
|
||
func getCache(ctx context.Context, name string) (*Cache, error) { | ||
var cache Cache | ||
if err := datastore.Get(ctx, datastore.NewKey(ctx, entityPrefix+"Cache", name, 0, nil), &cache); err != nil { | ||
return &cache, err | ||
} | ||
return &cache, nil | ||
} | ||
|
||
func getCaches(ctx context.Context, names ...string) map[string]*Cache { | ||
out := make(map[string]*Cache) | ||
var keys []*datastore.Key | ||
var ptrs []*Cache | ||
for _, name := range names { | ||
keys = append(keys, datastore.NewKey(ctx, entityPrefix+"Cache", name, 0, nil)) | ||
out[name] = &Cache{} | ||
ptrs = append(ptrs, out[name]) | ||
} | ||
datastore.GetMulti(ctx, keys, ptrs) // Ignore errors since they might not exist. | ||
return out | ||
} | ||
|
||
func getPage(ctx context.Context, page string) (*Page, error) { | ||
entity := new(Page) | ||
err := datastore.Get(ctx, datastore.NewKey(ctx, entityPrefix+"Page", page, 0, nil), entity) | ||
return entity, err | ||
} | ||
|
||
func writePage(ctx context.Context, page string, content []byte) error { | ||
entity := &Page{ | ||
Content: content, | ||
} | ||
_, err := datastore.Put(ctx, datastore.NewKey(ctx, entityPrefix+"Page", page, 0, nil), entity) | ||
return err | ||
} | ||
|
||
func putCache(ctx context.Context, name string, c *Cache) error { | ||
_, err := datastore.Put(ctx, datastore.NewKey(ctx, entityPrefix+"Cache", name, 0, nil), &c) | ||
return err | ||
} | ||
|
||
func getToken(ctx context.Context) (string, error) { | ||
cache, err := getCache(ctx, "github-token") | ||
if err != nil { | ||
return "", err | ||
} | ||
return cache.Value, nil | ||
} | ||
|
||
// Store a token in the database | ||
func setTokenHandler(w http.ResponseWriter, r *http.Request) { | ||
ctx := r.Context() | ||
r.ParseForm() | ||
if value := r.Form.Get("value"); value != "" { | ||
var token Cache | ||
token.Value = []byte(value) | ||
if err := putCache(ctx, "github-token", &token); err != nil { | ||
http.Error(w, err.Error(), 500) | ||
} | ||
} | ||
} | ||
|
||
func getContext(r *http.Request) context.Context { | ||
return appengine.NewContext(r) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.