github.com/wtfutil/wtf@v0.43.0/modules/github/github_repo.go (about)

     1  package github
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	ghb "github.com/google/go-github/v32/github"
     9  	"github.com/wtfutil/wtf/utils"
    10  	"golang.org/x/oauth2"
    11  )
    12  
    13  const (
    14  	pullRequestsPath = "/pulls"
    15  	issuesPath       = "/issues"
    16  )
    17  
    18  // Repo defines a new GitHub Repo structure
    19  type Repo struct {
    20  	apiKey    string
    21  	baseURL   string
    22  	uploadURL string
    23  
    24  	Name         string
    25  	Owner        string
    26  	PullRequests []*ghb.PullRequest
    27  	RemoteRepo   *ghb.Repository
    28  	Err          error
    29  }
    30  
    31  // NewGithubRepo returns a new Github Repo with a name, owner, apiKey, baseURL and uploadURL
    32  func NewGithubRepo(name, owner, apiKey, baseURL, uploadURL string) *Repo {
    33  	repo := Repo{
    34  		Name:  name,
    35  		Owner: owner,
    36  
    37  		apiKey:    apiKey,
    38  		baseURL:   baseURL,
    39  		uploadURL: uploadURL,
    40  	}
    41  
    42  	return &repo
    43  }
    44  
    45  // Open will open the GitHub Repo URL using the utils helper
    46  func (repo *Repo) Open() {
    47  	utils.OpenFile(*repo.RemoteRepo.HTMLURL)
    48  }
    49  
    50  // OpenPulls will open the GitHub Pull Requests URL using the utils helper
    51  func (repo *Repo) OpenPulls() {
    52  	utils.OpenFile(*repo.RemoteRepo.HTMLURL + pullRequestsPath)
    53  }
    54  
    55  // OpenIssues will open the GitHub Issues URL using the utils helper
    56  func (repo *Repo) OpenIssues() {
    57  	utils.OpenFile(*repo.RemoteRepo.HTMLURL + issuesPath)
    58  }
    59  
    60  // Refresh reloads the github data via the Github API
    61  func (repo *Repo) Refresh() {
    62  	prs, err := repo.loadPullRequests()
    63  	repo.Err = err
    64  	repo.PullRequests = prs
    65  	if err != nil {
    66  		return
    67  	}
    68  	remote, err := repo.loadRemoteRepository()
    69  	repo.Err = err
    70  	repo.RemoteRepo = remote
    71  }
    72  
    73  /* -------------------- Counts -------------------- */
    74  
    75  // IssueCount return the total amount of issues as an int
    76  func (repo *Repo) IssueCount() int {
    77  	if repo.RemoteRepo == nil {
    78  		return 0
    79  	}
    80  
    81  	issuesLessPulls := *repo.RemoteRepo.OpenIssuesCount - len(repo.PullRequests)
    82  
    83  	return issuesLessPulls
    84  }
    85  
    86  // PullRequestCount returns the total amount of pull requests as an int
    87  func (repo *Repo) PullRequestCount() int {
    88  	return len(repo.PullRequests)
    89  }
    90  
    91  // StarCount returns the total amount of stars this repo has gained as an int
    92  func (repo *Repo) StarCount() int {
    93  	if repo.RemoteRepo == nil {
    94  		return 0
    95  	}
    96  
    97  	return *repo.RemoteRepo.StargazersCount
    98  }
    99  
   100  /* -------------------- Unexported Functions -------------------- */
   101  
   102  func (repo *Repo) isGitHubEnterprise() bool {
   103  	if len(repo.baseURL) > 0 {
   104  		if repo.uploadURL == "" {
   105  			repo.uploadURL = repo.baseURL
   106  		}
   107  		return true
   108  	}
   109  	return false
   110  }
   111  
   112  func (repo *Repo) oauthClient() *http.Client {
   113  	tokenService := oauth2.StaticTokenSource(
   114  		&oauth2.Token{AccessToken: repo.apiKey},
   115  	)
   116  
   117  	return oauth2.NewClient(context.Background(), tokenService)
   118  }
   119  
   120  func (repo *Repo) githubClient() (*ghb.Client, error) {
   121  	oauthClient := repo.oauthClient()
   122  
   123  	if repo.isGitHubEnterprise() {
   124  		return ghb.NewEnterpriseClient(repo.baseURL, repo.uploadURL, oauthClient)
   125  	}
   126  
   127  	return ghb.NewClient(oauthClient), nil
   128  }
   129  
   130  // myPullRequests returns a list of pull requests created by username on this repo
   131  func (repo *Repo) myPullRequests(username string, showStatus bool) []*ghb.PullRequest {
   132  	prs := []*ghb.PullRequest{}
   133  
   134  	for _, pr := range repo.PullRequests {
   135  		user := *pr.User
   136  
   137  		if *user.Login == username {
   138  			prs = append(prs, pr)
   139  		}
   140  	}
   141  
   142  	if showStatus {
   143  		prs = repo.individualPRs(prs)
   144  	}
   145  
   146  	return prs
   147  }
   148  
   149  // individualPRs takes a list of pull requests (presumably returned from
   150  // github.PullRequests.List) and fetches them individually to get more detailed
   151  // status info on each. see: https://developer.github.com/v3/git/#checking-mergeability-of-pull-requests
   152  func (repo *Repo) individualPRs(prs []*ghb.PullRequest) []*ghb.PullRequest {
   153  	github, err := repo.githubClient()
   154  	if err != nil {
   155  		return prs
   156  	}
   157  
   158  	var ret []*ghb.PullRequest
   159  	for i := range prs {
   160  		pr, _, err := github.PullRequests.Get(context.Background(), repo.Owner, repo.Name, prs[i].GetNumber())
   161  		if err != nil {
   162  			// worst case, just keep the original one
   163  			ret = append(ret, prs[i])
   164  		} else {
   165  			ret = append(ret, pr)
   166  		}
   167  	}
   168  	return ret
   169  }
   170  
   171  // myReviewRequests returns a list of pull requests for which username has been
   172  // requested to do a code review
   173  func (repo *Repo) myReviewRequests(username string) []*ghb.PullRequest {
   174  	prs := []*ghb.PullRequest{}
   175  
   176  	for _, pr := range repo.PullRequests {
   177  		for _, reviewer := range pr.RequestedReviewers {
   178  			if *reviewer.Login == username {
   179  				prs = append(prs, pr)
   180  			}
   181  		}
   182  	}
   183  
   184  	return prs
   185  }
   186  
   187  func (repo *Repo) customIssueQuery(filter string, perPage int) *ghb.IssuesSearchResult {
   188  	github, err := repo.githubClient()
   189  	if err != nil {
   190  		return nil
   191  	}
   192  
   193  	opts := &ghb.SearchOptions{}
   194  	if perPage != 0 {
   195  		opts.ListOptions.PerPage = perPage
   196  	}
   197  
   198  	prs, _, _ := github.Search.Issues(context.Background(), fmt.Sprintf("%s repo:%s/%s", filter, repo.Owner, repo.Name), opts)
   199  	return prs
   200  }
   201  
   202  func (repo *Repo) loadPullRequests() ([]*ghb.PullRequest, error) {
   203  	github, err := repo.githubClient()
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	opts := &ghb.PullRequestListOptions{}
   209  	opts.ListOptions.PerPage = 100
   210  
   211  	prs, _, err := github.PullRequests.List(context.Background(), repo.Owner, repo.Name, opts)
   212  
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	return prs, nil
   218  }
   219  
   220  func (repo *Repo) loadRemoteRepository() (*ghb.Repository, error) {
   221  	github, err := repo.githubClient()
   222  
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  
   227  	repository, _, err := github.Repositories.Get(context.Background(), repo.Owner, repo.Name)
   228  
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  
   233  	return repository, nil
   234  }