borrow_direct 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|