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 |