| OLD | NEW |
| 1 package frontend | 1 package frontend |
| 2 | 2 |
| 3 import ( | 3 import ( |
| 4 "bytes" | 4 "bytes" |
| 5 "encoding/json" | 5 "encoding/json" |
| 6 "fmt" | 6 "fmt" |
| 7 "infra/appengine/test-results/model" | 7 "infra/appengine/test-results/model" |
| 8 "io" | 8 "io" |
| 9 "net/http" | 9 "net/http" |
| 10 "regexp" | 10 "regexp" |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 httpTimeFormat = time.RFC1123 | 28 httpTimeFormat = time.RFC1123 |
| 29 | 29 |
| 30 // httpNoTZTimeFormat is httpTimeFormat with the timezone removed. | 30 // httpNoTZTimeFormat is httpTimeFormat with the timezone removed. |
| 31 httpNoTZTimeFormat = "Mon, 02 Jan 2006 15:04:05" | 31 httpNoTZTimeFormat = "Mon, 02 Jan 2006 15:04:05" |
| 32 ) | 32 ) |
| 33 | 33 |
| 34 // callbackNameRx matches start of strings that look like | 34 // callbackNameRx matches start of strings that look like |
| 35 // JavaScript function names. Not a comprehensive solution. | 35 // JavaScript function names. Not a comprehensive solution. |
| 36 var callbackNameRx = regexp.MustCompile(`^[A-Za-z0-9_]+$`) | 36 var callbackNameRx = regexp.MustCompile(`^[A-Za-z0-9_]+$`) |
| 37 | 37 |
| 38 // GetHandler is the HTTP handler for GET /testfile requests. | 38 // getHandler is the HTTP handler for GET /testfile requests. |
| 39 func GetHandler(ctx *router.Context) { | 39 func getHandler(ctx *router.Context) { |
| 40 c, w, r := ctx.Context, ctx.Writer, ctx.Request | 40 c, w, r := ctx.Context, ctx.Writer, ctx.Request |
| 41 if err := r.ParseForm(); err != nil { | 41 if err := r.ParseForm(); err != nil { |
| 42 http.Error(w, err.Error(), http.StatusInternalServerError) | 42 http.Error(w, err.Error(), http.StatusInternalServerError) |
| 43 logging.Errorf(c, "failed to parse form: %v", err) | 43 logging.Errorf(c, "failed to parse form: %v", err) |
| 44 return | 44 return |
| 45 } | 45 } |
| 46 | 46 |
| 47 params, err := NewURLParams(r.Form) | 47 params, err := NewURLParams(r.Form) |
| 48 if err != nil { | 48 if err != nil { |
| 49 e := fmt.Sprintf("failed to parse URL parameters: %+v: %v", para
ms, err) | 49 e := fmt.Sprintf("failed to parse URL parameters: %+v: %v", para
ms, err) |
| (...skipping 18 matching lines...) Expand all Loading... |
| 68 | 68 |
| 69 key, err := datastore.NewKeyEncoded(params.Key) | 69 key, err := datastore.NewKeyEncoded(params.Key) |
| 70 if err != nil { | 70 if err != nil { |
| 71 http.Error(w, err.Error(), http.StatusBadRequest) | 71 http.Error(w, err.Error(), http.StatusBadRequest) |
| 72 logging.Errorf(c, "failed to encode key: %v: %v", key, err) | 72 logging.Errorf(c, "failed to encode key: %v: %v", key, err) |
| 73 return | 73 return |
| 74 } | 74 } |
| 75 | 75 |
| 76 tf := model.TestFile{ID: key.IntID()} | 76 tf := model.TestFile{ID: key.IntID()} |
| 77 | 77 |
| 78 » if err := datastore.Get(c).Get(&tf); err == datastore.ErrNoSuchEntity { | 78 » if err := datastore.Get(c).Get(&tf); err != nil { |
| 79 » » http.Error(w, err.Error(), http.StatusNotFound) | 79 » » if err == datastore.ErrNoSuchEntity { |
| 80 » » logging.Errorf(c, "TestFile with ID %v not found: %v", key.IntID
(), err) | 80 » » » http.Error(w, err.Error(), http.StatusNotFound) |
| 81 » » return | 81 » » » logging.Errorf(c, "TestFile with ID %v not found: %v", k
ey.IntID(), err) |
| 82 » } else if err != nil { | 82 » » » return |
| 83 » » } |
| 83 http.Error(w, err.Error(), http.StatusInternalServerError) | 84 http.Error(w, err.Error(), http.StatusInternalServerError) |
| 84 logging.Errorf(c, "failed to get TestFile with ID %v: %v", key.I
ntID(), err) | 85 logging.Errorf(c, "failed to get TestFile with ID %v: %v", key.I
ntID(), err) |
| 85 return | 86 return |
| 86 } | 87 } |
| 87 | 88 |
| 88 modTime, err := time.Parse(r.Header.Get("If-Modified-Since"), httpTimeFo
rmat) | 89 modTime, err := time.Parse(r.Header.Get("If-Modified-Since"), httpTimeFo
rmat) |
| 89 if err == nil && !tf.LastMod.After(modTime) { | 90 if err == nil && !tf.LastMod.After(modTime) { |
| 90 w.WriteHeader(http.StatusNotModified) | 91 w.WriteHeader(http.StatusNotModified) |
| 91 return | 92 return |
| 92 } | 93 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 108 logging.Errorf(c, "GetAll failed for query: %+v: %v", q, err) | 109 logging.Errorf(c, "GetAll failed for query: %+v: %v", q, err) |
| 109 return | 110 return |
| 110 } | 111 } |
| 111 if len(testFiles) == 0 { | 112 if len(testFiles) == 0 { |
| 112 e := fmt.Sprintf("no TestFile found for query: %+v", q) | 113 e := fmt.Sprintf("no TestFile found for query: %+v", q) |
| 113 http.Error(w, e, http.StatusNotFound) | 114 http.Error(w, e, http.StatusNotFound) |
| 114 logging.Errorf(c, e) | 115 logging.Errorf(c, e) |
| 115 return | 116 return |
| 116 } | 117 } |
| 117 | 118 |
| 118 args := templates.Args{ | |
| 119 "Master": params.Master, | |
| 120 "Builder": params.Builder, | |
| 121 "TestType": params.TestType, | |
| 122 "BuildNumber": params.BuildNumber, | |
| 123 "Name": params.Name, | |
| 124 "Files": testFiles, | |
| 125 } | |
| 126 | |
| 127 if params.Callback != "" { | 119 if params.Callback != "" { |
| 128 b, err := keysJSON(c, testFiles) | 120 b, err := keysJSON(c, testFiles) |
| 129 if err != nil { | 121 if err != nil { |
| 130 http.Error(w, err.Error(), http.StatusInternalServerErro
r) | 122 http.Error(w, err.Error(), http.StatusInternalServerErro
r) |
| 131 logging.Errorf(c, "failed to create callback JSON: %v: %
v", testFiles, err) | 123 logging.Errorf(c, "failed to create callback JSON: %v: %
v", testFiles, err) |
| 132 return | 124 return |
| 133 } | 125 } |
| 134 respondJSON(c, w, bytes.NewReader(b), testFiles[0].LastMod, para
ms.Callback) | 126 respondJSON(c, w, bytes.NewReader(b), testFiles[0].LastMod, para
ms.Callback) |
| 135 return | 127 return |
| 136 } | 128 } |
| 137 | 129 |
| 138 » templates.MustRender(c, w, "pages/showfilelist.html", args) | 130 » templates.MustRender(c, w, "pages/showfilelist.html", templates.Args{ |
| 131 » » "Master": params.Master, |
| 132 » » "Builder": params.Builder, |
| 133 » » "TestType": params.TestType, |
| 134 » » "BuildNumber": params.BuildNumber, |
| 135 » » "Name": params.Name, |
| 136 » » "Files": testFiles, |
| 137 » }) |
| 139 } | 138 } |
| 140 | 139 |
| 141 func keysJSON(c context.Context, tfiles []*model.TestFile) ([]byte, error) { | 140 func keysJSON(c context.Context, tfiles []*model.TestFile) ([]byte, error) { |
| 142 type K struct { | 141 type K struct { |
| 143 Key string `json:"key"` | 142 Key string `json:"key"` |
| 144 } | 143 } |
| 145 keys := make([]K, len(tfiles)) | 144 keys := make([]K, len(tfiles)) |
| 146 for i, tf := range tfiles { | 145 for i, tf := range tfiles { |
| 147 keys[i] = K{datastore.Get(c).KeyForObj(tf).Encode()} | 146 keys[i] = K{datastore.Get(c).KeyForObj(tf).Encode()} |
| 148 } | 147 } |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 214 http.Error(w, err.Error(), http.StatusInternalServerErro
r) | 213 http.Error(w, err.Error(), http.StatusInternalServerErro
r) |
| 215 logging.Errorf(c, "failed to clean test results JSON: %v
", err) | 214 logging.Errorf(c, "failed to clean test results JSON: %v
", err) |
| 216 return | 215 return |
| 217 } | 216 } |
| 218 aggr := model.AggregateResult{Builder: params.Builder} | 217 aggr := model.AggregateResult{Builder: params.Builder} |
| 219 if err := json.NewDecoder(data).Decode(&aggr); err != nil { | 218 if err := json.NewDecoder(data).Decode(&aggr); err != nil { |
| 220 http.Error(w, err.Error(), http.StatusInternalServerErro
r) | 219 http.Error(w, err.Error(), http.StatusInternalServerErro
r) |
| 221 logging.Errorf(c, "failed to unmarshal test results JSON
: %+v: %v", data, err) | 220 logging.Errorf(c, "failed to unmarshal test results JSON
: %+v: %v", data, err) |
| 222 return | 221 return |
| 223 } | 222 } |
| 223 |
| 224 tl := aggr.ToTestList() | 224 tl := aggr.ToTestList() |
| 225 » » buf := &bytes.Buffer{} | 225 » » buf := bytes.Buffer{} |
| 226 » » if err := json.NewEncoder(buf).Encode(&tl); err != nil { | 226 » » if err := json.NewEncoder(&buf).Encode(&tl); err != nil { |
| 227 http.Error(w, err.Error(), http.StatusInternalServerErro
r) | 227 http.Error(w, err.Error(), http.StatusInternalServerErro
r) |
| 228 logging.Errorf(c, "failed to marshal test list JSON: %+v
, %v", aggr.Tests, err) | 228 logging.Errorf(c, "failed to marshal test list JSON: %+v
, %v", aggr.Tests, err) |
| 229 return | 229 return |
| 230 } | 230 } |
| 231 » » finalData = buf | 231 » » finalData = &buf |
| 232 } | 232 } |
| 233 | 233 |
| 234 respondJSON(c, w, finalData, tf.LastMod, params.Callback) | 234 respondJSON(c, w, finalData, tf.LastMod, params.Callback) |
| 235 } | 235 } |
| 236 | 236 |
| 237 // ErrNoMatches is returned when a query returns 0 entities. | 237 // ErrNoMatches is returned when a query returns 0 entities. |
| 238 type ErrNoMatches string | 238 type ErrNoMatches string |
| 239 | 239 |
| 240 func (e ErrNoMatches) Error() string { | 240 func (e ErrNoMatches) Error() string { |
| 241 return string(e) | 241 return string(e) |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 273 } | 273 } |
| 274 } | 274 } |
| 275 | 275 |
| 276 // wrapCallback returns an io.Reader that wraps the data in r in a | 276 // wrapCallback returns an io.Reader that wraps the data in r in a |
| 277 // JavaScript-style function call with the supplied name as the function name. | 277 // JavaScript-style function call with the supplied name as the function name. |
| 278 func wrapCallback(r io.Reader, name string) io.Reader { | 278 func wrapCallback(r io.Reader, name string) io.Reader { |
| 279 start := bytes.NewReader([]byte(name + "(")) | 279 start := bytes.NewReader([]byte(name + "(")) |
| 280 end := bytes.NewReader([]byte(");")) | 280 end := bytes.NewReader([]byte(");")) |
| 281 return io.MultiReader(start, r, end) | 281 return io.MultiReader(start, r, end) |
| 282 } | 282 } |
| OLD | NEW |