ghtorrent 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/CHANGELOG +24 -0
  2. data/Gemfile +17 -0
  3. data/Gemfile.lock +40 -0
  4. data/README.md +23 -22
  5. data/bin/ght-data-retrieval +66 -24
  6. data/bin/ght-load +41 -19
  7. data/bin/ght-mirror-events +13 -16
  8. data/bin/ght-rm-dupl +119 -77
  9. data/lib/ghtorrent.rb +14 -4
  10. data/lib/ghtorrent/adapters/base_adapter.rb +17 -5
  11. data/lib/ghtorrent/adapters/mongo_persister.rb +122 -56
  12. data/lib/ghtorrent/api_client.rb +151 -16
  13. data/lib/ghtorrent/bson_orderedhash.rb +23 -0
  14. data/lib/ghtorrent/cache.rb +97 -0
  15. data/lib/ghtorrent/command.rb +43 -25
  16. data/lib/ghtorrent/gh_torrent_exception.rb +6 -0
  17. data/lib/ghtorrent/ghtorrent.rb +615 -164
  18. data/lib/ghtorrent/hash.rb +11 -0
  19. data/lib/ghtorrent/logging.rb +11 -7
  20. data/lib/ghtorrent/migrations/001_init_schema.rb +3 -3
  21. data/lib/ghtorrent/migrations/002_add_external_ref_ids.rb +2 -0
  22. data/lib/ghtorrent/migrations/003_add_orgs.rb +4 -1
  23. data/lib/ghtorrent/migrations/004_add_commit_comments.rb +4 -2
  24. data/lib/ghtorrent/migrations/005_add_repo_collaborators.rb +2 -0
  25. data/lib/ghtorrent/migrations/006_add_watchers.rb +2 -0
  26. data/lib/ghtorrent/migrations/007_add_pull_requests.rb +64 -0
  27. data/lib/ghtorrent/migrations/008_add_project_unq.rb +23 -0
  28. data/lib/ghtorrent/migrations/009_add_project_commit.rb +27 -0
  29. data/lib/ghtorrent/migrations/010_add_forks.rb +28 -0
  30. data/lib/ghtorrent/migrations/mysql_defaults.rb +6 -0
  31. data/lib/ghtorrent/persister.rb +3 -0
  32. data/lib/ghtorrent/retriever.rb +298 -102
  33. data/lib/ghtorrent/settings.rb +20 -1
  34. data/lib/ghtorrent/time.rb +5 -0
  35. data/lib/ghtorrent/utils.rb +22 -4
  36. data/lib/version.rb +5 -0
  37. metadata +173 -145
  38. data/lib/ghtorrent/call_stack.rb +0 -91
@@ -0,0 +1,11 @@
1
+ class Hash
2
+ def merge_recursive(o, overwrite = true)
3
+ merge(o) do |_,x,y|
4
+ if x.respond_to?(:merge_recursive) && y.is_a?(Hash)
5
+ x.merge_recursive(y)
6
+ else
7
+ if overwrite then y else [*x,*y] end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -21,21 +21,25 @@ module GHTorrent
21
21
 
22
22
  # Log a message at the given level.
23
23
  def log(level, msg)
24
- return unless @logger
24
+
25
25
  case level
26
26
  when :fatal then
27
- @logger.fatal msg
27
+ logger.fatal msg
28
28
  when :error then
29
- @logger.error msg
29
+ logger.error msg
30
30
  when :warn then
31
- @logger.warn msg
31
+ logger.warn msg
32
32
  when :info then
33
- @logger.info msg
33
+ logger.info msg
34
34
  when :debug then
35
- @logger.debug msg
35
+ logger.debug msg
36
36
  else
37
- @logger.debug msg
37
+ logger.debug msg
38
38
  end
39
39
  end
40
+
41
+ def logger
42
+ raise Exception.new("Unimplemented")
43
+ end
40
44
  end
41
45
  end
@@ -1,5 +1,7 @@
1
1
  require 'sequel'
2
2
 
3
+ require 'ghtorrent/migrations/mysql_defaults'
4
+
3
5
  Sequel.migration do
4
6
  up do
5
7
  puts("Creating table users")
@@ -9,9 +11,7 @@ Sequel.migration do
9
11
  String :name
10
12
  String :company, :null => true
11
13
  String :location, :null => true
12
- String :email, :null => true, :unique => true
13
- TrueClass :hireable, :null => true
14
- String :bio, :null => true
14
+ String :email, :null => true
15
15
  DateTime :created_at, :null => false, :default=>Sequel::CURRENT_TIMESTAMP
