github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/api/receiver/client/client.go (about) 1 // Copyright 2019 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 client is a package for simplifying the upload request made by a 6 // client to the results receiver upload endpoint (/api/results/upload). 7 package client 8 9 import ( 10 "context" 11 "fmt" 12 "io" 13 "net/http" 14 "net/url" 15 "strings" 16 "time" 17 18 "github.com/web-platform-tests/wpt.fyi/shared" 19 ) 20 21 // UploadTimeout is the timeout to upload results to the results receiver. 22 const UploadTimeout = time.Minute 23 24 // Client is the interface for the client. 25 type Client interface { 26 CreateRun( 27 sha, 28 username string, 29 password string, 30 resultURLs []string, 31 screenshotURLs []string, 32 labels []string) error 33 } 34 35 // NewClient returns a client impl. 36 // nolint:ireturn // TODO: Fix ireturn lint error 37 func NewClient(aeAPI shared.AppEngineAPI) Client { 38 return client{ 39 aeAPI: aeAPI, 40 } 41 } 42 43 type client struct { 44 aeAPI shared.AppEngineAPI 45 } 46 47 // CreateRun issues a POST request to the results receiver with the given payload. 48 func (c client) CreateRun( 49 sha, 50 username, 51 password string, 52 resultURLs []string, 53 screenshotURLs []string, 54 labels []string) error { 55 // https://github.com/web-platform-tests/wpt.fyi/blob/main/api/README.md#url-payload 56 payload := make(url.Values) 57 // Not to be confused with `revision` in the wpt.fyi TestRun model, this 58 // parameter is the full revision hash. 59 if sha != "" { 60 payload.Add("revision", sha) 61 } 62 for _, url := range resultURLs { 63 payload.Add("result_url", url) 64 } 65 for _, url := range screenshotURLs { 66 payload.Add("screenshot_url", url) 67 } 68 if labels != nil { 69 payload.Add("labels", strings.Join(labels, ",")) 70 } 71 // Ensure we call back to this appengine version instance. 72 host := c.aeAPI.GetVersionedHostname() 73 payload.Add("callback_url", fmt.Sprintf("https://%s/api/results/create", host)) 74 75 uploadURL := c.aeAPI.GetResultsUploadURL() 76 req, err := http.NewRequestWithContext( 77 context.Background(), 78 http.MethodPost, 79 uploadURL.String(), 80 strings.NewReader(payload.Encode()), 81 ) 82 if err != nil { 83 return err 84 } 85 req.SetBasicAuth(username, password) 86 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 87 88 hc := c.aeAPI.GetHTTPClientWithTimeout(UploadTimeout) 89 resp, err := hc.Do(req) 90 if err != nil { 91 return err 92 } 93 respBody, err := io.ReadAll(resp.Body) 94 resp.Body.Close() 95 if err != nil { 96 return err 97 } 98 if resp.StatusCode >= 300 { 99 return fmt.Errorf("API error: HTTP %v: %s", resp.StatusCode, string(respBody)) 100 } 101 102 return nil 103 }