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