16
16
  end
17
17
 
@@ -1,5 +1,7 @@
1
1
  require 'sequel'
2
2
 
3
+ require 'ghtorrent/migrations/mysql_defaults'
4
+
3
5
  Sequel.migration do
4
6
  up do
5
7
  alter_table :users do
@@ -1,12 +1,15 @@
1
1
  require 'sequel'
2
2
 
3
+ require 'ghtorrent/migrations/mysql_defaults'
4
+
3
5
  Sequel.migration do
4
6
  up do
5
7
 
6
8
  puts("Adding organization descriminator field to table users")
7
9
 
8
10
  alter_table :users do
9
- add_column :type, "enum('USR', 'ORG')", :null => false
11
+ add_column :type, String, :null => false, :default => 'USR'
12
+ add_constraint(:type_allowed_values, :type => %w[USR ORG])
10
13
  end
11
14
 
12
15
  puts("Updating users with default values")
@@ -1,5 +1,7 @@
1
1
  require 'sequel'
2
2
 
3
+ require 'ghtorrent/migrations/mysql_defaults'
4
+
3
5
  Sequel.migration do
4
6
  up do
5
7
 
@@ -9,7 +11,7 @@ Sequel.migration do
9
11
  primary_key :id
10
12
  foreign_key :commit_id, :commits, :null => false
11
13
  foreign_key :user_id, :users, :null => false
12
- String :body
14
+ String :body, :size => 256
13
15
  Integer :line, :null => true
14
16
  Integer :position, :null => true
15
17
  Integer :comment_id, :null => false, :unique => true
@@ -24,4 +26,4 @@ Sequel.migration do
24
26
  drop_table :commit_comments
25
27
 
26
28
  end
27
- end
29
+ end
@@ -1,5 +1,7 @@
1
1
  require 'sequel'
2
2
 
3
+ require 'ghtorrent/migrations/mysql_defaults'
4
+
3
5
  Sequel.migration do
4
6
  up do
5
7
 
@@ -1,5 +1,7 @@
1
1
  require 'sequel'
2
2
 
3
+ require 'ghtorrent/migrations/mysql_defaults'
4
+
3
5
  Sequel.migration do
4
6
  up do
5
7
 
