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  }