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 }