github.com/blend/go-sdk@v1.20240719.1/codeowners/github_client.go (about) 1 /* 2 3 Copyright (c) 2024 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package codeowners 9 10 import ( 11 "bytes" 12 "context" 13 "encoding/json" 14 "fmt" 15 "io" 16 "net/http" 17 "net/url" 18 "strings" 19 ) 20 21 // GithubClient is a client to make requests to github via the api. 22 type GithubClient struct { 23 Addr string 24 Token string 25 } 26 27 // CreateURL creates a fully qualified url with a given path. 28 func (ghc GithubClient) CreateURL(path string) string { 29 parsedAddr, _ := url.Parse(ghc.Addr) 30 parsedAddr.Path = path 31 return parsedAddr.String() 32 } 33 34 // Do performs a request. 35 func (ghc GithubClient) Do(req *http.Request) (*http.Response, error) { 36 if req.Header == nil { 37 req.Header = make(http.Header) 38 } 39 req.Header.Set("Accept", "application/vnd.github.v3+json") 40 req.Header.Set("Authorization", fmt.Sprintf("token %s", ghc.Token)) 41 res, err := http.DefaultClient.Do(req) 42 if err != nil { 43 return nil, err 44 } 45 if statusCode := res.StatusCode; statusCode < http.StatusOK || statusCode > 299 { 46 defer func() { _ = res.Body.Close() }() 47 contents, _ := io.ReadAll(res.Body) 48 return nil, fmt.Errorf("non-200 returned from remote; %s", string(contents)) 49 } 50 return res, nil 51 } 52 53 // Post makes a post request. 54 func (ghc GithubClient) Post(ctx context.Context, path string, input, output interface{}) error { 55 inputContents, err := json.Marshal(input) 56 if err != nil { 57 return err 58 } 59 req, err := http.NewRequestWithContext(ctx, "POST", ghc.CreateURL(path), bytes.NewReader(inputContents)) 60 if err != nil { 61 return err 62 } 63 res, err := ghc.Do(req) 64 if err != nil { 65 return err 66 } 67 68 defer func() { _ = res.Body.Close() }() 69 if err := json.NewDecoder(res.Body).Decode(output); err != nil { 70 return err 71 } 72 return nil 73 } 74 75 // Get makes a get request. 76 func (ghc GithubClient) Get(ctx context.Context, path string, output interface{}) error { 77 req, err := http.NewRequestWithContext(ctx, "GET", ghc.CreateURL(path), nil) 78 if err != nil { 79 return err 80 } 81 res, err := ghc.Do(req) 82 if err != nil { 83 return err 84 } 85 86 defer func() { _ = res.Body.Close() }() 87 if err := json.NewDecoder(res.Body).Decode(output); err != nil { 88 return err 89 } 90 return nil 91 } 92 93 // UserExists fetches a user and returns if that user exists or not. 94 func (ghc GithubClient) UserExists(ctx context.Context, username string) error { 95 output := make(map[string]interface{}) 96 username = strings.TrimPrefix(strings.TrimSpace(username), "@") 97 return ghc.Get(ctx, fmt.Sprintf("/api/v3/users/%s", username), &output) 98 } 99 100 // TeamExists fetches a team and returns if that team exists or not. 101 func (ghc GithubClient) TeamExists(ctx context.Context, teamName string) error { 102 output := make(map[string]interface{}) 103 parts := strings.Split(teamName, "/") 104 if len(parts) != 2 { 105 return fmt.Errorf("invalid team name: %q", teamName) 106 } 107 org := strings.TrimPrefix(strings.TrimSpace(parts[0]), "@") 108 team := strings.TrimSpace(parts[1]) 109 return ghc.Get(ctx, fmt.Sprintf("/api/v3/orgs/%s/teams/%s", org, team), &output) 110 }