api
api/api.go
Switch order of arguments. Let's match Google; we'll put the context.Contexts as the first arguments in the Handlers.
1 package api
3 import (
4 "encoding/json"
5 "errors"
6 "log"
7 "net/http"
8 "strings"
10 "bitbucket.org/ww/goautoneg"
12 "code.secondbit.org/uuid.hg"
14 "golang.org/x/net/context"
15 )
17 const (
18 RequestErrAccessDenied = "access_denied"
19 RequestErrInsufficient = "insufficient"
20 RequestErrOverflow = "overflow"
21 RequestErrInvalidValue = "invalid_value"
22 RequestErrInvalidFormat = "invalid_format"
23 RequestErrMissing = "missing"
24 RequestErrNotFound = "not_found"
25 RequestErrConflict = "conflict"
26 RequestErrActOfGod = "act_of_god"
27 )
29 var (
30 ActOfGodError = []RequestError{{Slug: RequestErrActOfGod}}
31 InvalidFormatError = []RequestError{{Slug: RequestErrInvalidFormat, Field: "/"}}
33 Encoders = []string{"application/json"}
35 ErrUserIDNotSet = errors.New("user ID not set")
36 )
38 type RequestError struct {
39 Slug string `json:"error,omitempty"`
40 Field string `json:"field,omitempty"`
41 Param string `json:"param,omitempty"`
42 Header string `json:"header,omitempty"`
43 }
45 type ContextHandler func(context.Context, http.ResponseWriter, *http.Request)
47 func NegotiateMiddleware(h http.Handler) http.Handler {
48 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
49 if r.Header.Get("Accept") != "" {
50 contentType := goautoneg.Negotiate(r.Header.Get("Accept"), Encoders)
51 if contentType == "" {
52 w.WriteHeader(http.StatusNotAcceptable)
53 w.Write([]byte("Unsupported content type requested: " + r.Header.Get("Accept")))
54 return
55 }
56 }
57 h.ServeHTTP(w, r)
58 })
59 }
61 func Encode(w http.ResponseWriter, r *http.Request, status int, resp interface{}) {
62 contentType := goautoneg.Negotiate(r.Header.Get("Accept"), Encoders)
63 w.Header().Set("content-type", contentType)
64 w.WriteHeader(status)
65 var err error
66 switch contentType {
67 case "application/json":
68 enc := json.NewEncoder(w)
69 err = enc.Encode(resp)
70 default:
71 enc := json.NewEncoder(w)
72 err = enc.Encode(resp)
73 }
74 if err != nil {
75 log.Println(err)
76 }
77 }
79 func Decode(r *http.Request, target interface{}) error {
80 defer r.Body.Close()
81 switch r.Header.Get("Content-Type") {
82 case "application/json":
83 dec := json.NewDecoder(r.Body)
84 return dec.Decode(target)
85 default:
86 dec := json.NewDecoder(r.Body)
87 return dec.Decode(target)
88 }
89 }
91 func CORSMiddleware(h http.Handler) http.Handler {
92 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
93 w.Header().Set("Access-Control-Allow-Origin", "*")
94 w.Header().Set("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers"))
95 w.Header().Set("Access-Control-Allow-Credentials", "true")
96 if strings.ToLower(r.Method) == "options" {
97 methods := strings.Join(r.Header[http.CanonicalHeaderKey("Trout-Methods")], ", ")
98 w.Header().Set("Access-Control-Allow-Methods", methods)
99 w.Header().Set("Allow", methods)
100 w.WriteHeader(http.StatusOK)
101 return
102 }
103 h.ServeHTTP(w, r)
104 })
105 }
107 func ContextWrapper(c context.Context, handler ContextHandler) http.Handler {
108 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
109 handler(c, w, r)
110 })
111 }
113 func CheckScopes(r *http.Request, scopes ...string) bool {
114 passedStr := r.Header.Get("scopes")
115 passed := strings.Split(passedStr, " ")
116 for _, scope := range scopes {
117 var found bool
118 for _, p := range passed {
119 if scope == strings.TrimSpace(p) {
120 found = true
121 break
122 }
123 }
124 if !found {
125 return false
126 }
127 }
128 return true
129 }
131 func AuthUser(r *http.Request) (uuid.ID, error) {
132 rawID := r.Header.Get("User-ID")
133 if rawID == "" {
134 return nil, ErrUserIDNotSet
135 }
136 return uuid.Parse(rawID)
137 }