@@ -0,0 +1,64 @@
1
+ require 'sequel'
2
+
3
+ require 'ghtorrent/migrations/mysql_defaults'
4
+
5
+ Sequel.migration do
6
+ up do
7
+
8
+ puts("Adding table pull requests")
9
+
10
+ create_table :pull_requests do
11
+ primary_key :id
12
+ foreign_key :head_repo_id, :projects
13
+ foreign_key :base_repo_id, :projects, :null => false
14
+ foreign_key :head_commit_id, :commits
15
+ foreign_key :base_commit_id, :commits, :null => false
16
+ foreign_key :user_id, :users, :null => false
17
+ Integer :pullreq_id, :null => false
18
+ TrueClass :intra_branch, :null => false
19
+ unique([:pullreq_id, :base_repo_id])
20
+ end
21
+
22
+ puts("Adding table pull request history")
23
+
24
+ create_table :pull_request_history do
25
+ primary_key :id
26
+ foreign_key :pull_request_id, :pull_requests, :null => false
27
+ DateTime :created_at, :null => false, :default=>Sequel::CURRENT_TIMESTAMP
28
+ String :ext_ref_id, :null => false, :size => 24, :default => "0"
29
+ String :action, :null => false
30
+ check(:action=>%w[opened closed merged synchronize reopened])
31
+ end
32
+
33
+ puts("Adding table pull request commits")
34
+
35
+ create_table :pull_request_commits do
36
+ foreign_key :pull_request_id, :pull_requests, :null => false
37
+ foreign_key :commit_id, :commits, :null => false
38
+ primary_key [:pull_request_id, :commit_id]
39
+ end
40
+
41
+ puts("Adding table pull request comments")
42
+
43
+ create_table :pull_request_comments do
44
+ foreign_key :pull_request_id, :pull_requests, :null => false
45
+ foreign_key :user_id, :users, :null => false
46
+ Long :comment_id, :null => false
47
+ Integer :position, :null => true
48
+ String :body, :size => 256
49
+ foreign_key :commit_id, :commits, :null => false
50
+ DateTime :created_at, :null => false, :default=>Sequel::CURRENT_TIMESTAMP
51
+ String :ext_ref_id, :null => false, :size => 24, :default => "0"
52
+ end
53
+
54
+ end
55
+
56
+ down do
57
+
58
+ drop_table :pull_requests
59
+ drop_table :pull_request_history
60
+ drop_table :pull_request_commits
61
+ drop_table :pull_request_comments
62
+
63
+ end
64
+ end
@@ -0,0 +1,23 @@
1
+ require 'sequel'
2
+
3
+ require 'ghtorrent/migrations/mysql_defaults'
4
+
5
+ Sequel.migration do
6
+ up do
7
+
8
+ puts("Adding unique(name, owner) constraint to table projects")
9
+
10
+ alter_table :projects do
11
+ add_unique_constraint([:name, :owner_id])
12
+ end
13
+ end
14
+
15
+ down do
16
+
17
+ puts("Removing unique(name, owner) constraint from table projects")
18
+
19
+ alter_table :projects do
20
+ drop_unique_constraint([:name, :owner_id])
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ require 'sequel'
2
+
3
+ require 'ghtorrent/migrations/mysql_defaults'
4
+
5
+ Sequel.migration do
6
+ up do
7
+
8
+ puts("Create table project_commits")
9
+ create_table :project_commits do
10
+ foreign_key :project_id, :projects
11
+ foreign_key :commit_id, :commits
12
+ primary_key [:project_id, :commit_id]
13
+ end
14
+
15
+ puts("Migrating data from commits to project_commits")
16
+ transaction(:rollback => :reraise, :isolation => :committed) do
17
+ self[:project_commits].insert([:project_id, :commit_id],
18
+ self[:commits].select(:project_id, :id))
19
+ end
20
+
21
+ end
22
+
23
+ down do
24
+
25
+ drop_table :project_commits
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ require 'sequel'
2
+
3
+ require 'ghtorrent/migrations/mysql_defaults'
4
+
5
+ Sequel.migration do
6
+ up do
7
+
8
+ puts("Adding table forks")
9
+
10
+ create_table :forks do
11
+ foreign_key :forked_project_id, :projects, :null => false
12
+ foreign_key :forked_from_id, :projects, :null => false
13
+ Integer :fork_id, :null => false, :unique => true
14
+ DateTime :created_at, :null => false,
15
+ :default => Sequel::CURRENT_TIMESTAMP
16
+ String :ext_ref_id, :null => false, :size => 24, :default => "0"
17
+ primary_key([:forked_project_id, :forked_from_id])
18
+ end
19
+
20
+ end
21
+
22
+ down do
23
+
24
+ puts("Droping table forks")
25
+ drop_table :forks
26
+
27
+ end
28
+ end
@@ -0,0 +1,6 @@
1
+ require 'sequel'
2
+
3
+ Sequel::MySQL.default_engine = 'InnoDB' if defined?(Sequel::MySQL)
4
+ Sequel::MySQL.default_charset = 'utf8' if defined?(Sequel::MySQL)
5
+ Sequel::MySQL.default_collate = 'utf8_general_ci' if defined?(Sequel::MySQL)
6
+
@@ -1,3 +1,6 @@
1
+ require 'ghtorrent/adapters/mongo_persister'
2
+ require 'ghtorrent/adapters/noop_persister'
3
+
1
4
  module GHTorrent
2
5
 
3
6
  #
@@ -1,29 +1,37 @@
1
1
  require 'uri'
2
2
 
3
+ require 'ghtorrent/api_client'
4
+ require 'ghtorrent/settings'
5
+ require 'ghtorrent/utils'
6
+ require 'ghtorrent/gh_torrent_exception'
7
+
3
8
  module GHTorrent
4
9
  module Retriever
5
10
 
11
+ include GHTorrent::Settings
6
12
  include GHTorrent::Utils
7
13
  include GHTorrent::APIClient
8
14
 
9
- def initialize(settings)
10
- super(settings)
11
- @settings = settings
12
- @uniq = config(:uniq_id)
15
+ def ext_uniq
16
+ raise Exception("Unimplemented")
17
+ end
18
+
19
+ def persister
20
+ raise Exception("Unimplemented")
13
21
  end
14
22
 
15
23
  def retrieve_user_byusername(user)
16
- stored_user = @persister.find(:users, {'login' => user})
24
+ stored_user = persister.find(:users, {'login' => user})
17
25
  if stored_user.empty?
18
26
  url = ghurl "users/#{user}"
19
27
  u = api_request(url)
20
28
 
21
29
  if u.empty?
22
- throw GHTorrentException.new("Cannot find user #{user}")
30
+ return
23
31
  end
24
32
 
