github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/api/azure/api.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 //go:generate mockgen -destination mock_azure/api_mock.go github.com/web-platform-tests/wpt.fyi/api/azure API 6 7 package azure 8 9 import ( 10 "context" 11 "encoding/json" 12 "fmt" 13 "io" 14 "net/http" 15 16 "github.com/web-platform-tests/wpt.fyi/shared" 17 ) 18 19 // PipelinesAppID is the ID of the Azure Pipelines GitHub app. 20 const PipelinesAppID = int64(9426) 21 22 // https://docs.microsoft.com/en-us/rest/api/azure/devops/build/artifacts/get?view=azure-devops-rest-4.1 23 24 // BuildArtifacts is a wrapper for multiple BuildArtifact results. 25 type BuildArtifacts struct { 26 Count int64 `json:"count"` 27 Value []BuildArtifact `json:"value"` 28 } 29 30 // BuildArtifact is an artifact published by a build. 31 type BuildArtifact struct { 32 ID int64 `json:"id"` 33 Name string `json:"name"` 34 Resource ArtifactResource `json:"resource"` 35 } 36 37 // ArtifactResource is a resource for an artifact. 38 type ArtifactResource struct { 39 Data string `json:"data"` 40 DownloadURL string `json:"downloadUrl"` 41 Type string `json:"type"` 42 URL string `json:"url"` 43 } 44 45 // Build is an Azure Pipelines build object. 46 type Build struct { 47 SourceBranch string `json:"sourceBranch"` 48 TriggerInfo BuildTriggerInfo `json:"triggerInfo"` 49 } 50 51 // BuildTriggerInfo is information about what triggered the build. 52 type BuildTriggerInfo struct { 53 SourceBranch string `json:"pr.sourceBranch"` 54 SourceSHA string `json:"pr.sourceSha"` 55 } 56 57 // IsMasterBranch returns whether the source branch for the build is the master branch. 58 func (a *Build) IsMasterBranch() bool { 59 return a != nil && a.SourceBranch == "refs/heads/master" 60 } 61 62 // API is for Azure Pipelines related requests. 63 type API interface { 64 GetBuildURL(owner, repo string, buildID int64) string 65 GetAzureArtifactsURL(owner, repo string, buildID int64) string 66 GetBuild(owner, repo string, buildID int64) (*Build, error) 67 } 68 69 type apiImpl struct { 70 ctx context.Context // nolint:containedctx // TODO: Fix containedctx lint error 71 } 72 73 // NewAPI returns an implementation of azure API. 74 // nolint:ireturn // TODO: Fix ireturn lint error 75 func NewAPI(ctx context.Context) API { 76 return apiImpl{ 77 ctx: ctx, 78 } 79 } 80 81 func (a apiImpl) GetBuildURL(owner, repo string, buildID int64) string { 82 // https://docs.microsoft.com/en-us/rest/api/azure/devops/build/builds/get?view=azure-devops-rest-4.1#build 83 return fmt.Sprintf( 84 "https://dev.azure.com/%s/%s/_apis/build/builds/%v", owner, repo, buildID) 85 } 86 87 func (a apiImpl) GetAzureArtifactsURL(owner, repo string, buildID int64) string { 88 return fmt.Sprintf( 89 "https://dev.azure.com/%s/%s/_apis/build/builds/%v/artifacts", 90 owner, 91 repo, 92 buildID) 93 } 94 95 func (a apiImpl) GetBuild(owner, repo string, buildID int64) (*Build, error) { 96 buildURL := a.GetBuildURL(owner, repo, buildID) 97 client := shared.NewAppEngineAPI(a.ctx).GetHTTPClient() 98 req, err := http.NewRequestWithContext(a.ctx, http.MethodGet, buildURL, nil) 99 if err != nil { 100 return nil, fmt.Errorf("failed to create GET request: %w", err) 101 } 102 resp, err := client.Do(req) 103 if err != nil { 104 return nil, fmt.Errorf("failed to handle fetch build: %w", err) 105 } 106 defer resp.Body.Close() 107 data, err := io.ReadAll(resp.Body) 108 if err != nil { 109 return nil, fmt.Errorf("failed to read request response: %w", err) 110 } 111 var build Build 112 if err := json.Unmarshal(data, &build); err != nil { 113 return nil, fmt.Errorf("failed to unmarshal request response: %w", err) 114 } 115 log := shared.GetLogger(a.ctx) 116 log.Debugf("Source branch: %s", build.SourceBranch) 117 log.Debugf("Trigger PR branch: %s", build.TriggerInfo.SourceBranch) 118 119 return &build, nil 120 }