borrow_direct 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +183 -0
- data/Rakefile +11 -0
- data/bd_metrics/finditem_measure.rb +61 -0
- data/bd_metrics/isbn.txt +1088 -0
- data/bd_metrics/lccn.txt +1668 -0
- data/bd_metrics/oclc.txt +167 -0
- data/borrow_direct.gemspec +30 -0
- data/lib/borrow_direct/authentication.rb +55 -0
- data/lib/borrow_direct/defaults.rb +38 -0
- data/lib/borrow_direct/error.rb +17 -0
- data/lib/borrow_direct/find_item.rb +149 -0
- data/lib/borrow_direct/generate_query.rb +78 -0
- data/lib/borrow_direct/request.rb +185 -0
- data/lib/borrow_direct/request_item.rb +119 -0
- data/lib/borrow_direct/request_query.rb +124 -0
- data/lib/borrow_direct/util.rb +18 -0
- data/lib/borrow_direct/version.rb +3 -0
- data/lib/borrow_direct.rb +19 -0
- data/test/authentication_test.rb +79 -0
- data/test/find_item_test.rb +159 -0
- data/test/generate_query_test.rb +91 -0
- data/test/request_item_test.rb +109 -0
- data/test/request_query_test.rb +113 -0
- data/test/request_test.rb +141 -0
- data/test/support/assertions.rb +23 -0
- data/test/support/vcr_filter.rb +45 -0
- data/test/test_helper.rb +39 -0
- data/test/util_test.rb +32 -0
- data/test/vcr_cassettes/Authentication/Makes_a_request_succesfully.yml +52 -0
- data/test/vcr_cassettes/Authentication/Raises_for_bad_library_symbol.yml +52 -0
- data/test/vcr_cassettes/Authentication/Raises_for_bad_patron_barcode.yml +53 -0
- data/test/vcr_cassettes/Authentication/get_auth_id/raises_for_a_bad_library_symbol.yml +52 -0
- data/test/vcr_cassettes/Authentication/get_auth_id/raises_for_a_bad_patron_barcode.yml +53 -0
- data/test/vcr_cassettes/Authentication/get_auth_id/returns_an_auth_id_for_a_good_request.yml +52 -0
- data/test/vcr_cassettes/Authentication/raw_request_to_verify_HTTP_api/.yml +52 -0
- data/test/vcr_cassettes/FindItem/_find_item_request/finds_a_locally_available_item.yml +49 -0
- data/test/vcr_cassettes/FindItem/_find_item_request/finds_a_requestable_item.yml +49 -0
- data/test/vcr_cassettes/FindItem/_find_item_request/finds_an_item_that_does_not_exist_in_BD.yml +50 -0
- data/test/vcr_cassettes/FindItem/_find_item_request/with_expected_error_PUBFI002/returns_result.yml +40 -0
- data/test/vcr_cassettes/FindItem/_find_item_request/works_with_multiple_values.yml +49 -0
- data/test/vcr_cassettes/FindItem/find_with_Response/has_an_auth_id.yml +49 -0
- data/test/vcr_cassettes/FindItem/find_with_Response/has_nil_auth_id_when_BD_doesn_t_want_to_give_us_one.yml +40 -0
- data/test/vcr_cassettes/FindItem/find_with_Response/has_nil_pickup_locations_when_BD_doesn_t_want_to_give_us_them.yml +40 -0
- data/test/vcr_cassettes/FindItem/find_with_Response/has_pickup_locations.yml +49 -0
- data/test/vcr_cassettes/FindItem/find_with_Response/not_requestable_for_item_that_BD_returns_PUBFI002.yml +40 -0
- data/test/vcr_cassettes/FindItem/find_with_Response/not_requestable_for_item_that_does_not_exist_in_BD.yml +50 -0
- data/test/vcr_cassettes/FindItem/find_with_Response/not_requestable_for_item_that_no_libraries_will_lend.yml +50 -0
- data/test/vcr_cassettes/FindItem/find_with_Response/not_requestable_for_locally_available_item.yml +49 -0
- data/test/vcr_cassettes/FindItem/find_with_Response/requestable_for_requestable_item.yml +49 -0
- data/test/vcr_cassettes/FindItem/find_with_Response/requestable_with_multiple_items_if_at_least_one_is_requestable.yml +49 -0
- data/test/vcr_cassettes/Request/authentication_id/automatically_fetches_one_when_needed.yml +52 -0
- data/test/vcr_cassettes/Request/authentication_id/can_refetch_when_instructed.yml +52 -0
- data/test/vcr_cassettes/Request/can_make_a_succesful_request.yml +49 -0
- data/test/vcr_cassettes/Request/gets_BD_error_info.yml +41 -0
- data/test/vcr_cassettes/Request/raises_on_bad_path.yml +53 -0
- data/test/vcr_cassettes/Request/raises_on_bad_request_hash.yml +63 -0
- data/test/vcr_cassettes/Request/with_expected_errors/still_returns_result.yml +41 -0
- data/test/vcr_cassettes/RequestItem/make_request/make_request_for_a_locally_available_item.yml +90 -0
- data/test/vcr_cassettes/RequestItem/make_request/make_request_for_a_requestable_item.yml +89 -0
- data/test/vcr_cassettes/RequestItem/make_request/make_request_for_an_unrequestable_item.yml +91 -0
- data/test/vcr_cassettes/RequestItem/make_request/raises_for_unrequestable.yml +91 -0
- data/test/vcr_cassettes/RequestItem/make_request/returns_number_for_succesful_request.yml +89 -0
- data/test/vcr_cassettes/RequestItem/make_request/says_no_for_item_that_BD_returns_PUBRI004.yml +89 -0
- data/test/vcr_cassettes/RequestItem/raw_requests_an_unrequestable_item.yml +91 -0
- data/test/vcr_cassettes/RequestItem/uses_manually_set_auth_id.yml +89 -0
- data/test/vcr_cassettes/RequestItem/with_pickup_location_and_requestable_item/still_works.yml +90 -0
- data/test/vcr_cassettes/RequestQuery/raw_request_query_request/returns_results.yml +381 -0
- data/test/vcr_cassettes/RequestQuery/raw_request_to_verify_the_BD_HTTP_API.yml +381 -0
- data/test/vcr_cassettes/RequestQuery/requests/fetches_default_records.yml +384 -0
- data/test/vcr_cassettes/RequestQuery/requests/fetches_full_records.yml +481 -0
- data/test/vcr_cassettes/top_level_describe/an_inner_describe/.yml +76 -0
- metadata +262 -0
data/bd_metrics/oclc.txt
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
10065659
|
2
|
+
10586552
|
3
|
+
1081684
|
4
|
+
10904204
|
5
|
+
1124917
|
6
|
+
11803073
|
7
|
+
11910543
|
8
|
+
123077828
|
9
|
+
123209896
|
10
|
+
12748813
|
11
|
+
1331857
|
12
|
+
1379728
|
13
|
+
1424222
|
14
|
+
1434991
|
15
|
+
1478791
|
16
|
+
1480801
|
17
|
+
14814735
|
18
|
+
1536398
|
19
|
+
1564931
|
20
|
+
1565627
|
21
|
+
1567377
|
22
|
+
1586310
|
23
|
+
1638942
|
24
|
+
1639814
|
25
|
+
16402936
|
26
|
+
1643323
|
27
|
+
1644869
|
28
|
+
1645522
|
29
|
+
1714985
|
30
|
+
1751778
|
31
|
+
1751795
|
32
|
+
1752305
|
33
|
+
1755507
|
34
|
+
1763246
|
35
|
+
1764190
|
36
|
+
1765691
|
37
|
+
1767509
|
38
|
+
1779849
|
39
|
+
18141357
|
40
|
+
18654422
|
41
|
+
190859513
|
42
|
+
2033274
|
43
|
+
20400968
|
44
|
+
22167445
|
45
|
+
2251554
|
46
|
+
226114275
|
47
|
+
2264017
|
48
|
+
232115955
|
49
|
+
232117723
|
50
|
+
24072815
|
51
|
+
2410175
|
52
|
+
2445140
|
53
|
+
2464767
|
54
|
+
247189142
|
55
|
+
2520764
|
56
|
+
29874562
|
57
|
+
2993219
|
58
|
+
300267691
|
59
|
+
30404911
|
60
|
+
31042626
|
61
|
+
31218891
|
62
|
+
31949646
|
63
|
+
3306860
|
64
|
+
34085858
|
65
|
+
34134679
|
66
|
+
34601690
|
67
|
+
36289392
|
68
|
+
36491673
|
69
|
+
36818836
|
70
|
+
36950362
|
71
|
+
37320198
|
72
|
+
37939040
|
73
|
+
38161063
|
74
|
+
38309698
|
75
|
+
38537165
|
76
|
+
38589589
|
77
|
+
38866515
|
78
|
+
38871666
|
79
|
+
38911795
|
80
|
+
38912661
|
81
|
+
38945637
|
82
|
+
39003731
|
83
|
+
3900884
|
84
|
+
39148292
|
85
|
+
39224682
|
86
|
+
41070319
|
87
|
+
41092744
|
88
|
+
41407365
|
89
|
+
41882904
|
90
|
+
41973550
|
91
|
+
41978558
|
92
|
+
42635201
|
93
|
+
4339970
|
94
|
+
43437977
|
95
|
+
43573821
|
96
|
+
43829445
|
97
|
+
44178619
|
98
|
+
44519004
|
99
|
+
44520978
|
100
|
+
44685364
|
101
|
+
44715152
|
102
|
+
45267760
|
103
|
+
45421405
|
104
|
+
4652347
|
105
|
+
46822658
|
106
|
+
47075834
|
107
|
+
47076058
|
108
|
+
47377635
|
109
|
+
47671164
|
110
|
+
47727849
|
111
|
+
47810818
|
112
|
+
47815833
|
113
|
+
48017624
|
114
|
+
48151618
|
115
|
+
48801390
|
116
|
+
48880094
|
117
|
+
48887313
|
118
|
+
49286131
|
119
|
+
49604992
|
120
|
+
498003413
|
121
|
+
49849538
|
122
|
+
49920040
|
123
|
+
50167015
|
124
|
+
50245479
|
125
|
+
50427037
|
126
|
+
50732308
|
127
|
+
50784557
|
128
|
+
51244236
|
129
|
+
51761890
|
130
|
+
52616475
|
131
|
+
54449599
|
132
|
+
5461534
|
133
|
+
55201024
|
134
|
+
5520474
|
135
|
+
55737776
|
136
|
+
55803866
|
137
|
+
57004362
|
138
|
+
57378025
|
139
|
+
60616144
|
140
|
+
60620970
|
141
|
+
60623349
|
142
|
+
60624664
|
143
|
+
60626878
|
144
|
+
60627706
|
145
|
+
60628611
|
146
|
+
607405987
|
147
|
+
61125729
|
148
|
+
611639238
|
149
|
+
61311169
|
150
|
+
613617204
|
151
|
+
61523343
|
152
|
+
6291542
|
153
|
+
6294105
|
154
|
+
641269190
|
155
|
+
6415579
|
156
|
+
67618577
|
157
|
+
696271299
|
158
|
+
702602240
|
159
|
+
762029593
|
160
|
+
809393
|
161
|
+
8287652
|
162
|
+
830989328
|
163
|
+
84844215
|
164
|
+
85489765
|
165
|
+
94139525
|
166
|
+
9438862
|
167
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'borrow_direct/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "borrow_direct"
|
8
|
+
spec.version = BorrowDirect::VERSION
|
9
|
+
spec.authors = ["Jonathan Rochkind"]
|
10
|
+
spec.email = ["jonathan@dnil.net"]
|
11
|
+
spec.summary = %q{Ruby tools for interacting with the Borrow Direct consortial services}
|
12
|
+
spec.homepage = ""
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency "httpclient", "~> 2.4"
|
21
|
+
|
22
|
+
#spec.add_development_dependency "bundler", ">= 1.6.2", "< 2"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
|
25
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
26
|
+
spec.add_development_dependency "minitest-vcr", ">= 1.0.2", "< 2"
|
27
|
+
spec.add_development_dependency "vcr", "~> 2.9"
|
28
|
+
spec.add_development_dependency "webmock", "~> 1.11"
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'borrow_direct/request'
|
2
|
+
|
3
|
+
module BorrowDirect
|
4
|
+
|
5
|
+
# The BD Authorization API
|
6
|
+
# http://borrowdirect.pbworks.com/w/file/83346673/Authorization%20Service.docx
|
7
|
+
#
|
8
|
+
# For now, always calls with 'patron' type.
|
9
|
+
class Authentication < Request
|
10
|
+
attr_reader :patron_barcode, :patron_library_symbol
|
11
|
+
|
12
|
+
@@api_path = "/portal-service/user/authentication/patron"
|
13
|
+
|
14
|
+
# BorrowDirect::Authentication.new(barcode)
|
15
|
+
# BorrowDirect::Authentication.new(barcode, library_symbol)
|
16
|
+
def initialize(patron_barcode,
|
17
|
+
patron_library_symbol = Defaults.library_symbol)
|
18
|
+
super(@@api_path)
|
19
|
+
|
20
|
+
@patron_barcode = patron_barcode
|
21
|
+
@patron_library_symbol = patron_library_symbol
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns raw Hash results of the Authentication request
|
25
|
+
# See also #get_auth_id
|
26
|
+
def authentication_request
|
27
|
+
self.request authentication_request_hash(self.patron_barcode, self.patron_library_symbol)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Makes a request and returns the "AId" value which is used as input
|
31
|
+
# "AuthorizationId" in other API calls.
|
32
|
+
#
|
33
|
+
# If one can't be obtained for some reason, will raise BorrowDirect::Error
|
34
|
+
def get_auth_id
|
35
|
+
response = authentication_request
|
36
|
+
|
37
|
+
if response["Authentication"] && response["Authentication"]["AuthnUserInfo"] && response["Authentication"]["AuthnUserInfo"]["AId"]
|
38
|
+
return response["Authentication"]["AuthnUserInfo"]["AId"]
|
39
|
+
else
|
40
|
+
raise BorrowDirect::Error.new("Could not obtain AId from Authorization API call: #{response.inspect}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def authentication_request_hash(patron_barcode, library_symbol)
|
45
|
+
{
|
46
|
+
"AuthenticationInformation" => {
|
47
|
+
"LibrarySymbol" => library_symbol,
|
48
|
+
"PatronId" => patron_barcode
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'borrow_direct'
|
2
|
+
|
3
|
+
module BorrowDirect
|
4
|
+
# Some defaults for BD requests, including some you might def want to set
|
5
|
+
# at app boot, perhaps in a Rails initializer.
|
6
|
+
#
|
7
|
+
# To use the production BD system instead of test system:
|
8
|
+
# BorrowDirect::Defaults.api_base = BorrowDirect::Defaults::PRODUCTION_API_BASE
|
9
|
+
#
|
10
|
+
# To set your library's BD symbol as a default:
|
11
|
+
# BorrowDirect::Defaults.library_symbol = "YOURSYMBOL"
|
12
|
+
#
|
13
|
+
# To set a default generic patron barcode to use for FindItem requests
|
14
|
+
# BorrowDirect::Defaults.find_item_patron_barcode = "99999999999"
|
15
|
+
class Defaults
|
16
|
+
TEST_API_BASE = "https://bdtest.relais-host.com/"
|
17
|
+
PRODUCTION_API_BASE = "NOT_YET_AVAILABLE"
|
18
|
+
|
19
|
+
TEST_HTML_BASE = "https://bdtest.relaisd2d.com/service-proxy?command=query"
|
20
|
+
PRODUCTION_HTML_BASE = "https://borrow-direct.relaisd2d.com/service-proxy?command=query"
|
21
|
+
|
22
|
+
class << self
|
23
|
+
attr_accessor :api_base, :partnership_id, :find_item_patron_barcode, :library_symbol
|
24
|
+
attr_accessor :html_base_url
|
25
|
+
|
26
|
+
# used for HTTPClient send, connection, AND receive timeouts, so
|
27
|
+
# theoretically could take 3x this, but unlikely, usually it's just
|
28
|
+
# receive that might timeout. In seconds. Default 30s.
|
29
|
+
attr_accessor :timeout
|
30
|
+
end
|
31
|
+
|
32
|
+
self.api_base = BorrowDirect::Defaults::TEST_API_BASE
|
33
|
+
self.partnership_id = "BD"
|
34
|
+
self.timeout = 30
|
35
|
+
self.html_base_url = TEST_HTML_BASE
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module BorrowDirect
|
2
|
+
class Error < StandardError
|
3
|
+
attr_reader :bd_code
|
4
|
+
|
5
|
+
def initialize(msg, bd_code = nil)
|
6
|
+
@bd_code = bd_code
|
7
|
+
if @bd_code
|
8
|
+
msg = "#{@bd_code}: #{msg}"
|
9
|
+
end
|
10
|
+
super(msg)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
class HttpError < Error ; end
|
16
|
+
class HttpTimeoutError < HttpError ; end
|
17
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'borrow_direct'
|
2
|
+
require 'borrow_direct/request'
|
3
|
+
|
4
|
+
module BorrowDirect
|
5
|
+
# The BorrowDirect FindItem service, for discovering item availability
|
6
|
+
# http://borrowdirect.pbworks.com/w/file/83346676/Find%20Item%20Service.docx
|
7
|
+
#
|
8
|
+
# BorrowDirect::FindItem.new(patron_barcode).bd_requestability?(:isbn => isbn)
|
9
|
+
# # or set BorrowDirect::Defaults.find_item_patron_barcode to make patron barcode
|
10
|
+
# # optional and use a default patron barcode
|
11
|
+
#
|
12
|
+
# You can also use #find_item_request to get the raw BD response as a ruby hash
|
13
|
+
class FindItem < Request
|
14
|
+
attr_reader :patron_barcode, :patron_library_symbol
|
15
|
+
|
16
|
+
@@api_path = "/dws/item/available"
|
17
|
+
@@valid_search_types = %w{ISBN ISSN LCCN OCLC PHRASE}
|
18
|
+
|
19
|
+
|
20
|
+
def initialize(patron_barcode = Defaults.find_item_patron_barcode,
|
21
|
+
patron_library_symbol = Defaults.library_symbol)
|
22
|
+
super(@@api_path)
|
23
|
+
|
24
|
+
@patron_barcode = patron_barcode
|
25
|
+
@patron_library_symbol = patron_library_symbol
|
26
|
+
|
27
|
+
# BD sometimes unpredictably returns this error when it means
|
28
|
+
# "no results", other times it doens't. We don't want to raise on it.
|
29
|
+
self.expected_error_codes << "PUBFI002"
|
30
|
+
end
|
31
|
+
|
32
|
+
# need to send a key and value for a valid exact_search type
|
33
|
+
# type can be string or symbol, lowercase or uppercase.
|
34
|
+
#
|
35
|
+
# Returns the actual complete BD response hash. You may want
|
36
|
+
# #bd_requestable? instead
|
37
|
+
#
|
38
|
+
# finder.find_item_request(:isbn => "12345545456")
|
39
|
+
#
|
40
|
+
# You can request multiple values which BD will treat as an 'OR'/union -- sort
|
41
|
+
# of. BD does unpredictable things here, be careful.
|
42
|
+
#
|
43
|
+
# finder.find_item_request(:isbn => ["12345545456", "99999999"])
|
44
|
+
def find_item_request(options)
|
45
|
+
search_type, search_value = nil, nil
|
46
|
+
options.each_pair do |key, value|
|
47
|
+
if @@valid_search_types.include? key.to_s.upcase
|
48
|
+
if search_type || search_value
|
49
|
+
raise ArgumentError.new("Only one search criteria at a time is allowed: '#{options}'")
|
50
|
+
end
|
51
|
+
|
52
|
+
search_type, search_value = key, value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
unless search_type && search_value
|
56
|
+
raise ArgumentError.new("Missing valid search type and value: '#{options}'")
|
57
|
+
end
|
58
|
+
|
59
|
+
request exact_search_request_hash(search_type, search_value)
|
60
|
+
end
|
61
|
+
|
62
|
+
# need to send a key and value for a valid exact_search type
|
63
|
+
# type can be string or symbol, lowercase or uppercase.
|
64
|
+
#
|
65
|
+
# Returns a BorrowDirect::FindItem::Response object, from which you
|
66
|
+
# can find out requestability, list of pickup locations, etc.
|
67
|
+
def find(options)
|
68
|
+
BorrowDirect::FindItem::Response.new find_item_request(options)
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
# Produce BD request hash for exact search of type eg "ISBN"
|
74
|
+
# value can be a singel value, or an array of values. For array,
|
75
|
+
# BD will "OR" them.
|
76
|
+
def exact_search_request_hash(type, value)
|
77
|
+
# turn it into an array if it's not one already
|
78
|
+
values = Array(value)
|
79
|
+
|
80
|
+
hash = {
|
81
|
+
"PartnershipId" => Defaults.partnership_id,
|
82
|
+
"Credentials" => {
|
83
|
+
"LibrarySymbol" => self.patron_library_symbol,
|
84
|
+
"Barcode" => self.patron_barcode
|
85
|
+
},
|
86
|
+
"ExactSearch" => []
|
87
|
+
}
|
88
|
+
|
89
|
+
values.each do |value|
|
90
|
+
hash["ExactSearch"] << {
|
91
|
+
"Type" => type.to_s.upcase,
|
92
|
+
"Value" => value
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
return hash
|
97
|
+
end
|
98
|
+
|
99
|
+
class Response
|
100
|
+
include BorrowDirect::Util
|
101
|
+
|
102
|
+
attr_reader :response_hash
|
103
|
+
|
104
|
+
def initialize(hash)
|
105
|
+
@response_hash = hash
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# Returns true or false -- can the item actually be requested
|
110
|
+
# via BorrowDirect.
|
111
|
+
#
|
112
|
+
# finder.find(:isbn => "12345545456").requestable?
|
113
|
+
def requestable?
|
114
|
+
# Sometimes a PUBFI002 error code isn't really an error,
|
115
|
+
# but just means not available.
|
116
|
+
if response_hash && response_hash["Error"] && (response_hash["Error"]["ErrorNumber"] == "PUBFI002")
|
117
|
+
return false
|
118
|
+
end
|
119
|
+
|
120
|
+
# Items that are available locally, and thus not requestable via BD, can
|
121
|
+
# only be found by looking at the RequestMessage, bah
|
122
|
+
h = response_hash["Item"]["RequestLink"]
|
123
|
+
if h && h["RequestMessage"] == "This item is available locally"
|
124
|
+
return false
|
125
|
+
end
|
126
|
+
|
127
|
+
return response_hash["Item"]["Available"].to_s == "true"
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns the AuthorizationID returned by FindItem API call,
|
131
|
+
# or nil if none is available. Nil _can_ be returned, for
|
132
|
+
# instance when BD returns a NotFound error instead of a good
|
133
|
+
# response.
|
134
|
+
def auth_id
|
135
|
+
hash_key_path response_hash, "Item", "AuthorizationId"
|
136
|
+
end
|
137
|
+
|
138
|
+
# Can be nil in some cases if not requestable?
|
139
|
+
# if requestable?, should be an array of Strings.
|
140
|
+
def pickup_locations
|
141
|
+
hash_key_path response_hash, "Item", "PickupLocations", "PickupLocation"
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module BorrowDirect
|
4
|
+
# Generate a "deep link" to query results in BD's native
|
5
|
+
# HTML interface.
|
6
|
+
class GenerateQuery
|
7
|
+
attr_accessor :url_base
|
8
|
+
|
9
|
+
# Hash from our own API argument to BD field code
|
10
|
+
@@fields = {
|
11
|
+
:keyword => "term",
|
12
|
+
:title => "ti",
|
13
|
+
:author => "au",
|
14
|
+
:subject => "su",
|
15
|
+
:isbn => "isbn",
|
16
|
+
:issn => "issn"
|
17
|
+
}
|
18
|
+
|
19
|
+
def initialize(url_base = nil)
|
20
|
+
self.url_base = (url_base || BorrowDirect::Defaults.html_base_url)
|
21
|
+
end
|
22
|
+
|
23
|
+
# query_with(:title => "one two", :author => "three four")
|
24
|
+
# valid keys are those supported by BD HTML interface:
|
25
|
+
# :title, :author, :isbn, :subject, :keyword, :isbn, :issn
|
26
|
+
#
|
27
|
+
# For now, the value is always searched as a phrase, and multiple
|
28
|
+
# fields are always 'and'd. We may enhance/expand later.
|
29
|
+
#
|
30
|
+
# Returns an un-escaped query, still needs to be put into a URL
|
31
|
+
def query_with(options)
|
32
|
+
clauses = []
|
33
|
+
|
34
|
+
options.each_pair do |field, value|
|
35
|
+
code = @@fields[field]
|
36
|
+
|
37
|
+
raise ArgumentError.new("Don't recognize field code `#{field}`") unless code
|
38
|
+
|
39
|
+
clauses << %Q{#{code}="#{escape_query_value value}"}
|
40
|
+
end
|
41
|
+
|
42
|
+
return clauses.join(" and ")
|
43
|
+
end
|
44
|
+
|
45
|
+
# Pass in :title, :author, :isbn, etc -- if we have an isbn or issn,
|
46
|
+
# we'll use that alone, otherwise we'll use title and author
|
47
|
+
def best_known_item_query_with(options)
|
48
|
+
if options[:isbn]
|
49
|
+
return query_with(options.dup.delete_if {|k| k != :isbn})
|
50
|
+
elsif options[:issn]
|
51
|
+
return query_with(options.dup.delete_if {|k| k != :issn})
|
52
|
+
else
|
53
|
+
return query_with options
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def query_url_with(options)
|
58
|
+
query = query_with(options)
|
59
|
+
|
60
|
+
return self.url_base + '?' + "query=#{CGI.escape query}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def best_known_item_query_url_with(options)
|
64
|
+
query = best_known_item_query_with(options)
|
65
|
+
|
66
|
+
return self.url_base + '?' + "query=#{CGI.escape query}"
|
67
|
+
end
|
68
|
+
|
69
|
+
# We don't really know how to escape, for now
|
70
|
+
# we just remove double quotes and parens, and replace with spaces.
|
71
|
+
# those seem to cause problems, and that seems to work.
|
72
|
+
def escape_query_value(str)
|
73
|
+
str.gsub(/[")()]/, ' ')
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|