25
- unq = @persister.store(:users, u)
26
- u[@uniq] = unq
33
+ unq = persister.store(:users, u)
34
+ u[ext_uniq] = unq
27
35
  what = user_type(u['type'])
28
36
  info "Retriever: New #{what} #{user}"
29
37
  u
@@ -41,62 +49,79 @@ module GHTorrent
41
49
  url = ghurl("legacy/user/email/#{URI.escape(email)}")
42
50
  r = api_request(url)
43
51
 
44
- return nil if r.empty?
45
- r
52
+ unless r.empty? or r['user']['login'].nil?
53
+ info "Retriever: User #{r['user']['login']} retrieved by email #{email}"
54
+ retrieve_user_byusername(r['user']['login'])
55
+ else
56
+ if r.empty?
57
+ nil
58
+ else
59
+ u = r['user']
60
+ unq = persister.store(:users, u)
61
+ u[ext_uniq] = unq
62
+ what = user_type(u['type'])
63
+ info "Retriever: New #{what} #{user}"
64
+ u
65
+ end
66
+ end
46
67
  end
47
68
 
48
- def retrieve_user_followers(user)
49
- stored_followers = @persister.find(:followers, {'follows' => user})
69
+ def retrieve_user_follower(followed, follower)
70
+ stored_item = persister.find(:followers, {'follows' => followed,
71
+ 'login' => follower})
72
+
73
+ if stored_item.empty?
74
+ retrieve_user_followers(followed).find{|x| x['login'] == follower}
75
+ else
76
+ stored_item.first
77
+ end
78
+ end
50
79
 
80
+ def retrieve_user_followers(user)
51
81
  followers = paged_api_request(ghurl "users/#{user}/followers")
52
82
  followers.each do |x|
53
83
  x['follows'] = user
54
84
 
55
- exists = !stored_followers.find { |f|
56
- f['follows'] == user && f['login'] == x['login']
57
- }.nil?
85
+ exists = !persister.find(:followers, {'follows' => user,
86
+ 'login' => x['login']}).empty?
58
87
 
59
88
  if not exists
60
- @persister.store(:followers, x)
89
+ persister.store(:followers, x)
61
90
  info "Retriever: Added follower #{user} -> #{x['login']}"
62
91
  else
63
92
  debug "Retriever: Follower #{user} -> #{x['login']} exists"
64
93
  end
65
94
  end
66
95
 
67
- @persister.find(:followers, {'follows' => user})
96
+ persister.find(:followers, {'follows' => user})
68
97
  end
69
98
 
70
99
  # Retrieve a single commit from a repo
71
100
  def retrieve_commit(repo, sha, user)
72
- commit = @persister.find(:commits, {'sha' => "#{sha}"})
101
+ commit = persister.find(:commits, {'sha' => "#{sha}"})
73
102
 
74
103
  if commit.empty?
75
104
  url = ghurl "repos/#{user}/#{repo}/commits/#{sha}"
76
105
  c = api_request(url)
77
106
 
78
107
  if c.empty?
79
- throw GHTorrentException.new("Cannot find commit #{user}/#{repo}/#{sha}")
108
+ return
80
109
  end
81
110
 
82
- unq = @persister.store(:commits, c)
83
- info "Retriever: New commit #{repo} -> #{sha}"
84
- c[@uniq] = unq
111
+ unq = persister.store(:commits, c)
112
+ info "Retriever: New commit #{user}/#{repo} -> #{sha}"
113
+ c[ext_uniq] = unq
85
114
  c
86
115
  else
87
- debug "Retriever: Already got commit #{repo} -> #{sha}"
116
+ debug "Retriever: Already got commit #{user}/#{repo} -> #{sha}"
88
117
  commit.first
89
118
  end
90
119
  end
91
120
 
92
- # Retrieve all project commits or 500 (whatever comes first),
121
+ # Retrieve up to 30 * +:mirror_commit_pages_new_repo+ commits
93
122
  # starting from the provided +sha+
94
123
  def retrieve_commits(repo, sha, user)
95
- last_sha = if sha.nil?
96
- "master"
97
- else
98
- sha
99
- end
124
+ last_sha = if sha.nil? then "master" else sha end
100
125
 
101
126
  url = ghurl "repos/#{user}/#{repo}/commits?last_sha=#{last_sha}"
102
127
  commits = paged_api_request(url, config(:mirror_commit_pages_new_repo))
@@ -108,19 +133,19 @@ module GHTorrent
108
133
 
109
134
 
110
135
  def retrieve_repo(user, repo)
