dmitri.shuralyov.com/service/change/...

githubapi: Use github.Router for PR HTML URLs.

This allows overriding where GitHub PRs can be viewed.

Similar to https://github.com/shurcooL/issues/commit/1cc2d579af3959bb9742445861b47a65a68c1a18.
dmitshur committed 1 year ago commit fc8c6aa79f17ce892e73e9f5422bb8b24becdd14
githubapi/githubapi.go
@@ -7,12 +7,13 @@ import (
 	"fmt"
 	"log"
 	"sort"
 	"strings"
 
+	"dmitri.shuralyov.com/route/github"
 	"dmitri.shuralyov.com/service/change"
-	"github.com/google/go-github/github"
+	githubv3 "github.com/google/go-github/github"
 	"github.com/shurcooL/githubql"
 	"github.com/shurcooL/issues"
 	"github.com/shurcooL/notifications"
 	"github.com/shurcooL/reactions"
 	"github.com/shurcooL/users"
@@ -20,21 +21,28 @@ import (
 
 // NewService creates a GitHub-backed change.Service using given GitHub clients.
 // It uses notifications service, if not nil. At this time it infers the current user
 // from GitHub clients (their authentication info), and cannot be used to serve multiple users.
 // Both GitHub clients must use same authentication info.
-func NewService(clientV3 *github.Client, clientV4 *githubql.Client, notifications notifications.ExternalService) change.Service {
+//
+// If router is nil, github.DotCom router is used, which links to subjects on github.com.
+func NewService(clientV3 *githubv3.Client, clientV4 *githubql.Client, notifications notifications.ExternalService, router github.Router) change.Service {
+	if router == nil {
+		router = github.DotCom{}
+	}
 	return service{
 		clV3:          clientV3,
 		clV4:          clientV4,
+		rtr:           router,
 		notifications: notifications,
 	}
 }
 
 type service struct {
-	clV3 *github.Client   // GitHub REST API v3 client.
+	clV3 *githubv3.Client // GitHub REST API v3 client.
 	clV4 *githubql.Client // GitHub GraphQL API v4 client.
+	rtr  github.Router
 
 	// notifications may be nil if there's no notifications service.
 	notifications notifications.ExternalService
 }
 
@@ -204,11 +212,11 @@ func (s service) ListCommits(ctx context.Context, rs string, id uint64) ([]chang
 	repo, err := ghRepoSpec(rs)
 	if err != nil {
 		// TODO: Map to 400 Bad Request HTTP error.
 		return nil, err
 	}
-	cs, _, err := s.clV3.PullRequests.ListCommits(ctx, repo.Owner, repo.Repo, int(id), &github.ListOptions{PerPage: 100}) // TODO: Pagination.
+	cs, _, err := s.clV3.PullRequests.ListCommits(ctx, repo.Owner, repo.Repo, int(id), &githubv3.ListOptions{PerPage: 100}) // TODO: Pagination.
 	if err != nil {
 		return nil, err
 	}
 	var commits []change.Commit
 	for _, c := range cs {
@@ -228,17 +236,17 @@ func (s service) GetDiff(ctx context.Context, rs string, id uint64, opt *change.
 		// TODO: Map to 400 Bad Request HTTP error.
 		return nil, err
 	}
 	switch opt {
 	case nil:
-		diff, _, err := s.clV3.PullRequests.GetRaw(ctx, repo.Owner, repo.Repo, int(id), github.RawOptions{Type: github.Diff})
+		diff, _, err := s.clV3.PullRequests.GetRaw(ctx, repo.Owner, repo.Repo, int(id), githubv3.RawOptions{Type: githubv3.Diff})
 		if err != nil {
 			return nil, err
 		}
 		return []byte(diff), nil
 	default:
-		diff, _, err := s.clV3.Repositories.GetCommitRaw(ctx, repo.Owner, repo.Repo, opt.Commit, github.RawOptions{Type: github.Diff})
+		diff, _, err := s.clV3.Repositories.GetCommitRaw(ctx, repo.Owner, repo.Repo, opt.Commit, githubv3.RawOptions{Type: githubv3.Diff})
 		if err != nil {
 			return nil, err
 		}
 		return []byte(diff), nil
 	}
@@ -282,13 +290,17 @@ func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *ch
 						ClosedEvent struct {
 							event
 							Closer struct {
 								Typename    string `graphql:"__typename"`
 								PullRequest struct {
-									State githubql.PullRequestState
-									Title string
-									URL   string
+									State      githubql.PullRequestState
+									Title      string
+									Repository struct {
+										Owner struct{ Login string }
+										Name  string
+									}
+									Number uint64
 								} `graphql:"...on PullRequest"`
 								Commit struct {
 									OID     string
 									Message string
 									Author  struct {
@@ -482,11 +494,11 @@ func (s service) ListTimeline(ctx context.Context, rs string, id uint64, opt *ch
 				e.Payload = change.ClosedEvent{
 					Closer: change.Change{
 						State: ghPRState(pr.State),
 						Title: pr.Title,
 					},
-					CloserHTMLURL: pr.URL,
+					CloserHTMLURL: s.rtr.PullRequestURL(ctx, pr.Repository.Owner.Login, pr.Repository.Name, pr.Number),
 				}
 			case "Commit":
 				c := event.ClosedEvent.Closer.Commit
 				e.Payload = change.ClosedEvent{
 					Closer: change.Commit{
@@ -790,11 +802,11 @@ func ghUser(user *githubqlUser) users.User {
 		AvatarURL: user.AvatarURL,
 		HTMLURL:   user.URL,
 	}
 }
 
-func ghV3UserOrGitUser(user *github.User, gitUser github.CommitAuthor) users.User {
+func ghV3UserOrGitUser(user *githubv3.User, gitUser githubv3.CommitAuthor) users.User {
 	if user == nil {
 		return users.User{
 			Name:      *gitUser.Name,
 			Email:     *gitUser.Email,
 			AvatarURL: "https://secure.gravatar.com/avatar?d=mm&f=y&s=96", // TODO: Use email.