Add helper wrapper for handlers that require signed in state, to reduce boilerplate.

This commit is contained in:
Mihai Parparita 2014-11-28 15:00:39 -08:00
parent 852142b726
commit 16f16ae9d9
2 changed files with 98 additions and 91 deletions

View file

@ -15,6 +15,7 @@ import (
"appengine"
"github.com/google/go-github/github"
"github.com/gorilla/sessions"
)
const (
@ -32,6 +33,36 @@ type AppError struct {
Type int
}
type AppSignedInState struct {
Account *Account
GitHubClient *github.Client
session *sessions.Session
request *http.Request
responseWriter http.ResponseWriter
}
func (state *AppSignedInState) AddFlash(value interface{}) {
state.session.AddFlash(value)
state.saveSession()
}
func (state *AppSignedInState) Flashes() []interface{} {
flashes := state.session.Flashes()
if len(flashes) > 0 {
state.saveSession()
}
return flashes
}
func (state *AppSignedInState) ClearSession() {
state.session.Options.MaxAge = -1
state.saveSession()
}
func (state *AppSignedInState) saveSession() {
state.session.Save(state.request, state.responseWriter)
}
func GitHubFetchError(err error, fetchType string) *AppError {
return &AppError{
Error: err,
@ -84,6 +115,10 @@ func RedirectToRoute(routeName string) *AppError {
return RedirectToUrl(routeUrl.String())
}
func NotSignedIn() *AppError {
return RedirectToRoute("index")
}
type AppHandler func(http.ResponseWriter, *http.Request) *AppError
func (fn AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@ -92,6 +127,39 @@ func (fn AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
type SignedInAppHandler func(http.ResponseWriter, *http.Request, *AppSignedInState) *AppError
func (fn SignedInAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
session, _ := sessionStore.Get(r, sessionConfig.CookieName)
userId, ok := session.Values[sessionConfig.UserIdKey].(int)
if !ok {
handleAppError(NotSignedIn(), w, r)
return
}
c := appengine.NewContext(r)
account, err := getAccount(c, userId)
if account == nil || err != nil {
handleAppError(NotSignedIn(), w, r)
return
}
oauthTransport := githubOAuthTransport(c)
oauthTransport.Token = &account.OAuthToken
githubClient := github.NewClient(oauthTransport.Client())
state := &AppSignedInState{
Account: account,
GitHubClient: githubClient,
session: session,
responseWriter: w,
request: r,
}
if e := fn(w, r, state); e != nil {
handleAppError(e, w, r)
}
}
func handleAppError(e *AppError, w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
if e.Type == AppErrorTypeGitHubFetch {

View file

@ -46,14 +46,14 @@ func init() {
router.Handle("/session/sign-out", AppHandler(signOutHandler)).Name("sign-out").Methods("POST")
router.Handle("/github/callback", AppHandler(githubOAuthCallbackHandler))
router.Handle("/digest/view", AppHandler(viewDigestHandler)).Name("view-digest")
router.Handle("/digest/send", AppHandler(sendDigestHandler)).Name("send-digest").Methods("POST")
router.Handle("/digest/view", SignedInAppHandler(viewDigestHandler)).Name("view-digest")
router.Handle("/digest/send", SignedInAppHandler(sendDigestHandler)).Name("send-digest").Methods("POST")
router.Handle("/digest/cron", AppHandler(digestCronHandler))
router.Handle("/account/settings", AppHandler(settingsHandler)).Name("settings").Methods("GET")
router.Handle("/account/settings", AppHandler(saveSettingsHandler)).Name("save-settings").Methods("POST")
router.Handle("/account/set-initial-timezone", AppHandler(setInitialTimezoneHandler)).Name("set-initial-timezone").Methods("POST")
router.Handle("/account/delete", AppHandler(deleteAccountHandler)).Name("delete-account").Methods("POST")
router.Handle("/account/settings", SignedInAppHandler(settingsHandler)).Name("settings").Methods("GET")
router.Handle("/account/settings", SignedInAppHandler(saveSettingsHandler)).Name("save-settings").Methods("POST")
router.Handle("/account/set-initial-timezone", SignedInAppHandler(setInitialTimezoneHandler)).Name("set-initial-timezone").Methods("POST")
router.Handle("/account/delete", SignedInAppHandler(deleteAccountHandler)).Name("delete-account").Methods("POST")
router.Handle("/admin/users", AppHandler(usersAdminHandler))
router.Handle("/admin/digest", AppHandler(digestAdminHandler)).Name("digest-admin")
@ -183,20 +183,9 @@ func signOutHandler(w http.ResponseWriter, r *http.Request) *AppError {
return RedirectToRoute("index")
}
func viewDigestHandler(w http.ResponseWriter, r *http.Request) *AppError {
session, _ := sessionStore.Get(r, sessionConfig.CookieName)
userId := session.Values[sessionConfig.UserIdKey].(int)
func viewDigestHandler(w http.ResponseWriter, r *http.Request, state *AppSignedInState) *AppError {
c := appengine.NewContext(r)
account, err := getAccount(c, userId)
if err != nil {
return InternalError(err, "Could not look up account")
}
oauthTransport := githubOAuthTransport(c)
oauthTransport.Token = &account.OAuthToken
githubClient := github.NewClient(oauthTransport.Client())
digest, err := newDigest(c, githubClient, account)
digest, err := newDigest(c, state.GitHubClient, state.Account)
if err != nil {
return GitHubFetchError(err, "digest")
}
@ -206,26 +195,18 @@ func viewDigestHandler(w http.ResponseWriter, r *http.Request) *AppError {
return templates["digest-page"].Render(w, data)
}
func sendDigestHandler(w http.ResponseWriter, r *http.Request) *AppError {
session, _ := sessionStore.Get(r, sessionConfig.CookieName)
userId := session.Values[sessionConfig.UserIdKey].(int)
func sendDigestHandler(w http.ResponseWriter, r *http.Request, state *AppSignedInState) *AppError {
c := appengine.NewContext(r)
account, err := getAccount(c, userId)
if err != nil {
return InternalError(err, "Could not look up account")
}
sent, err := sendDigestForAccount(account, c)
sent, err := sendDigestForAccount(state.Account, c)
if err != nil {
return InternalError(err, "Could not send digest")
}
if sent {
session.AddFlash("Digest emailed!")
state.AddFlash("Digest emailed!")
} else {
session.AddFlash("No digest was sent, it was empty or disabled.")
state.AddFlash("No digest was sent, it was empty or disabled.")
}
session.Save(r, w)
return RedirectToRoute("index")
}
@ -346,31 +327,19 @@ func githubOAuthCallbackHandler(w http.ResponseWriter, r *http.Request) *AppErro
return RedirectToUrl(continueUrl)
}
func settingsHandler(w http.ResponseWriter, r *http.Request) *AppError {
session, _ := sessionStore.Get(r, sessionConfig.CookieName)
userId := session.Values[sessionConfig.UserIdKey].(int)
func settingsHandler(w http.ResponseWriter, r *http.Request, state *AppSignedInState) *AppError {
c := appengine.NewContext(r)
account, err := getAccount(c, userId)
if err != nil {
// TODO: redirect to sign in again
return InternalError(err, "Could not look up account")
}
oauthTransport := githubOAuthTransport(c)
oauthTransport.Token = &account.OAuthToken
githubClient := github.NewClient(oauthTransport.Client())
user, _, err := githubClient.Users.Get("")
user, _, err := state.GitHubClient.Users.Get("")
if err != nil {
return GitHubFetchError(err, "user")
}
repos, err := getRepos(c, githubClient, account, user)
repos, err := getRepos(c, state.GitHubClient, state.Account, user)
if err != nil {
return GitHubFetchError(err, "repositories")
}
emails, _, err := githubClient.Users.ListEmails(nil)
emails, _, err := state.GitHubClient.Users.ListEmails(nil)
if err != nil {
return GitHubFetchError(err, "emails")
}
@ -378,18 +347,15 @@ func settingsHandler(w http.ResponseWriter, r *http.Request) *AppError {
for i := range emails {
emailAddresses[i] = *emails[i].Email
}
accountEmailAddress, err := account.GetDigestEmailAddress(githubClient)
accountEmailAddress, err := state.Account.GetDigestEmailAddress(state.GitHubClient)
if err != nil {
return GitHubFetchError(err, "emails")
}
flashes := session.Flashes()
if len(flashes) > 0 {
session.Save(r, w)
}
flashes := state.Flashes()
var data = map[string]interface{}{
"Account": account,
"Account": state.Account,
"User": user,
"Timezones": timezones,
"Repos": repos,
@ -400,25 +366,16 @@ func settingsHandler(w http.ResponseWriter, r *http.Request) *AppError {
return templates["settings"].Render(w, data)
}
func saveSettingsHandler(w http.ResponseWriter, r *http.Request) *AppError {
session, _ := sessionStore.Get(r, sessionConfig.CookieName)
userId := session.Values[sessionConfig.UserIdKey].(int)
func saveSettingsHandler(w http.ResponseWriter, r *http.Request, state *AppSignedInState) *AppError {
c := appengine.NewContext(r)
account, err := getAccount(c, userId)
if err != nil {
// TODO: redirect to sign in again
return InternalError(err, "Could not look up account")
}
oauthTransport := githubOAuthTransport(c)
oauthTransport.Token = &account.OAuthToken
githubClient := github.NewClient(oauthTransport.Client())
account := state.Account
user, _, err := githubClient.Users.Get("")
user, _, err := state.GitHubClient.Users.Get("")
if err != nil {
return GitHubFetchError(err, "user")
}
repos, err := getRepos(c, githubClient, account, user)
repos, err := getRepos(c, state.GitHubClient, account, user)
if err != nil {
return GitHubFetchError(err, "repos")
}
@ -453,23 +410,16 @@ func saveSettingsHandler(w http.ResponseWriter, r *http.Request) *AppError {
return InternalError(err, "Could not save user")
}
session.AddFlash("Settings saved.")
session.Save(r, w)
state.AddFlash("Settings saved.")
return RedirectToRoute("settings")
}
func setInitialTimezoneHandler(w http.ResponseWriter, r *http.Request) *AppError {
session, _ := sessionStore.Get(r, sessionConfig.CookieName)
userId := session.Values[sessionConfig.UserIdKey].(int)
func setInitialTimezoneHandler(w http.ResponseWriter, r *http.Request, state *AppSignedInState) *AppError {
c := appengine.NewContext(r)
account, err := getAccount(c, userId)
if err != nil {
// TODO: redirect to sign in again
return InternalError(err, "Could not look up account")
}
account := state.Account
timezoneName := r.FormValue("timezone_name")
_, err = time.LoadLocation(timezoneName)
_, err := time.LoadLocation(timezoneName)
if err != nil {
return BadRequest(err, "Malformed timezone_name value")
}
@ -512,21 +462,10 @@ var cacheDigestForAccountFunc = delay.Func(
return nil
})
func deleteAccountHandler(w http.ResponseWriter, r *http.Request) *AppError {
session, _ := sessionStore.Get(r, sessionConfig.CookieName)
userId := session.Values[sessionConfig.UserIdKey].(int)
func deleteAccountHandler(w http.ResponseWriter, r *http.Request, state *AppSignedInState) *AppError {
c := appengine.NewContext(r)
account, err := getAccount(c, userId)
if err != nil {
// TODO: redirect to sign in again
return InternalError(err, "Could not look up account")
}
account.Delete(c)
session.Options.MaxAge = -1
session.Save(r, w)
// TODO: add a flash message saying that the account was deleted.
state.Account.Delete(c)
state.ClearSession()
return RedirectToRoute("index")
}