github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/api/receiver/create_run.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 receiver 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "io" 11 "net/http" 12 "strings" 13 "time" 14 15 mapset "github.com/deckarep/golang-set" 16 "github.com/web-platform-tests/wpt.fyi/api/checks" 17 "github.com/web-platform-tests/wpt.fyi/shared" 18 ) 19 20 // InternalUsername is a special uploader whose password is kept secret and can 21 // only be accessed by services in this AppEngine project via Datastore. 22 const InternalUsername = "_processor" 23 24 // HandleResultsCreate handles the POST requests for creating test runs. 25 func HandleResultsCreate(a API, s checks.API, w http.ResponseWriter, r *http.Request) { 26 logger := shared.GetLogger(a.Context()) 27 28 if AuthenticateUploader(a, r) != InternalUsername { 29 http.Error(w, "This is a private API.", http.StatusUnauthorized) 30 31 return 32 } 33 body, err := io.ReadAll(r.Body) 34 if err != nil { 35 http.Error(w, err.Error(), http.StatusInternalServerError) 36 37 return 38 } 39 40 var testRun shared.TestRun 41 if err := json.Unmarshal(body, &testRun); err != nil { 42 http.Error(w, "Failed to parse JSON: "+err.Error(), http.StatusBadRequest) 43 44 return 45 } 46 47 if testRun.TimeStart.IsZero() { 48 testRun.TimeStart = time.Now() 49 } 50 if testRun.TimeEnd.IsZero() { 51 testRun.TimeEnd = testRun.TimeStart 52 } 53 testRun.CreatedAt = time.Now() 54 55 // nolint:staticcheck // TODO: Fix staticcheck lint error (SA1019). 56 if len(testRun.FullRevisionHash) != 40 { 57 http.Error(w, "full_revision_hash must be the full SHA (40 chars)", http.StatusBadRequest) 58 59 return 60 } else if testRun.Revision != "" && strings.Index(testRun.FullRevisionHash, testRun.Revision) != 0 { 61 http.Error(w, 62 fmt.Sprintf( 63 "Mismatch of full_revision_hash and revision fields: %s vs %s", 64 testRun.FullRevisionHash, 65 testRun.Revision, 66 ), 67 http.StatusBadRequest) 68 69 return 70 } 71 // nolint:staticcheck // TODO: Fix staticcheck lint error (SA1019). 72 testRun.Revision = testRun.FullRevisionHash[:10] 73 74 key, err := a.AddTestRun(&testRun) 75 if err != nil { 76 http.Error(w, err.Error(), http.StatusInternalServerError) 77 78 return 79 } 80 // Copy int64 representation of key into TestRun.ID so that clients can 81 // inspect/use key value. 82 testRun.ID = key.IntID() 83 84 // Do not schedule on pr_base to avoid redundancy with pr_head. 85 if !testRun.LabelsSet().Contains(shared.PRBaseLabel) { 86 spec := shared.ProductSpec{} // nolint:exhaustruct // TODO: Fix exhaustruct lint error 87 spec.BrowserName = testRun.BrowserName 88 spec.Labels = mapset.NewSet(testRun.Channel()) 89 err = s.ScheduleResultsProcessing(testRun.FullRevisionHash, spec) 90 if err != nil { 91 logger.Warningf("Failed to schedule results: %s", err.Error()) 92 } 93 } 94 95 // nolint:exhaustruct // TODO: Fix exhaustruct lint error. 96 pendingRun := shared.PendingTestRun{ 97 ID: testRun.ID, 98 Stage: shared.StageValid, 99 ProductAtRevision: testRun.ProductAtRevision, 100 } 101 if err := a.UpdatePendingTestRun(pendingRun); err != nil { 102 // This is a non-fatal error; don't return. 103 logger.Errorf("Failed to update pending test run: %s", err.Error()) 104 } 105 106 jsonOutput, err := json.Marshal(testRun) 107 if err != nil { 108 http.Error(w, err.Error(), http.StatusInternalServerError) 109 110 return 111 } 112 logger.Infof("Successfully created run %v (%s)", testRun.ID, testRun.String()) 113 w.WriteHeader(http.StatusCreated) 114 _, err = w.Write(jsonOutput) 115 if err != nil { 116 logger.Warningf("Failed to write data in api/results/create handler: %s", err.Error()) 117 } 118 }