1
0
Fork 0

Improve request abstraction

This commit is contained in:
Gregory Eremin 2015-03-07 21:56:02 +07:00
parent 977f67e38a
commit 30b57ded50
7 changed files with 143 additions and 120 deletions

View File

@ -1,19 +1,10 @@
package db package db
import ( import (
"net/http"
"strconv"
"time" "time"
) )
type ( type (
StatRequest struct {
Org string
Team string
User string
From int64
To int64
}
StatItem struct { StatItem struct {
Item string `json:"item"` Item string `json:"item"`
Value int `json:"value"` Value int `json:"value"`
@ -110,54 +101,32 @@ where
group by item group by item
order by value desc` order by value desc`
func StatOrgReposTop(r *StatRequest) (res []StatItem) { func StatOrgReposTop(org string, from, to int64) (res []StatItem) {
defer measure("StatOrgReposTop", time.Now()) defer measure("StatOrgReposTop", time.Now())
mustSelect(&res, orgReposTopQuery, r.Org, r.From, r.To) mustSelect(&res, orgReposTopQuery, org, from, to)
return return
} }
func StatOrgReposActivity(r *StatRequest) (res []StatPoint) { func StatOrgReposActivity(org string, from, to int64) (res []StatPoint) {
defer measure("StatOrgReposActivity", time.Now()) defer measure("StatOrgReposActivity", time.Now())
mustSelect(&res, orgReposActivityQuery, r.Org, r.From, r.To) mustSelect(&res, orgReposActivityQuery, org, from, to)
return return
} }
func StatOrgTeamsTop(r *StatRequest) (res []StatItem) { func StatOrgTeamsTop(org string, from, to int64) (res []StatItem) {
defer measure("StatOrgTeamsTop", time.Now()) defer measure("StatOrgTeamsTop", time.Now())
mustSelect(&res, orgTeamsTopQuery, r.Org, r.From, r.To) mustSelect(&res, orgTeamsTopQuery, org, from, to)
return return
} }
func StatOrgTeamsActivity(r *StatRequest) (res []StatPoint) { func StatOrgTeamsActivity(org string, from, to int64) (res []StatPoint) {
defer measure("StatOrgTeamsActivity", time.Now()) defer measure("StatOrgTeamsActivity", time.Now())
mustSelect(&res, orgTeamsActivityQuery, r.Org, r.From, r.To) mustSelect(&res, orgTeamsActivityQuery, org, from, to)
return return
} }
func StatOrgUsersTop(r *StatRequest) (res []StatItem) { func StatOrgUsersTop(org string, from, to int64) (res []StatItem) {
defer measure("StatOrgUsersTop", time.Now()) defer measure("StatOrgUsersTop", time.Now())
mustSelect(&res, orgUsersTopQuery, r.Org, r.From, r.To) mustSelect(&res, orgUsersTopQuery, org, from, to)
return return
} }
func ParseRequest(r *http.Request) *StatRequest {
var err error
var from, to int64
if len(r.FormValue("from")) > 0 {
if from, err = strconv.ParseInt(r.FormValue("from"), 10, 64); err != nil {
panic(err)
}
}
if len(r.FormValue("to")) > 0 {
if to, err = strconv.ParseInt(r.FormValue("to"), 10, 64); err != nil {
panic(err)
}
}
return &StatRequest{
Org: r.FormValue("org"),
Team: r.FormValue("team"),
User: r.FormValue("user"),
From: from,
To: to,
}
}

View File

@ -7,17 +7,19 @@ import (
) )
func apiOrgsHandler(w http.ResponseWriter, r *http.Request) { func apiOrgsHandler(w http.ResponseWriter, r *http.Request) {
login := sessionUser(r) req, _ := parseRequest(w, r)
orgs := db.UserOrgs(login) orgs := db.UserOrgs(req.login)
respondWith(w, orgs) req.respondWith(orgs)
} }
func apiTeamsHandler(w http.ResponseWriter, r *http.Request) { func apiTeamsHandler(w http.ResponseWriter, r *http.Request) {
teams := db.OrgTeams(r.FormValue("org")) req, stat := parseRequest(w, r)
respondWith(w, teams) teams := db.OrgTeams(stat.org)
req.respondWith(teams)
} }
func apiReposHandler(w http.ResponseWriter, r *http.Request) { func apiReposHandler(w http.ResponseWriter, r *http.Request) {
repos := db.OrgRepos(r.FormValue("org")) req, stat := parseRequest(w, r)
respondWith(w, repos) repos := db.OrgRepos(stat.org)
req.respondWith(repos)
} }

View File

@ -18,6 +18,7 @@ func authSigninHandler(w http.ResponseWriter, r *http.Request) {
} }
func authCallbackHandler(w http.ResponseWriter, r *http.Request) { func authCallbackHandler(w http.ResponseWriter, r *http.Request) {
req, _ := parseRequest(w, r)
if r.FormValue("error") != "" { if r.FormValue("error") != "" {
w.Write([]byte(r.FormValue("error_description"))) w.Write([]byte(r.FormValue("error_description")))
return return
@ -25,15 +26,16 @@ func authCallbackHandler(w http.ResponseWriter, r *http.Request) {
code := r.FormValue("code") code := r.FormValue("code")
log.Printf("Got code %q\n", code) log.Printf("Got code %q\n", code)
if _, login, err := task.Authenticate(code); err == nil { if _, login, err := task.Authenticate(code); err == nil {
createSession(r, login) req.authorize(login)
} else { } else {
panic(err) panic(err)
} }
} }
func authHandler(w http.ResponseWriter, r *http.Request) { func authHandler(w http.ResponseWriter, r *http.Request) {
if sessionUser(r) == "" { if req, _ := parseRequest(w, r); req.login == "" {
http.Redirect(w, r, "/auth/hello", 302) http.Redirect(w, r, "/", 302)
} }
} }

92
server/request.go Normal file
View File

@ -0,0 +1,92 @@
package server
import (
"encoding/json"
"net/http"
"strconv"
"time"
"code.google.com/p/go-uuid/uuid"
"github.com/garyburd/redigo/redis"
)
type (
request struct {
r *http.Request
w http.ResponseWriter
sessionID string
login string
}
statRequest struct {
org string
team string
user string
from int64
to int64
}
)
func parseRequest(w http.ResponseWriter, r *http.Request) (*request, *statRequest) {
setCookie(w, r)
cook, _ := r.Cookie(cookieName)
login, _ := redis.String(redisPool.Get().Do("HGET", "sessions", cook.Value))
req := &request{
r: r,
w: w,
sessionID: cook.Value,
login: login,
}
return req, parseStatRequest(r)
}
func (r *request) authorize(login string) {
redisPool.Get().Do("HSET", "sessions", r.sessionID, login)
}
func (r *request) respondWith(resp interface{}) {
b, err := json.Marshal(resp)
if err != nil {
panic(err)
}
r.w.Header().Set("Access-Control-Allow-Origin", "*")
r.w.Header().Set("Content-Type", "application/json; charset=utf8")
r.w.Write(b)
}
func parseStatRequest(r *http.Request) *statRequest {
var err error
var from, to int64
if r.FormValue("from") != "" {
if from, err = strconv.ParseInt(r.FormValue("from"), 10, 64); err != nil {
panic(err)
}
}
if r.FormValue("to") != "" {
if to, err = strconv.ParseInt(r.FormValue("to"), 10, 64); err != nil {
panic(err)
}
}
return &statRequest{
org: r.FormValue("org"),
team: r.FormValue("team"),
user: r.FormValue("user"),
from: from,
to: to,
}
}
func setCookie(w http.ResponseWriter, r *http.Request) {
if cook, err := r.Cookie(cookieName); err != nil {
cook = &http.Cookie{
Name: cookieName,
Value: uuid.New(),
Path: "/",
Expires: time.Now().Add(365 * 24 * time.Hour),
HttpOnly: true,
}
http.SetCookie(w, cook)
r.AddCookie(cook)
}
}

View File

@ -1,13 +1,21 @@
package server package server
import ( import (
"encoding/json"
"log" "log"
"net/http" "net/http"
"github.com/garyburd/redigo/redis"
)
const (
cookieName = "session_id"
)
var (
redisPool = redis.NewPool(dialRedis, 10)
) )
func init() { func init() {
http.HandleFunc("/", sessionHandler)
http.HandleFunc("/auth/signin", authSigninHandler) http.HandleFunc("/auth/signin", authSigninHandler)
http.HandleFunc("/auth/callback", authCallbackHandler) http.HandleFunc("/auth/callback", authCallbackHandler)
http.HandleFunc("/api/", authHandler) http.HandleFunc("/api/", authHandler)
@ -26,13 +34,6 @@ func Start() {
http.ListenAndServe(":8080", nil) http.ListenAndServe(":8080", nil)
} }
func respondWith(w http.ResponseWriter, resp interface{}) { func dialRedis() (redis.Conn, error) {
b, err := json.Marshal(resp) return redis.Dial("tcp", ":6379")
if err != nil {
panic(err)
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Content-Type", "application/json; charset=utf8")
w.Write(b)
} }

View File

@ -1,48 +0,0 @@
package server
import (
"net/http"
"time"
"code.google.com/p/go-uuid/uuid"
"github.com/garyburd/redigo/redis"
)
const (
cookieName = "session_id"
)
var (
redisPool = redis.NewPool(dialRedis, 10)
)
func sessionHandler(w http.ResponseWriter, r *http.Request) {
if cook, err := r.Cookie(cookieName); err != nil {
cook = &http.Cookie{
Name: cookieName,
Value: uuid.New(),
Path: "/",
Expires: time.Now().Add(365 * 24 * time.Hour),
HttpOnly: true,
}
http.SetCookie(w, cook)
}
}
func createSession(r *http.Request, login string) {
redisPool.Get().Do("HSET", "sessions", sessionID(r), login)
}
func sessionID(r *http.Request) string {
cook, _ := r.Cookie(cookieName)
return cook.Value
}
func sessionUser(r *http.Request) (login string) {
login, _ = redis.String(redisPool.Get().Do("HGET", "sessions", sessionID(r)))
return
}
func dialRedis() (redis.Conn, error) {
return redis.Dial("tcp", ":6379")
}

View File

@ -7,26 +7,31 @@ import (
) )
func statOrgReposTop(w http.ResponseWriter, r *http.Request) { func statOrgReposTop(w http.ResponseWriter, r *http.Request) {
top := db.StatOrgReposTop(db.ParseRequest(r)) req, stat := parseRequest(w, r)
respondWith(w, top) top := db.StatOrgReposTop(stat.org, stat.from, stat.to)
req.respondWith(top)
} }
func statOrgReposActivity(w http.ResponseWriter, r *http.Request) { func statOrgReposActivity(w http.ResponseWriter, r *http.Request) {
activity := db.StatOrgReposActivity(db.ParseRequest(r)) req, stat := parseRequest(w, r)
respondWith(w, activity) activity := db.StatOrgReposActivity(stat.org, stat.from, stat.to)
req.respondWith(activity)
} }
func statOrgTeamsTop(w http.ResponseWriter, r *http.Request) { func statOrgTeamsTop(w http.ResponseWriter, r *http.Request) {
top := db.StatOrgTeamsTop(db.ParseRequest(r)) req, stat := parseRequest(w, r)
respondWith(w, top) top := db.StatOrgTeamsTop(stat.org, stat.from, stat.to)
req.respondWith(top)
} }
func statOrgTeamsActivity(w http.ResponseWriter, r *http.Request) { func statOrgTeamsActivity(w http.ResponseWriter, r *http.Request) {
activity := db.StatOrgTeamsActivity(db.ParseRequest(r)) req, stat := parseRequest(w, r)
respondWith(w, activity) activity := db.StatOrgTeamsActivity(stat.org, stat.from, stat.to)
req.respondWith(activity)
} }
func statOrgUsersTop(w http.ResponseWriter, r *http.Request) { func statOrgUsersTop(w http.ResponseWriter, r *http.Request) {
top := db.StatOrgUsersTop(db.ParseRequest(r)) req, stat := parseRequest(w, r)
respondWith(w, top) top := db.StatOrgUsersTop(stat.org, stat.from, stat.to)
req.respondWith(top)
} }