111
- stored_repo = @persister.find(:repos, {'owner.login' => user,
136
+ stored_repo = persister.find(:repos, {'owner.login' => user,
112
137
  'name' => repo })
113
138
  if stored_repo.empty?
114
139
  url = ghurl "repos/#{user}/#{repo}"
115
140
  r = api_request(url)
116
141
 
117
142
  if r.empty?
118
- throw GHTorrentException.new("Cannot find repo #{user}/#{repo}")
143
+ return
119
144
  end
120
145
 
121
- unq = @persister.store(:repos, r)
146
+ unq = persister.store(:repos, r)
122
147
  info "Retriever: New repo #{user} -> #{repo}"
123
- r[@uniq] = unq
148
+ r[ext_uniq] = unq
124
149
  r
125
150
  else
126
151
  debug "Retriever: Already got repo #{user} -> #{repo}"
@@ -142,51 +167,49 @@ module GHTorrent
142
167
 
143
168
  # Retrieve organization members
144
169
  def retrieve_org_members(org)
145
- url = ghurl "orgs/#{org}/members"
146
- stored_org_members = @persister.find(:org_members, {'org' => org})
170
+ stored_org_members = persister.find(:org_members, {'org' => org})
147
171
 
148
172
  org_members = paged_api_request(ghurl "orgs/#{org}/members")
149
173
  org_members.each do |x|
150
174
  x['org'] = org
151
175
 
152
176
  exists = !stored_org_members.find { |f|
153
- f['org'] == user && f['login'] == x['login']
177
+ f['org'] == org && f['login'] == x['login']
154
178
  }.nil?
155
179
 
156
180
  if not exists
157
- @persister.store(:org_members, x)
158
- info "Retriever: Added member #{org} -> #{x['login']}"
181
+ persister.store(:org_members, x)
182
+ info "Retriever: Added org member #{org} -> #{x['login']}"
159
183
  else
160
- debug "Retriever: Member #{org} -> #{x['login']} exists"
184
+ debug "Retriever: Org Member #{org} -> #{x['login']} exists"
161
185
  end
162
186
  end
163
187
 
164
- @persister.find(:org_members, {'org' => org}).map{|o| retrieve_org(o['login'])}
188
+ persister.find(:org_members, {'org' => org}).map{|o| retrieve_org(o['login'])}
165
189
  end
166
190
 
167
191
  # Retrieve all comments for a single commit
168
192
  def retrieve_commit_comments(user, repo, sha)
169
- stored_comments = @persister.find(:commit_comments, {'commit_id' => sha})
170
193
  retrieved_comments = paged_api_request(ghurl "repos/#{user}/#{repo}/commits/#{sha}/comments")
171
194
 
172
- retrieved_comments.each{ |x|
195
+ retrieved_comments.each { |x|
173
196
  x['repo'] = repo
174
197
  x['user'] = user
175
198
  x['commit_id'] = sha
176
199
 
177
- if @persister.find(:commit_comments, {'repo' => repo,
200
+ if persister.find(:commit_comments, {'repo' => repo,
178
201
  'user' => user,
179
202
  'id' => x['id']}).empty?
180
- @persister.store(:commit_comments, x)
203
+ persister.store(:commit_comments, x)
181
204
  end
182
205
  }
183
- @persister.find(:commit_comments, {'commit_id' => sha})#.map{|x| x[@uniq] = x['_id']; x}
184
- end
206
+ persister.find(:commit_comments, {'commit_id' => sha})
207
+ end
185
208
 
186
- # Retrieve a single comment
209
+ # Retrieve a single comment
187
210
  def retrieve_commit_comment(user, repo, id)
188
211
 
189
- comment = @persister.find(:commit_comments, {'repo' => repo,
212
+ comment = persister.find(:commit_comments, {'repo' => repo,
190
213
  'user' => user,
191
214
  'id' => id}).first
192
215
  if comment.nil?
@@ -199,10 +222,10 @@ module GHTorrent
199
222
 
200
223
  r['repo'] = repo
201
224
  r['user'] = user
202
- @persister.store(:commit_comments, r)
225
+ persister.store(:commit_comments, r)
203
226
  info "Retriever: Added commit comment #{r['commit_id']} -> #{r['id']}"
204
- r[@uniq] = r['_id']
205
- r
227
+ persister.find(:commit_comments, {'repo' => repo, 'user' => user,
228
+ 'id' => id}).first
206
229
  else
207
230
  debug "Retriever: Commit comment #{comment['commit_id']} -> #{comment['id']} exists"
208
231
  comment
@@ -211,91 +234,264 @@ module GHTorrent
211
234
 
212
235
  # Retrieve all collaborators for a repository
213
236
  def retrieve_repo_collaborators(user, repo)
237
+ repo_bound_items(user, repo, :repo_collaborators,
238
+ "repos/#{user}/#{repo}/collaborators",
239
+ {'repo' => repo, 'owner' => user},
240
+ 'login')
241
+ end
242
+
243
+ # Retrieve a single repository collaborator
244
+ def retrieve_repo_collaborator(user, repo, new_member)
245
+ repo_bound_item(user, repo, new_member, :repo_collaborators,
246
+ "repos/#{user}/#{repo}/collaborators",
247
+ {'repo' => repo, 'owner' => user},
248
+ 'login')
249
+ end
250
+
251
+ # Retrieve all watchers for a repository
252
+ def retrieve_watchers(user, repo)
253
+ repo_bound_items(user, repo, :watchers,
254
+ "repos/#{user}/#{repo}/watchers",
255
+ {'repo' => repo, 'owner' => user},
256
+ 'login')
257
+ end
258
+
259
+ # Retrieve a single watcher for a repositry
260
+ def retrieve_watcher(user, repo, watcher)
261
+ repo_bound_item(user, repo, watcher, :watchers,
262
+ "repos/#{user}/#{repo}/watchers",
263
+ {'repo' => repo, 'owner' => user},
264
+ 'login')
265
+ end
214
266
 
215
- url = ghurl "repos/#{user}/#{repo}/collaborators"
216
- stored_collaborators = @persister.find(:repo_collaborators,
217
- {'repo' => repo, 'owner' => user})
267
+ def retrieve_pull_requests(user, repo)
268
+ open = "repos/#{user}/#{repo}/pulls"
269
+ closed = "repos/#{user}/#{repo}/pulls?state=closed"
270
+ repo_bound_items(user, repo, :pull_requests,
271
+ [open, closed],
272
+ {'repo' => repo, 'owner' => user},
273
+ 'number')
274
+ end
275
+
276
+ def retrieve_pull_request(user, repo, pullreq_id)
277
+ open = "repos/#{user}/#{repo}/pulls"
278
+ closed = "repos/#{user}/#{repo}/pulls?state=closed"
279
+ repo_bound_item(user, repo, pullreq_id, :pull_requests,
280
+ [open, closed],
281
+ {'repo' => repo, 'owner' => user,
282
+ 'number' => pullreq_id},
283
+ 'number')
284
+ end
285
+
286
+ def retrieve_forks(user, repo)
287
+ repo_bound_items(user, repo, :forks,
288
+ "repos/#{user}/#{repo}/forks",
289
+ {'repo' => repo, 'owner' => user},
290
+ 'id')
291
+ end
292
+
293
+ def retrieve_fork(user, repo, fork_id)
294
+ repo_bound_item(user, repo, fork_id, :forks,
295
+ "repos/#{user}/#{repo}/forks",
296
+ {'repo' => repo, 'owner' => user},
297
+ 'id')
298
+ end
218
299
 
219
- collaborators = paged_api_request url
220
- collaborators.each do |x|
300
+ def retrieve_pull_req_commits(user, repo, pullreq_id)
301
+ is_intra_branch = Proc.new do |req|
302
+ req['head']['repo'].nil?
303
+ end
304
+
305
+ pull_req = retrieve_pull_request(user, repo, pullreq_id)
306
+
307
+ unless is_intra_branch.call(pull_req)
308
+ head_user = pull_req['head']['repo']['owner']['login']
309
+ head_repo = pull_req['head']['repo']['name']
310
+
311
+ commits = paged_api_request(ghurl "repos/#{user}/#{repo}/pulls/#{pullreq_id}/commits")
312
+ commits.map { |x|
313
+ retrieve_commit(head_repo, x['sha'], head_user)
314
+ }
315
+ else
316
+ commits = paged_api_request(ghurl "repos/#{user}/#{repo}/pulls/#{pullreq_id}/commits")
317
+ commits.map { |x|
318
+ retrieve_commit(repo, x['sha'], user)
319
+ }
320
+ end
321
+ end
322
+
323
+ def retrieve_pull_req_comments(owner, repo, pullreq_id)
324
+ review_comments_url = ghurl "repos/#{owner}/#{repo}/pulls/#{pullreq_id}/comments"
325
+
326
+ url = review_comments_url
327
+ retrieved_comments = paged_api_request url
328
+
329
+ retrieved_comments.each { |x|
330
+ x['owner'] = owner
221
331
  x['repo'] = repo
222
- x['owner'] = user
332
+ x['issue_id'] = pullreq_id
223
333
 
224
- exists = !stored_collaborators.find { |f|
225
- f['login'] == x['login']
226
- }.nil?
334
+ if persister.find(:pull_request_comments, {'owner' => owner,
335
+ 'repo' => repo,
336
+ 'pullreq_id' => pullreq_id,
337
+ 'id' => x['id']}).empty?
338
+ persister.store(:pull_request_comments, x)
339
+ end
340
+ }
227
341
 
228
- if not exists
229
- @persister.store(:repo_collaborators, x)
230
- info "Retriever: Added collaborator #{repo} -> #{x['login']}"
231
- else
232
- debug "Retriever: Collaborator #{repo} -> #{x['login']} exists"
342
+ persister.find(:pull_request_comments, {'owner' => owner, 'repo' => repo,
343
+ 'pullreq_id' => pullreq_id})
344
+ end
345
+
346
+ def retrieve_pull_req_comment(owner, repo, pullreq_id, comment_id)
347
+ comment = persister.find(:pull_request_comments, {'repo' => repo,
348
+ 'owner' => owner,
349
+ 'pullreq_id' => pullreq_id,
350
+ 'id' => comment_id}).first
351
+ if comment.nil?
352
+ r = api_request(ghurl "repos/#{owner}/#{repo}/pulls/comments/#{comment_id}")
353
+
354
+ if r.empty?
355
+ debug "Retriever: Pullreq comment #{owner}/#{repo} #{pullreq_id}->#{comment_id} deleted"
356
+ return
233
357
  end
358
+
359
+ r['repo'] = repo
360
+ r['owner'] = owner
361
+ r['pullreq_id'] = pullreq_id
362
+ persister.store(:pull_request_comments, r)
363
+ info "Retriever: Added pullreq comment #{owner}/#{repo} #{pullreq_id}->#{comment_id}"
364
+ persister.find(:pull_request_comments, {'repo' => repo, 'owner' => owner,
365
+ 'pullreq_id' => pullreq_id,
366
+ 'id' => comment_id}).first
367
+ else
368
+ debug "Retriever: Pullreq comment #{owner}/#{repo} #{pullreq_id}->#{comment_id} exists"
369
+ comment
234
370
  end
371
+ end
235
372
 
236
- @persister.find(:repo_collaborators, {'repo' => repo, 'owner' => user})
373
+ def retrieve_issues(user, repo)
374
+ repo_bound_items(user, repo, :issues,
375
+ "repos/#{user}/#{repo}/issues",
376
+ {'repo' => repo, 'owner' => user},
377
+ 'id')
237
378
  end
238
379
 
239
- # Retrieve a single repository collaborator
240
- def retrieve_repo_collaborator(user, repo, new_member)
380
+ def retrieve_issue(user, repo, issue_id)
381
+ repo_bound_item(user, repo, issue_id, :issues,
382
+ "repos/#{user}/#{repo}/issues/#{issue_id}",
383
+ {'repo' => repo, 'owner' => user},
384
+ 'id')
385
+ end
386
+
387
+ def retrieve_issue_comments(owner, repo, issue_id)
388
+ url = ghurl "repos/#{owner}/#{repo}/issues/#{issue_id}/comments"
389
+ retrieved_comments = paged_api_request url
241
390
 
242
- collaborator = @persister.find(:repo_collaborators,
243
- {'repo' => repo,
244
- 'owner' => user,
245
- 'login' => new_member})
391
+ retrieved_comments.each { |x|
392
+ x['owner'] = owner
393
+ x['repo'] = repo
394
+ x['issue_id'] = issue_id
395
+
396
+ if persister.find(:issue_comments, {'owner' => owner,
397
+ 'repo' => repo,
398
+ 'issue_id' => issue_id,
399
+ 'id' => x['id']}).empty?
400
+ persister.store(:issue_comments, x)
401
+ end
402
+ }
403
+ persister.find(:issue_comments, {'owner' => owner, 'repo' => repo,
404
+ 'issue_id' => issue_id})
405
+ end
406
+
407
+ def retrieve_issue_comment(owner, repo, issue_id, comment_id)
408
+ comment = persister.find(:issue_comments, {'repo' => repo,
409
+ 'owner' => owner,
410
+ 'issue_id' => issue_id,
411
+ 'id' => comment_id}).first
412
+ if comment.nil?
413
+ r = api_request(ghurl "repos/#{owner}/#{repo}/issues/#{issue_id}/comments/#{comment_id}")
414
+
415
+ if r.empty?
416
+ debug "Retriever: Issue comment #{owner}/#{repo} #{issue_id}->#{comment_id} deleted"
417
+ return
418
+ end
246
419
 
247
- if collaborator.empty?
248
- retrieve_repo_collaborators(user, repo).find{|x| x['login'] == new_member}
420
+ r['repo'] = repo
421
+ r['owner'] = owner
422
+ x['issue_id'] = issue_id
423
+ persister.store(:issue_comments, r)
424
+ info "Retriever: Added issue comment #{owner}/#{repo} #{issue_id}->#{comment_id}"
425
+ persister.find(:issue_comments, {'repo' => repo, 'owner' => owner,
426
+ 'issue_id' => issue_id,
427
+ 'id' => comment_id}).first
428
+ r
249
429
  else
250
- collaborator.first
430
+ debug "Retriever: Commit comment #{owner}/#{repo} #{issue_id}->#{comment_id} exists"
431
+ comment
251
432
  end
252
433
  end
253
434
 
254
- # Retrieve all watchers for a repository
255
- def retrieve_watchers(user, repo)
256
- stored_watchers = @persister.find(:watchers,
257
- {'repo' => repo, 'owner' => user})
435
+ # Get current Github events
436
+ def get_events
437
+ api_request "https://api.github.com/events"
438
+ end
439
+
440
+ private
441
+
442
+ def repo_bound_items(user, repo, entity, urls, selector, descriminator,
443
+ item_id = nil)
258
444
 
259
- watchers = paged_api_request(ghurl "repos/#{user}/#{repo}/watchers")
260
- watchers.each do |x|
445
+ items = if urls.class == Array
446
+ urls.map { |url| paged_api_request(ghurl url) }.flatten
447
+ else
448
+ paged_api_request(ghurl urls)
449
+ end
450
+
451
+ items.each do |x|
261
452
  x['repo'] = repo
262
453
  x['owner'] = user
263
454
 
264
- exists = !stored_watchers.find { |f|
265
- f['login'] == x['login']
266
- }.nil?
455
+ exists = !repo_bound_instance(entity, selector,
456
+ descriminator, x[descriminator]).empty?
267
457
 
268
458
  if not exists
269
- @persister.store(:watchers, x)
270
- info "Retriever: Added watcher #{repo} -> #{x['login']}"
459
+ persister.store(entity, x)
460
+ info "Retriever: Added #{entity} #{user}/#{repo} -> #{x[descriminator]}"
271
461
  else
272
- debug "Retriever: Watcher #{repo} -> #{x['login']} exists"
462
+ debug "Retriever: #{entity} #{user}/#{repo} -> #{x[descriminator]} exists"
273
463
  end
274
464
  end
275
465
 
276
- @persister.find(:watchers, {'repo' => repo, 'owner' => user})
466
+ if item_id.nil?
467
+ persister.find(entity, selector)
468
+ else
469
+ repo_bound_instance(entity, selector, descriminator, item_id)
470
+ end
277
471
  end
278
472
 
279
- # Retrieve a single watcher for a repositry
280
- def retrieve_watcher(user, repo, watcher)
281
- stored_watcher = @persister.find(:watchers,
282
- {'repo' => repo,
283
- 'owner' => user,
284
- 'login' => watcher})
473
+ def repo_bound_item(user, repo, item_id, entity, url, selector, descriminator)
474
+ stored_item = repo_bound_instance(entity, selector, descriminator, item_id)
285
475
 
286
- if stored_watcher.empty?
287
- retrieve_watchers(user, repo).find{|x| x['login'] == watcher}
476
+ if stored_item.empty?
477
+ repo_bound_items(user, repo, entity, url, selector, descriminator,
478
+ item_id).first
288
479
  else
289
- stored_watcher.first
480
+ stored_item.first
290
481
  end
291
482
  end
292
483
 
293
- # Get current Github events
294
- def get_events
295
- api_request "https://api.github.com/events"
296
- end
484
+ def repo_bound_instance(entity, selector, descriminator, item_id)
297
485
 
298
- private
486
+ id = if item_id.to_i.to_s != item_id
487
+ item_id # item_id is string
488
+ else
489
+ item_id.to_i # convert to int
490
+ end
491
+
492
+ instance_selector = selector.merge({descriminator => id})
493
+ persister.find(entity, instance_selector)
494
+ end
299
495
 
300
496
  def ghurl(path)
301
497
  config(:mirror_urlbase) + path