Skip to content

Commit 09fabcb

Browse files
authored
Merge pull request #97 from code4lib/provider-validation-updates
Merge changes to be more responsive to provider's configured granularity
2 parents b75b61a + be07b61 commit 09fabcb

File tree

12 files changed

+149
-32
lines changed

12 files changed

+149
-32
lines changed

examples/models/file_model.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ def find(selector, opts={})
4545
case selector
4646
when :all
4747
records = Dir["#{@directory}/*.xml"].sort.collect do |file|
48-
File.new(file) unless File.stat(file).mtime.utc < opts[:from] or
49-
File.stat(file).mtime.utc > opts[:until]
48+
File.new(file) unless File.stat(file).mtime.utc < opts[:from].to_time or
49+
File.stat(file).mtime.utc > opts[:until].to_time
5050
end
5151
records
5252
else

lib/oai/client/response.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ def initialize(doc, &resumption_block)
3737
message = error.content
3838
code = ""
3939
if defined?(error.property) == nil
40-
code = error.attributes['code']
41-
else
42-
begin
43-
code = error["code"]
44-
rescue
45-
code = error.property('code')
46-
end
40+
code = error.attributes['code']
41+
else
42+
begin
43+
code = error["code"]
44+
rescue
45+
code = error.property('code')
4746
end
47+
end
4848
end
4949
raise OAI::Exception.new(message, code)
5050
end

lib/oai/harvester.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
require 'readline'
1010
require 'socket'
1111

12+
if not defined?(OAI::Const::VERBS)
13+
require 'oai/constants'
14+
end
15+
1216
require 'oai/client'
1317
require 'oai/harvester/config'
1418
require 'oai/harvester/harvest'

lib/oai/harvester/config.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
module OAI
66
module Harvester
77

8-
LOW_RESOLUTION = "YYYY-MM-DD"
8+
LOW_RESOLUTION = OAI::Const::Granularity::LOW
99

1010
class Config < OpenStruct
1111

lib/oai/harvester/harvest.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def harvest(site)
3838
harvest_time = Time.now.utc
3939
end
4040

41-
if "YYYY-MM-DD" == granularity(opts[:url])
41+
if OAI::Const::Granularity::LOW == granularity(opts[:url])
4242
opts[:until] = harvest_time.strftime("%Y-%m-%d")
4343
opts[:from] = @from.strftime("%Y-%m-%d") if @from
4444
else
@@ -139,7 +139,7 @@ def filename(from_time, until_time)
139139
"#{from_time.strftime(format)}_til_#{until_time.strftime(format)}"\
140140
"_at_#{until_time.strftime('%H-%M-%S')}"
141141
end
142-
142+
143143
def granularity(url)
144144
client = OAI::Client.new url
145145
client.identify.granularity
@@ -149,7 +149,7 @@ def granularity(url)
149149
def earliest(url)
150150
client = OAI::Client.new url
151151
identify = client.identify
152-
if "YYYY-MM-DD" == identify.granularity
152+
if OAI::Const::Granularity::LOW == identify.granularity
153153
Time.parse(identify.earliest_datestamp).strftime("%Y-%m-%d")
154154
else
155155
Time.parse(identify.earliest_datestamp).xmlschema

lib/oai/provider/model/activerecord_wrapper.rb

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def find(selector, options={})
6161
find_scope.where(conditions)
6262
end
6363
else
64-
find_scope.where(conditions).find_by!(identifier_field => selector)
64+
find_scope.where(conditions).where(identifier_field => selector).first
6565
end
6666
end
6767

@@ -177,22 +177,40 @@ def sql_conditions(opts)
177177
sql << "#{timestamp_field} < :until"
178178
esc_values[:until] = parse_to_local(opts[:until]) { |t| t + 1 }
179179
end
180+
180181
return [sql.join(" AND "), esc_values]
181182
end
182183

183184
private
184185

185186
def parse_to_local(time)
186-
time_obj = Time.parse(time.to_s)
187+
if time.respond_to?(:strftime)
188+
time_obj = time
189+
else
190+
begin
191+
if time[-1] == "Z"
192+
time_obj = Time.strptime(time, "%Y-%m-%dT%H:%M:%S%Z")
193+
else
194+
time_obj = Date.strptime(time, "%Y-%m-%d")
195+
end
196+
rescue
197+
raise OAI::ArgumentException.new, "unparsable date: '#{time}'"
198+
end
199+
end
200+
187201
time_obj = yield(time_obj) if block_given?
188-
# Convert to same as DB - :local => :getlocal, :utc => :getutc
189202

190-
if ActiveRecord::VERSION::MAJOR >= 7
191-
tzconv = "get#{ActiveRecord.default_timezone.to_s}".to_sym
203+
if time_obj.kind_of?(Date)
204+
time_obj.strftime("%Y-%m-%d")
192205
else
193-
tzconv = "get#{model.default_timezone.to_s}".to_sym
206+
# Convert to same as DB - :local => :getlocal, :utc => :getutc
207+
if ActiveRecord::VERSION::MAJOR >= 7
208+
tzconv = "get#{ActiveRecord.default_timezone.to_s}".to_sym
209+
else
210+
tzconv = "get#{model.default_timezone.to_s}".to_sym
211+
end
212+
time_obj.send(tzconv).strftime("%Y-%m-%d %H:%M:%S")
194213
end
195-
time_obj.send(tzconv).strftime("%Y-%m-%d %H:%M:%S")
196214
end
197215

198216
end

lib/oai/provider/response.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,13 @@ def externalize(value)
9090

