github.com/d0sbit/gocode@v0.0.0-20211001144653-a968ce917518/cmd/gocode_handlercrud/handlercrud.tmpl (about) 1 {{define "HandlerUtil"}} 2 import "net/http" 3 import "strings" 4 import "strconv" 5 import "encoding/json" 6 import "fmt" 7 import "time" 8 import "log" 9 import "errors" 10 import "runtime" 11 import mathrand "math/rand" 12 import "github.com/julienschmidt/httprouter" 13 14 const ( 15 Create = "create" 16 Read = "read" 17 Update = "update" 18 Delete = "delete" 19 ) 20 21 type Allower interface { 22 Allow(obj interface{}, perm string) error 23 } 24 25 type AllowerFunc func(obj interface{}, perm string) error 26 27 func (f AllowerFunc) Allow(obj interface{}, perm string) error { 28 return f(obj, perm) 29 } 30 31 func isJSONMimeType(mt string) bool { 32 mt = strings.TrimSpace(strings.SplitN(mt, ";", 2)[0]) 33 if strings.EqualFold("application/json", mt) { 34 return true 35 } 36 return false 37 } 38 39 func param(r *http.Request, names ...string) string { 40 p := httprouter.ParamsFromContext(r.Context()) 41 for _, name := range names { 42 value := p.ByName(name) 43 if value != "" { 44 return value 45 } 46 value = r.FormValue(name) 47 if value != "" { 48 return value 49 } 50 } 51 return "" 52 } 53 54 func scanParam(dst interface{}, paramVal string) (err error) { 55 56 // some common cases we can deal with simply 57 switch dstv := dst.(type) { 58 case *string: 59 *dstv = paramVal 60 return 61 case *int: 62 v, err := strconv.ParseInt(paramVal, 0, 0) 63 if err != nil { 64 return err 65 } 66 *dstv = int(v) 67 return nil 68 case *uint: 69 v, err := strconv.ParseUint(paramVal, 0, 0) 70 if err != nil { 71 return err 72 } 73 *dstv = uint(v) 74 return nil 75 case *int64: 76 *dstv, err = strconv.ParseInt(paramVal, 0, 64) 77 return nil 78 case *uint64: 79 *dstv, err = strconv.ParseUint(paramVal, 0, 64) 80 return nil 81 case *int32: 82 v, err := strconv.ParseInt(paramVal, 0, 32) 83 if err != nil { 84 return err 85 } 86 *dstv = int32(v) 87 return nil 88 case *uint32: 89 v, err := strconv.ParseUint(paramVal, 0, 32) 90 if err != nil { 91 return err 92 } 93 *dstv = uint32(v) 94 return nil 95 } 96 97 // or delegate to a JSON unmarshal func if available 98 if j, ok := dst.(json.Unmarshaler); ok { 99 // TODO: read json.Marshal source to see if it's worth adding a fast path for strings that don't need escaping 100 b, err := json.Marshal(paramVal) 101 if err != nil { 102 return err 103 } 104 return j.UnmarshalJSON(b) 105 } 106 107 return fmt.Errorf("don't know how to scan into %T", dst) 108 } 109 110 type httpStatusCoder interface { 111 HTTPStatusCode() int 112 } 113 114 var rnd = mathrand.New(mathrand.NewSource(time.Now().UnixNano())) 115 116 type printfer interface { 117 Printf(format string, v ...interface{}) 118 } 119 120 var logger printfer = log.Default() 121 122 func writeErrf(w http.ResponseWriter, status int, err error, responseTextFormat string, args ...interface{}) { 123 124 // look for a httpStatusCoder or default to status 500 (internal server error) 125 sterr := err 126 for status <= 0 && sterr != nil { 127 s, ok := sterr.(httpStatusCoder) 128 if ok { 129 status = s.HTTPStatusCode() 130 break 131 } 132 sterr = errors.Unwrap(sterr) 133 } 134 if status <= 0 { 135 status = 500 136 } 137 138 errID := fmt.Sprintf("%016x", rnd.Uint64()) 139 140 w.Header().Set("X-Error-Id", errID) 141 w.Header().Set("Content-Type", "text/html; charset=utf-8") 142 143 w.WriteHeader(status) 144 145 var responseMessage string 146 if responseTextFormat != "" { 147 responseMessage = fmt.Sprintf(responseTextFormat, args...) 148 } 149 if responseMessage == "" { 150 responseMessage = http.StatusText(status) 151 } 152 if responseMessage == "" { 153 responseMessage = "unknown error" 154 } 155 156 fmt.Fprint(w, "<h1>") 157 fmt.Fprint(w, responseMessage) 158 fmt.Fprint(w, "</h1><p>") 159 fmt.Fprintf(w, "\n\nError ID: %s\n", errID) 160 161 if logger == nil { 162 return 163 } 164 165 _, file, line, _ := runtime.Caller(1) 166 167 logger.Printf("HTTP handler error ID %s at %s:%d: %v; message: %s\n", errID, file, line, err, responseMessage) 168 } 169 170 func writeErr(w http.ResponseWriter, status int, err error) { 171 writeErrf(w, status, err, "") 172 } 173 {{end}} 174 175 {{define "Handler"}} 176 {{if .StoreImportPath}} 177 import store "{{.StoreImportPath}}" 178 {{end}} 179 180 type {{$.Struct.LocalName}}Handler struct { 181 Allower 182 Store *store.{{$.Struct.LocalName}}Store 183 } 184 {{end}} 185 186 {{define "HandlerMethods"}} 187 import "net/http" 188 import "encoding/json" 189 {{if .StoreImportPath}} 190 import store "{{.StoreImportPath}}" 191 {{end}} 192 193 194 func (h *{{$.Struct.LocalName}}Handler) GetByID(w http.ResponseWriter, r *http.Request) { 195 {{$idf := index $.Struct.FieldList.PK 0}} 196 var err error 197 var in store.{{$.Struct.LocalName}} 198 idParam := param(r, "id") 199 err = scanParam(&in.{{$idf.GoName}}, idParam) 200 if err != nil { 201 writeErrf(w, 400, err, "invalid ID %q", idParam) 202 return 203 } 204 id := in.{{$idf.GoName}} 205 err = h.Allow(&in, Read) 206 if err != nil { 207 writeErrf(w, 403, err, "access not allowed to record with ID %v", id) 208 return 209 } 210 ret, err := h.Store.SelectByID(r.Context(), id) 211 if err != nil { 212 writeErrf(w, 0, err, "failed to load record with ID %v", id) 213 return 214 } 215 err = json.NewEncoder(w).Encode(ret) 216 if err != nil { 217 writeErrf(w, 0, err, "json encoding error") 218 return 219 } 220 } 221 222 /* 223 func (h *XYZHandler) Post(w http.ResponseWriter, r *http.Request) { 224 } 225 226 func (h *XYZHandler) Patch(w http.ResponseWriter, r *http.Request) { 227 } 228 229 func (h *XYZHandler) Delete(w http.ResponseWriter, r *http.Request) { 230 } 231 232 func (h *XYZHandler) Select(w http.ResponseWriter, r *http.Request) { 233 } 234 235 func (h *XYZHandler) SelectCursor(w http.ResponseWriter, r *http.Request) { 236 } 237 */ 238 {{end}} 239 240 {{define "TestHandler"}} 241 {{end}}