Skip to content

Commit 81b7391

Browse files
committed
Fixes/improvements to scraping
* Don't crash the entire scraper task when loading legacy config * Log full backtrace in case of crash * Ensure all source methods are scraped, regardless of whether they are in the user-permitted set
1 parent 4aed0a9 commit 81b7391

6 files changed

Lines changed: 87 additions & 12 deletions

File tree

app/models/source.rb

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class Source < ApplicationRecord
2020
validates :url, url: true
2121
validates :approval_status, inclusion: { in: APPROVAL_STATUS.values }
2222
validates :method, inclusion: { in: -> (_) { TeSS::Config.user_ingestion_methods } },
23-
unless: -> { User.current_user&.is_admin? }
23+
unless: -> { User.current_user&.is_admin? || User.current_user&.has_role?(:scraper_user) }
2424
validate :check_method
2525

2626
before_create :set_approval_status
@@ -85,10 +85,7 @@ def self.enabled
8585
end
8686

8787
def check_method
88-
c = ingestor_class rescue nil
89-
if c.nil?
90-
errors.add(:method, 'is invalid')
91-
end
88+
errors.add(:method, 'is invalid') unless Ingestors::IngestorFactory.valid_ingestor?(method)
9289
end
9390

9491
def self.approved

lib/ingestors/ingestor_factory.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ def self.get_ingestor(method)
2727
end
2828
end
2929

30+
def self.valid_ingestor?(method)
31+
ingestor_config.key?(method)
32+
end
33+
3034
def self.grouped_options
3135
@grouped_options ||= ingestor_config.values.group_by { |c| c[:category] || :any }
3236
end

lib/scraper.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ def provider_exists
2020
errors.delete(:content_provider)
2121
end
2222
end
23+
24+
def resource_type=(*args)
25+
warn %(The "resource_type" property for a source is now redundant ("#{@provider}" in config/ingestion.yml))
26+
end
2327
end
2428

2529
attr_reader :log_file, :name, :username, :sources
@@ -59,6 +63,7 @@ def run
5963
if user.role.nil? or user.role.name != @default_role
6064
log t('scraper.messages.invalid', error_message: t('scraper.messages.bad_role')), 1
6165
end
66+
User.current_user = user
6267

6368
processed = 0
6469

@@ -146,7 +151,9 @@ def run
146151

147152
rescue Exception => e
148153
log " Run Scraper failed with: #{e.message}", 0
149-
log " #{e.backtrace[0]}", 0
154+
e.backtrace.each do |line|
155+
log " #{line}", 0
156+
end
150157
end
151158

152159
# wrap up
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name: crash
2+
logfile: log/ingestion.test.log
3+
loglevel: 0
4+
username: ingestor
5+
sources:
6+
- id: 1
7+
jgrhierjvrv: gjigdfgidf
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: legacy
2+
logfile: log/ingestion.test.log
3+
loglevel: 0
4+
username: ingestor
5+
sources:
6+
- id: 1
7+
provider: 'Portal Provider' # content provider's title
8+
url: https://zenodo.org/api/records/?communities=ardc # the root URL to access the source
9+
method: rest # one of 'csv', 'rest'
10+
resource_type: material
11+
enabled: true

test/unit/ingestors/scraper_test.rb

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ def run
6060

6161
# check user does exist
6262
user = User.find_by_username(scraper.username)
63-
assert !user.nil?
64-
assert !user.role.nil?
63+
refute user.nil?
64+
refute user.role.nil?
6565
assert_equal 'registered_user', user.role.name
6666

6767
# run task
@@ -124,7 +124,7 @@ def run
124124
assert scraper.sources.size > 0
125125

126126
source = scraper.sources[0]
127-
assert !source.nil?
127+
refute source.nil?
128128
title = source[:provider]
129129
provider = ContentProvider.find_by_title(title)
130130
assert provider.nil?
@@ -148,10 +148,10 @@ def run
148148
assert scraper.sources.size > 1
149149

150150
source = scraper.sources[1]
151-
assert !source.nil?
151+
refute source.nil?
152152
title = source[:provider]
153153
provider = ContentProvider.find_by_title(title)
154-
assert !provider.nil?, "Provider title[#{title}] not found!"
154+
refute provider.nil?, "Provider title[#{title}] not found!"
155155

156156
# run task
157157
freeze_time(stub_time = Time.new(2019)) do
@@ -160,7 +160,7 @@ def run
160160

161161
assert check_task_finished(logfile)
162162
error_message = 'Content provider must exist: ' + title.to_s
163-
assert !logfile_contains(logfile, error_message), "Unexpected error message: #{error_message}"
163+
refute logfile_contains(logfile, error_message), "Unexpected error message: #{error_message}"
164164
end
165165

166166
test 'check for invalid source parameters' do
@@ -184,6 +184,55 @@ def run
184184
assert logfile_contains(logfile, error_message), 'Error message not found: ' + error_message
185185
end
186186

187+
test 'handles non-user ingestion methods' do
188+
with_settings(user_ingestion_methods: ['bioschemas']) do
189+
config = load_scraper_config('test_ingestion.yml')
190+
scraper = Scraper.new(config)
191+
logfile = scraper.log_file
192+
assert_equal 'test', scraper.name
193+
194+
freeze_time(stub_time = Time.new(2019)) do
195+
scraper.run
196+
end
197+
198+
refute logfile_contains(logfile, 'Method is not included in the list: event_csv')
199+
refute logfile_contains(logfile, 'Method is not included in the list: material_csv')
200+
end
201+
end
202+
203+
test 'does not crash for legacy config' do
204+
logfile = nil
205+
Kernel.silence_warnings do
206+
config = load_scraper_config('test_ingestion_legacy.yml')
207+
scraper = Scraper.new(config)
208+
logfile = scraper.log_file
209+
assert_equal 'legacy', scraper.name
210+
211+
freeze_time(stub_time = Time.new(2019)) do
212+
scraper.run
213+
end
214+
end
215+
216+
refute logfile_contains(logfile, 'Run Scraper failed with')
217+
assert logfile_contains(logfile, 'Method is invalid: rest')
218+
end
219+
220+
test 'logs backtrace on error' do
221+
config = load_scraper_config('test_ingestion_crash.yml')
222+
scraper = Scraper.new(config)
223+
logfile = scraper.log_file
224+
assert_equal 'crash', scraper.name
225+
226+
Ingestors::IngestorFactory.stub(:get_ingestor, -> { raise 'oh no' }) do
227+
scraper.run
228+
end
229+
230+
assert logfile_contains(logfile, 'Run Scraper failed with')
231+
assert logfile_contains(logfile, 'in `block in run')
232+
assert logfile_contains(logfile, 'in `map')
233+
assert logfile_contains(logfile, 'in `run')
234+
end
235+
187236
private
188237

189238
def check_task_finished(logfile)

0 commit comments

Comments
 (0)