-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgfp.go
112 lines (91 loc) · 2.35 KB
/
gfp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package gfp
import (
"context"
"fmt"
"log"
"golang.org/x/oauth2"
"github.com/google/go-github/github"
)
var (
client *github.Client
ctx context.Context
origin string
target string
seen = make(map[string]bool, 0)
done = make(chan *UserNode)
jobQueue = make(chan jobRequest, 1000)
)
// UserNode represents a single node in the follower graph. The Parent
// field holds the user that discovered this node.
type UserNode struct {
Login string
Parent *UserNode
Page int
}
// newUserNode creates/returns a new *UserNode and adds this login to the seen
// map. If we've already seen this login, return nil.
func newUserNode(login string, parent *UserNode) *UserNode {
if _, ok := seen[login]; ok {
return nil
}
seen[login] = true
return &UserNode{
Login: login,
Parent: parent,
Page: 1,
}
}
func (node *UserNode) String() string {
if node.Parent != nil {
return fmt.Sprintf("%s -> %s", node.Parent.String(), node.Login)
}
return node.Login
}
// run retrieves the list of users that the current user is following and
// enqueues each of them.
func (node *UserNode) run() {
opts := github.ListOptions{Page: node.Page, PerPage: 100}
following, _, err := client.Users.ListFollowing(ctx, node.Login, &opts)
if err != nil {
log.Fatal(err)
}
for _, followee := range following {
user := newUserNode(*followee.Login, node)
if user == nil {
continue
}
if user.Login == target {
done <- user
return
}
jobQueue <- jobRequest{User: user}
}
if len(following) == 100 {
node.Page++
jobQueue <- jobRequest{User: node}
}
}
// GetClient creates and returns a new *github.Client. Expects accessToken
// to be a valid GitHub personal access token.
func GetClient(accessToken string) *github.Client {
ctx := context.Background()
src := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: accessToken})
return github.NewClient(oauth2.NewClient(ctx, src))
}
// Run starts the dispatcher and pushes a new request for the root user onto
// the queue. Returns the *UserNode that is received on the done channel.
func Run(start, end string, nWorkers int, c *github.Client) *UserNode {
client, ctx = c, context.Background()
if nWorkers <= 0 {
nWorkers = 6
}
startDispatcher(nWorkers)
origin, target = start, end
jobQueue <- jobRequest{User: newUserNode(origin, nil)}
for {
select {
case user := <-done:
return user
}
}
}