Skip to content

Commit a3a0284

Browse files
author
Sparky Autonomous
committed
fix(github): resolve sync gaps and prevent data validation crashes
1 parent dd03553 commit a3a0284

File tree

2 files changed

+40
-21
lines changed

2 files changed

+40
-21
lines changed

app/models/github/issue.rb

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ def self.extract_from_url(url)
194194
def update_from_github(options={})
195195
# read synced_at before updating
196196
if_modified_since = options[:force] ? nil : self.synced_at.try(:httpdate)
197-
update_attribute(:synced_at, Time.now)
198197

199198
# add access token
200199
params = {
@@ -209,7 +208,12 @@ def update_from_github(options={})
209208
)
210209

211210
# trigger an update on the underlying object
212-
self.class.update_attributes_from_github_data(api_response.data, obj: self)
211+
if api_response.success?
212+
update_attribute(:synced_at, Time.now)
213+
self.class.update_attributes_from_github_data(api_response.data, obj: self)
214+
else
215+
Rails.logger.error "Github::Issue(#{id}) Update Failed: #{api_response.status} - #{api_response.data.inspect}"
216+
end
213217
end
214218

215219
# Sync comments with GitHub. Deletes comments that no longer exist.
@@ -281,23 +285,27 @@ def github_api_path
281285
end
282286

283287
def self.update_attributes_from_github_array(github_data, options={})
288+
# IMPROVEMENT: Guard against non-array data
289+
return [] unless github_data.is_a?(Array)
290+
284291
# bulk load all issues
285-
issue_hash = where("remote_id in (?)", github_data.map { |r| r['id'] })
292+
issue_hash = where("remote_id in (?)", github_data.map { |r| r['id'] if r.is_a?(Hash) }.compact)
286293
.inject({}) do |hash,obj|
287294
hash[obj.remote_id.to_i] = obj
288295
hash
289296
end
290297

291298

292299
# bulk update
293-
repos = Github::Repository.update_attributes_from_github_array(github_data.map { |issue| issue['repo'] }.uniq.compact)
300+
repos = Github::Repository.update_attributes_from_github_array(github_data.map { |issue| issue['repo'] if issue.is_a?(Hash) }.uniq.compact)
294301
repos_hash = repos.inject({}) { |hash,obj| hash[obj.remote_id.to_i] = obj; hash }
295302

296303
# bulk update linked accounts to save lots of lookups
297-
linked_accounts = LinkedAccount::Github.update_attributes_from_github_array(github_data.map { |issue| issue['user'] }.uniq.compact)
298-
linked_accounts_hash = linked_accounts.inject({}) { |hash,obj| hash[obj.remote_id.to_i] = obj; hash }
304+
linked_accounts = LinkedAccount::Github.update_attributes_from_github_array(github_data.map { |issue| issue['user'] if issue.is_a?(Hash) }.uniq.compact)
305+
linked_accounts_hash = linked_accounts.inject({}) { |hash,obj| hash[obj.uid.to_i] = obj; hash }
299306

300307
github_data.map do |issue_data|
308+
next unless issue_data.is_a?(Hash)
301309
update_attributes_from_github_data(
302310
issue_data,
303311
options.merge(
@@ -306,7 +314,7 @@ def self.update_attributes_from_github_array(github_data, options={})
306314
tracker: (issue_data.has_key?('repo') ? repos_hash[issue_data['repo']['id'].to_i] : options[:tracker])
307315
)
308316
)
309-
end
317+
end.compact
310318
end
311319

312320
def self.update_attributes_from_github_data(github_data, options={})

app/models/github/repository.rb

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ def remote_sync(options={})
125125
def update_from_github(options={})
126126
# read synced_at before updating
127127
if_modified_since = options[:force] ? nil : self.synced_at.try(:httpdate)
128-
update_attribute(:synced_at, Time.now)
129128

130129
# add access token
131130
params = {
@@ -139,7 +138,10 @@ def update_from_github(options={})
139138
headers: { 'If-Modified-Since' => if_modified_since }
140139
)
141140
# trigger an update on the underlying object if modified
142-
self.class.update_attributes_from_github_data(api_response.data, obj: self) if api_response.modified?
141+
if api_response.modified?
142+
update_attribute(:synced_at, Time.now)
143+
self.class.update_attributes_from_github_data(api_response.data, obj: self)
144+
end
143145
end
144146

145147
# Update the repo languages from GitHub
@@ -188,29 +190,38 @@ def find_or_create_issues_from_github(options={})
188190
# TODO: optimization: github api returns open_issues_count which we could compare to issues.where(state=open)
189191

190192
if options[:state]
193+
# IMPROVEMENT: Add a 10-minute buffer to account for clock drift/indexing delays
194+
since_timestamp = options[:since]
195+
since_timestamp -= 10.minutes if since_timestamp
196+
191197
# make a call to github API
192198
api_response = Github::API.call(
193199
url: File.join(self.github_api_path, "/issues"),
194200
params: {
195201
access_token: options[:person].try(:github_account).try(:oauth_token),
196-
since: options[:since].try(:iso8601),
202+
since: since_timestamp.try(:iso8601),
197203
state: options[:state],
204+
per_page: 100,
198205
page: options[:page] || 1
199-
}.reject { |k,v| v.nil? },
200-
201-
# NOTE: this doesn't have any affect... and we don't want to store etags, so no API optimization here.
202-
#headers: {
203-
# 'If-Modified-Since' => options[:since].try(:httpdate)
204-
#}.reject { |k,v| v.nil? }
206+
}.reject { |k,v| v.nil? }
205207
)
206208

207-
# Edge case: tracker is deleted, just raise for now
208-
if !api_response.success? && api_response.status == 404
209-
raise "Github::Repository(#{id}) not found at GitHub(#{url}). Maybe it was deleted?"
209+
# IMPROVEMENT: Robust error logging and status check
210+
unless api_response.success?
211+
Rails.logger.error "Github::Repository(#{id}) Issue Sync Failed: #{api_response.status} - #{api_response.data.inspect}"
212+
if api_response.status == 404
213+
raise "Github::Repository(#{id}) not found at GitHub(#{url}). Maybe it was deleted?"
214+
end
215+
return false # Stop processing this branch
210216
end
211217

212-
# process these 100 issues
213-
Github::Issue.update_attributes_from_github_array(api_response.data)
218+
# IMPROVEMENT: Type validation before processing
219+
if api_response.data.is_a?(Array)
220+
Github::Issue.update_attributes_from_github_array(api_response.data)
221+
else
222+
Rails.logger.error "Github::Repository(#{id}) Expected Array from API, got: #{api_response.data.class}"
223+
return false
224+
end
214225

215226
# if a page param wasn't passed in, load the rest of the pages
216227
if !options[:page] && api_response.link[:last]

0 commit comments

Comments
 (0)