9191
def parse_date(value)
9292
return value if value.respond_to?(:strftime)
93-
94-
Date.parse(value) # This will raise an exception for badly formatted dates
95-
Time.parse(value).utc # -- UTC Bug fix hack 8/08 not in core
96-
rescue
93+
94+
if value[-1] == "Z"
95+
Time.strptime(value, "%Y-%m-%dT%H:%M:%S%Z").utc
96+
else
97+
Date.strptime(value, "%Y-%m-%d")
98+
end
99+
rescue ArgumentError => e
97100
raise OAI::ArgumentException.new, "unparsable date: '#{value}'"
98101
end
99102

lib/oai/provider/response/list_records.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@ module OAI::Provider::Response
22

33
class ListRecords < RecordResponse
44
required_parameters :metadata_prefix
5+
6+
def valid?
7+
super && matching_granularity?
8+
end
9+
10+
def matching_granularity?
11+
if options[:from].nil? == false && options[:until].nil? == false && options[:from].class.name != options[:until].class.name
12+
raise OAI::ArgumentException.new, "The 'from' and 'until' options specified must have the same granularity"
13+
else
14+
true
15+
end
16+
end
517

618
def to_xml
719
result = provider.model.find(:all, options)

lib/oai/provider/resumption_token.rb

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,20 @@ def encode_conditions
126126

127127
encoded_token = @prefix.to_s.dup
128128
encoded_token << ".s(#{set})" if set
129-
encoded_token << ".f(#{self.from.utc.xmlschema})" if self.from
130-
encoded_token << ".u(#{self.until.utc.xmlschema})" if self.until
129+
if self.from
130+
if self.from.respond_to?(:utc)
131+
encoded_token << ".f(#{self.from.utc.xmlschema})"
132+
else
133+
encoded_token << ".f(#{self.from.xmlschema})"
134+
end
135+
end
136+
if self.until
137+
if self.until.respond_to?(:utc)
138+
encoded_token << ".u(#{self.until.utc.xmlschema})"
139+
else
140+
encoded_token << ".u(#{self.until.xmlschema})"
141+
end
142+
end
131143
encoded_token << ":#{last_str}"
132144
end
133145

test/activerecord_provider/tc_ar_provider.rb

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def test_from
8686
DCField.where("id < #{first_id + 10}").update_all(updated_at: Time.parse("June 1 2005"))
8787

8888

89-
from_param = Time.parse("January 1 2006")
89+
from_param = Time.parse("January 1 2006").getutc.iso8601
9090

9191
doc = REXML::Document.new(
9292
@provider.list_records(
@@ -97,7 +97,7 @@ def test_from
9797

9898
doc = REXML::Document.new(
9999
@provider.list_records(
100-
:metadata_prefix => 'oai_dc', :from => Time.parse("May 30 2005"))
100+
:metadata_prefix => 'oai_dc', :from => Time.parse("May 30 2005").getutc.iso8601)
101101
)
102102
assert_equal 20, doc.elements['OAI-PMH/ListRecords'].to_a.size
103103
end
@@ -122,11 +122,38 @@ def test_from_and_until
122122
doc = REXML::Document.new(
123123
@provider.list_records(
124124
:metadata_prefix => 'oai_dc',
125-
:from => Time.parse("June 3 2005"),
126-
:until => Time.parse("June 16 2005"))
125+
:from => Time.parse("June 3 2005").getutc.iso8601,
126+
:until => Time.parse("June 16 2005").getutc.iso8601)
127127
)
128128
assert_equal 40, doc.elements['OAI-PMH/ListRecords'].to_a.size
129129
end
130+
131+
def test_bad_until_raises_exception
132+
DCField.order('id asc').limit(10).update_all(updated_at: 1.year.ago)
133+
DCField.order('id desc').limit(10).update_all(updated_at: 1.year.from_now)
134+
badTimes = [
135+
'junk',
136+
'February 92nd, 2015']
137+
badTimes.each do |time|
138+
assert_raise(OAI::ArgumentException) do
139+
@provider.list_records(:metadata_prefix => 'oai_dc', :until => time)
140+
end
141+
end
142+
end
143+
144+
def test_bad_from_raises_exception
145+
DCField.order('id asc').limit(10).update_all(updated_at: 1.year.ago)
146+
DCField.order('id desc').limit(10).update_all(updated_at: 1.year.from_now)
147+
148+
badTimes = [
149+
'junk',
150+
'February 92nd, 2015']
151+
badTimes.each do |time|
152+
assert_raise(OAI::ArgumentException) do
153+
@provider.list_records(:metadata_prefix => 'oai_dc', :from => time)
154+
end
155+
end
156+
end
130157

131158
def test_handles_empty_collections
132159
DCField.delete_all
@@ -142,6 +169,21 @@ def test_handles_empty_collections
142169
REXML::Document.new(@provider.list_records(:metadata_prefix => 'oai_dc'))
143170
end
144171
end
172+
173+
def test_bad_id_raises_exception
174+
badIdentifiers = [
175+
'invalid"id',
176+
'oai:test/5000',
177+
'oai:test/-1',
178+
'oai:test/one',
179+
'oai:test/\\$1\1!']
180+
badIdentifiers.each do |id|
181+
assert_raise(OAI::IdException) do
182+
@provider.get_record(:identifier => id, :metadata_prefix => 'oai_dc')
183+
end
184+
end
185+
end
186+
145187

146188
def setup
147189
@provider = ARProvider.new

0 commit comments

Comments
 (0)