github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2013/highperf/mart/3/mart.go (about) 1 // +build OMIT 2 3 package mart 4 5 import ( 6 "bytes" 7 "fmt" 8 "math/rand" 9 "net/http" 10 "strconv" 11 "strings" 12 13 "appengine" 14 "appengine/datastore" 15 "appengine/delay" 16 "appengine/mail" 17 "appengine/user" 18 19 "github.com/mjibson/appstats" 20 ) 21 22 func init() { 23 http.HandleFunc("/", front) 24 http.Handle("/checkout", appstats.NewHandler(checkout)) 25 http.HandleFunc("/admin/populate", adminPopulate) 26 } 27 28 func front(w http.ResponseWriter, r *http.Request) { 29 if r.URL.Path != "/" { 30 http.NotFound(w, r) 31 return 32 } 33 fmt.Fprintf(w, "Hello, welcome to Gopher Mart!") 34 } 35 36 const ( 37 numItems = 100 // number of different items for sale 38 39 appAdmin = "noreply@google.com" // an admin of this app, for sending mail 40 ) 41 42 // Item represents an item for sale in Gopher Mart. 43 type Item struct { 44 Name string 45 Price float64 46 } 47 48 func itemKey(c appengine.Context, i int) *datastore.Key { 49 return datastore.NewKey(c, "Item", fmt.Sprintf("item%04d", i), 0, nil) 50 } 51 52 func checkout(c appengine.Context, w http.ResponseWriter, r *http.Request) { 53 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 54 num, err := strconv.Atoi(r.FormValue("num")) 55 if err == nil && (num < 1 || num > 30) { 56 err = fmt.Errorf("%d out of range [1,30]", num) 57 } 58 if err != nil { 59 http.Error(w, fmt.Sprintf("bad number of items: %v", err), http.StatusBadRequest) 60 return 61 } 62 63 // Pick random items. 64 keys := make([]*datastore.Key, num) 65 for i := range keys { 66 keys[i] = itemKey(c, rand.Intn(numItems)) 67 } 68 69 // Batch get 70 items := make([]Item, len(keys)) 71 if err := datastore.GetMulti(c, keys, items); err != nil { 72 // ... 73 http.Error(w, fmt.Sprintf("datastore.GetMulti: %v", err), http.StatusBadRequest) // OMIT 74 return // OMIT 75 } 76 77 // Print items. 78 var b bytes.Buffer 79 fmt.Fprintf(&b, "Here's what you bought:\n") 80 var sum float64 81 for _, item := range items { 82 fmt.Fprintf(&b, "\t%s", item.Name) 83 fmt.Fprint(&b, strings.Repeat("\t", (40-len(item.Name)+7)/8)) 84 fmt.Fprintf(&b, "$%5.2f\n", item.Price) 85 sum += item.Price 86 } 87 fmt.Fprintln(&b, strings.Repeat("-", 55)) 88 fmt.Fprintf(&b, "\tTotal:\t\t\t\t\t$%.2f\n", sum) 89 90 w.Write(b.Bytes()) 91 92 sendReceipt.Call(c, user.Current(c).Email, b.String()) 93 } 94 95 var sendReceipt = delay.Func("send-receipt", func(c appengine.Context, dst, body string) { 96 msg := &mail.Message{ 97 Sender: appAdmin, 98 To: []string{dst}, 99 Subject: "Your Gopher Mart receipt", 100 Body: body, 101 } 102 if err := mail.Send(c, msg); err != nil { 103 c.Errorf("mail.Send: %v", err) 104 } 105 }) 106 107 func adminPopulate(w http.ResponseWriter, r *http.Request) { 108 c := appengine.NewContext(r) 109 for i := range [numItems]struct{}{} { // r hates this. tee hee. 110 key := itemKey(c, i) 111 good := goods[rand.Intn(len(goods))] 112 item := &Item{ 113 // TODO: vary names more 114 Name: fmt.Sprintf("%s %dg", good.name, i+1), 115 Price: float64(rand.Intn(1999)+1) / 100, 116 } 117 if _, err := datastore.Put(c, key, item); err != nil { 118 http.Error(w, err.Error(), 500) 119 return 120 } 121 } 122 fmt.Fprintf(w, "ok. %d items populated.", numItems) 123 } 124 125 var goods = [...]struct { 126 name string 127 }{ 128 {"Gopher Bran"}, 129 {"Gopher Flakes"}, 130 {"Gopher Grease"}, 131 {"Gopher Litter"}, 132 }