github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/api/versions.go (about) 1 // Copyright 2018 The WPT Dashboard Project. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package api 6 7 import ( 8 "context" 9 "encoding/json" 10 "fmt" 11 "net/http" 12 "sort" 13 "time" 14 15 "github.com/web-platform-tests/wpt.fyi/shared" 16 ) 17 18 // VersionsHandler is an http.Handler for the /api/versions endpoint. 19 type VersionsHandler struct { 20 ctx context.Context // nolint:containedctx // TODO: Fix containedctx lint error 21 } 22 23 // apiVersionsHandler is responsible for emitting just the browser versions for the test runs. 24 func apiVersionsHandler(w http.ResponseWriter, r *http.Request) { 25 // Serve cached with 5 minute expiry. Delegate to VersionsHandler on cache 26 // miss. 27 ctx := r.Context() 28 shared.NewCachingHandler( 29 ctx, 30 VersionsHandler{ctx}, 31 shared.NewGZReadWritable(shared.NewRedisReadWritable(ctx, 5*time.Minute)), 32 shared.AlwaysCachable, 33 shared.URLAsCacheKey, 34 shared.CacheStatusOK, 35 ).ServeHTTP(w, r) 36 } 37 38 func (h VersionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 39 product, err := shared.ParseProductParam(r.URL.Query()) 40 if err != nil { 41 http.Error(w, err.Error(), http.StatusBadRequest) 42 43 return 44 } else if product == nil { 45 http.Error(w, fmt.Sprintf("Invalid product param: %s", r.URL.Query().Get("product")), http.StatusBadRequest) 46 47 return 48 } 49 50 ctx := h.ctx 51 store := shared.NewAppEngineDatastore(ctx, false) 52 query := store.NewQuery("TestRun").Filter("BrowserName =", product.BrowserName) 53 if product.Labels != nil { 54 for label := range product.Labels.Iter() { 55 query = query.Filter("Labels =", label) 56 } 57 } 58 distinctQuery := query.Project("BrowserVersion").Distinct() 59 var queries []shared.Query 60 if product.BrowserVersion == "" { 61 queries = []shared.Query{distinctQuery} 62 } else { 63 queries = []shared.Query{ 64 query.Filter("BrowserVersion =", product.BrowserVersion).Limit(1), 65 shared.VersionPrefix(distinctQuery, "BrowserVersion", product.BrowserVersion, false /*desc*/), 66 } 67 } 68 69 var runs shared.TestRuns 70 for _, query := range queries { 71 var someRuns shared.TestRuns 72 if _, err := store.GetAll(query, &someRuns); err != nil { 73 http.Error(w, err.Error(), http.StatusInternalServerError) 74 75 return 76 } 77 runs = append(runs, someRuns...) 78 } 79 80 if len(runs) < 1 { 81 w.WriteHeader(http.StatusNotFound) 82 _, err = w.Write([]byte("[]")) 83 if err != nil { 84 logger := shared.GetLogger(ctx) 85 logger.Warningf("Failed to write data in api/versions handler: %s", err.Error()) 86 } 87 88 return 89 } 90 91 versions := make([]string, len(runs)) 92 for i := range runs { 93 versions[i] = runs[i].BrowserVersion 94 } 95 // nolint:godox // TODO(lukebjerring): Fix this, it will put 100 before 11..., etc. 96 sort.Sort(sort.Reverse(sort.StringSlice(versions))) 97 98 versionsBytes, err := json.Marshal(versions) 99 if err != nil { 100 http.Error(w, err.Error(), http.StatusInternalServerError) 101 102 return 103 } 104 105 _, err = w.Write(versionsBytes) 106 if err != nil { 107 logger := shared.GetLogger(ctx) 108 logger.Warningf("Failed to write data in api/versions handler: %s", err.Error()) 109 } 110 }