berkeley_library-location 3.0.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +5 -0
- data/README.md +11 -0
- data/lib/berkeley_library/location/module_info.rb +1 -1
- data/lib/berkeley_library/location/world_cat/config.rb +51 -4
- data/lib/berkeley_library/location/world_cat/libraries_request.rb +27 -17
- data/lib/berkeley_library/location/world_cat/oclc_auth.rb +65 -0
- metadata +37 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcdd92dafd937a6dabe9cf2104e84311ae590d2db023b80c9c6fb06d0a1f3b40
|
4
|
+
data.tar.gz: fc15382505d0611b660cdca1e5575b780ca00a0a13cb65cf5dec1d3c2a07a501
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7f912cc954ceaf098cd7711cffc6016c0cfa945d7b63eb46e1a6c546d321962df4e76abb8e21ee8d57153590cacc7996f4d6ad4a90ae2f40fe91b46467b2e98
|
7
|
+
data.tar.gz: b8096c7cdc373ac6d79f0b966c2cfce33c959cc562c8b0d002ee1265ba6c0fb38eda81fb46ef8fff9accf6e033cef5b1b7e952bc96926b06eb3198235452d540
|
data/CHANGES.md
CHANGED
data/README.md
CHANGED
@@ -4,3 +4,14 @@
|
|
4
4
|
[![Gem Version](https://img.shields.io/gem/v/berkeley_library-location.svg)](https://github.com/BerkeleyLibrary/location/releases)
|
5
5
|
|
6
6
|
Miscellaneous location-related utilities for the UC Berkeley Library.
|
7
|
+
|
8
|
+
Updated to Worldcat API Version 2:
|
9
|
+
https://developer.api.oclc.org/wcv2#/Member%20General%20Holdings/find-bib-holdings
|
10
|
+
|
11
|
+
|
12
|
+
NOTE: For new and updated tests we've implemented the use of VCR to handle API mocks. Therefore in order to run tests, you'll need to have your API Key and Secret set as environment variables:
|
13
|
+
LIT_WORLDCAT_API_KEY
|
14
|
+
LIT_WORLDCAT_API_SECRET
|
15
|
+
|
16
|
+
Once the cassettes are created you can force a re-recording of cassettes by adding the following ENV:
|
17
|
+
RE_RECORD_VCR="true"
|
@@ -7,7 +7,7 @@ module BerkeleyLibrary
|
|
7
7
|
SUMMARY = 'Locaton-related utilities for the UC Berkeley Library'.freeze
|
8
8
|
DESCRIPTION = 'A collection of location-related utilities for the UC Berkeley Library'.freeze
|
9
9
|
LICENSE = 'MIT'.freeze
|
10
|
-
VERSION = '
|
10
|
+
VERSION = '4.0.0'.freeze
|
11
11
|
HOMEPAGE = 'https://github.com/BerkeleyLibrary/location'.freeze
|
12
12
|
end
|
13
13
|
end
|
@@ -6,22 +6,30 @@ module BerkeleyLibrary
|
|
6
6
|
module Config
|
7
7
|
include BerkeleyLibrary::Util::URIs
|
8
8
|
|
9
|
-
# The environment variable from which to read the WorldCat API key.
|
9
|
+
# The environment variable from which to read the WorldCat API key and secret.
|
10
10
|
ENV_WORLDCAT_API_KEY = 'LIT_WORLDCAT_API_KEY'.freeze
|
11
|
+
ENV_WORLDCAT_API_SECRET = 'LIT_WORLDCAT_API_SECRET'.freeze
|
11
12
|
|
12
13
|
# The environment variable from which to read the WorldCat base URL.
|
13
14
|
ENV_WORLDCAT_BASE_URL = 'LIT_WORLDCAT_BASE_URL'.freeze
|
14
15
|
|
16
|
+
# The environment variable from which to read the OCLC Token URL.
|
17
|
+
ENV_OCLC_TOKEN_URL = 'LIT_OCLC_TOKEN_URL'.freeze
|
18
|
+
|
15
19
|
# The default WorldCat base URL, if ENV_WORLDCAT_BASE_URL is not set.
|
16
|
-
DEFAULT_WORLDCAT_BASE_URL = 'https://www.worldcat.org/webservices/'.freeze
|
20
|
+
# DEFAULT_WORLDCAT_BASE_URL = 'https://www.worldcat.org/webservices/'.freeze
|
21
|
+
DEFAULT_WORLDCAT_BASE_URL = 'https://americas.discovery.api.oclc.org/worldcat/search/v2/'.freeze
|
22
|
+
|
23
|
+
# The default OCLC Token URL, if ENV_OCLC_TOKEN_URL is not set.
|
24
|
+
DEFAULT_OCLC_TOKEN_URL = 'https://oauth.oclc.org/token'.freeze
|
17
25
|
|
18
26
|
class << self
|
19
27
|
include Config
|
20
28
|
end
|
21
29
|
|
22
|
-
# Sets the WorldCat API key
|
30
|
+
# Sets the WorldCat API key and secret
|
23
31
|
# @param value [String] the API key.
|
24
|
-
attr_writer :api_key
|
32
|
+
attr_writer :api_key, :api_secret
|
25
33
|
|
26
34
|
# Gets the WorldCat API key.
|
27
35
|
# @return [String, nil] the WorldCat API key, or `nil` if not set.
|
@@ -29,14 +37,27 @@ module BerkeleyLibrary
|
|
29
37
|
@api_key ||= default_worldcat_api_key
|
30
38
|
end
|
31
39
|
|
40
|
+
# Sets the WorldCat API secret.
|
41
|
+
def api_secret
|
42
|
+
@api_secret ||= default_worldcat_api_secret
|
43
|
+
end
|
44
|
+
|
32
45
|
def base_uri
|
33
46
|
@base_uri ||= default_worldcat_base_uri
|
34
47
|
end
|
35
48
|
|
49
|
+
def token_uri
|
50
|
+
@token_uri ||= default_oclc_token_uri
|
51
|
+
end
|
52
|
+
|
36
53
|
def base_uri=(value)
|
37
54
|
@base_uri = uri_or_nil(value)
|
38
55
|
end
|
39
56
|
|
57
|
+
def token_uri=(value)
|
58
|
+
@token_uri = uri_or_nil(value)
|
59
|
+
end
|
60
|
+
|
40
61
|
private
|
41
62
|
|
42
63
|
def reset!
|
@@ -47,14 +68,26 @@ module BerkeleyLibrary
|
|
47
68
|
ENV[ENV_WORLDCAT_API_KEY] || rails_worldcat_api_key
|
48
69
|
end
|
49
70
|
|
71
|
+
def default_worldcat_api_secret
|
72
|
+
ENV[ENV_WORLDCAT_API_SECRET] || rails_worldcat_api_secret
|
73
|
+
end
|
74
|
+
|
50
75
|
def default_worldcat_base_uri
|
51
76
|
uri_or_nil(default_worldcat_base_url)
|
52
77
|
end
|
53
78
|
|
79
|
+
def default_oclc_token_uri
|
80
|
+
uri_or_nil(default_oclc_token_url)
|
81
|
+
end
|
82
|
+
|
54
83
|
def default_worldcat_base_url
|
55
84
|
ENV[ENV_WORLDCAT_BASE_URL] || rails_worldcat_base_url || DEFAULT_WORLDCAT_BASE_URL
|
56
85
|
end
|
57
86
|
|
87
|
+
def default_oclc_token_url
|
88
|
+
ENV[ENV_OCLC_TOKEN_URL] || rails_oclc_token_url || DEFAULT_OCLC_TOKEN_URL
|
89
|
+
end
|
90
|
+
|
58
91
|
def rails_worldcat_base_url
|
59
92
|
return unless (rails_config = self.rails_config)
|
60
93
|
return unless rails_config.respond_to?(:worldcat_base_url)
|
@@ -62,6 +95,13 @@ module BerkeleyLibrary
|
|
62
95
|
rails_config.worldcat_base_url
|
63
96
|
end
|
64
97
|
|
98
|
+
def rails_oclc_token_url
|
99
|
+
return unless (rails_config = self.rails_config)
|
100
|
+
return unless rails_config.respond_to?(:oclc_token_url)
|
101
|
+
|
102
|
+
rails_config.oclc_token_url
|
103
|
+
end
|
104
|
+
|
65
105
|
def rails_worldcat_api_key
|
66
106
|
return unless (rails_config = self.rails_config)
|
67
107
|
return unless rails_config.respond_to?(:worldcat_api_key)
|
@@ -69,6 +109,13 @@ module BerkeleyLibrary
|
|
69
109
|
rails_config.worldcat_api_key
|
70
110
|
end
|
71
111
|
|
112
|
+
def rails_worldcat_api_secret
|
113
|
+
return unless (rails_config = self.rails_config)
|
114
|
+
return unless rails_config.respond_to?(:worldcat_api_secret)
|
115
|
+
|
116
|
+
rails_config.worldcat_api_secret
|
117
|
+
end
|
118
|
+
|
72
119
|
def rails_config
|
73
120
|
return unless defined?(Rails)
|
74
121
|
return unless (app = Rails.application)
|
@@ -1,55 +1,65 @@
|
|
1
1
|
require 'nokogiri'
|
2
|
+
require 'json'
|
3
|
+
require 'jsonpath'
|
2
4
|
require 'berkeley_library/util'
|
3
5
|
require 'berkeley_library/location/oclc_number'
|
4
6
|
require 'berkeley_library/location/world_cat/symbols'
|
7
|
+
require 'berkeley_library/location/world_cat/oclc_auth'
|
5
8
|
|
6
9
|
module BerkeleyLibrary
|
7
10
|
module Location
|
8
11
|
module WorldCat
|
9
|
-
# @see https://developer.api.oclc.org/
|
12
|
+
# @see https://developer.api.oclc.org/wcv2#/Member%20General%20Holdings/find-bib-holdings
|
10
13
|
class LibrariesRequest
|
11
14
|
include BerkeleyLibrary::Util
|
12
15
|
|
13
|
-
|
16
|
+
JPATH_INST_ID_VALS = '$.briefRecords[*].institutionHolding.briefHoldings[*].oclcSymbol'.freeze
|
14
17
|
|
15
18
|
attr_reader :oclc_number, :symbols
|
16
19
|
|
17
20
|
def initialize(oclc_number, symbols: Symbols::ALL)
|
21
|
+
@oclc_token = OCLCAuth.instance
|
18
22
|
@oclc_number = OCLCNumber.ensure_oclc_number!(oclc_number)
|
19
23
|
@symbols = Symbols.ensure_valid!(symbols)
|
20
24
|
end
|
21
25
|
|
22
26
|
def uri
|
23
|
-
@uri ||= URIs.append(libraries_base_uri
|
27
|
+
@uri ||= URIs.append(libraries_base_uri)
|
24
28
|
end
|
25
29
|
|
26
|
-
# TODO: Check that this works w/more than 10 results
|
27
|
-
# See https://developer.api.oclc.org/wcv1#/Holdings
|
28
30
|
def params
|
29
31
|
@params ||= {
|
30
|
-
'
|
31
|
-
'
|
32
|
-
|
33
|
-
|
32
|
+
'oclcNumber' => oclc_number,
|
33
|
+
'heldBySymbol' => symbols.join(',')
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def headers
|
38
|
+
@headers ||= {
|
39
|
+
'Authorization' => "Bearer #{oclc_token.access_token}"
|
34
40
|
}
|
35
41
|
end
|
36
42
|
|
37
43
|
def execute
|
38
|
-
response_body = URIs.get(uri, params:, log: false)
|
39
|
-
|
40
|
-
inst_symbols.select { |sym| symbols.include?(sym) } # just in case
|
44
|
+
response_body = URIs.get(uri, params:, headers:, log: false)
|
45
|
+
inst_symbols_from(response_body)
|
41
46
|
end
|
42
47
|
|
43
48
|
private
|
44
49
|
|
50
|
+
# OCLC changed to a token-based authentication system
|
51
|
+
# separating auth from request
|
52
|
+
def oclc_token
|
53
|
+
@oclc_token ||= OCLCAuth.new
|
54
|
+
end
|
55
|
+
|
45
56
|
def libraries_base_uri
|
46
|
-
URIs.append(Config.base_uri, '
|
57
|
+
URIs.append(Config.base_uri, 'bibs-holdings')
|
47
58
|
end
|
48
59
|
|
49
|
-
def inst_symbols_from(
|
50
|
-
|
51
|
-
|
52
|
-
id_vals.filter_map { |value| value.text.strip }
|
60
|
+
def inst_symbols_from(json)
|
61
|
+
path = JsonPath.new(JPATH_INST_ID_VALS)
|
62
|
+
path.on(json)
|
53
63
|
end
|
54
64
|
end
|
55
65
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module BerkeleyLibrary
|
5
|
+
module Location
|
6
|
+
module WorldCat
|
7
|
+
class OCLCAuth
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
attr_accessor :token
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
# rubocop:disable Lint/DisjunctiveAssignmentInConstructor
|
14
|
+
@token ||= fetch_token
|
15
|
+
# rubocop:enable Lint/DisjunctiveAssignmentInConstructor:
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch_token
|
19
|
+
url = oclc_token_url
|
20
|
+
|
21
|
+
http = Net::HTTP.new(url.host, url.port)
|
22
|
+
http.use_ssl = url.scheme == 'https'
|
23
|
+
|
24
|
+
request = Net::HTTP::Post.new(url.request_uri)
|
25
|
+
request.basic_auth(Config.api_key, Config.api_secret)
|
26
|
+
request['Accept'] = 'application/json'
|
27
|
+
response = http.request(request)
|
28
|
+
|
29
|
+
JSON.parse(response.body, symbolize_names: true)
|
30
|
+
end
|
31
|
+
|
32
|
+
# def token
|
33
|
+
# @token = get_token if token_expired?
|
34
|
+
# @token
|
35
|
+
# end
|
36
|
+
|
37
|
+
def oclc_token_url
|
38
|
+
URI.parse("#{Config.token_uri}?#{URI.encode_www_form(token_params)}")
|
39
|
+
end
|
40
|
+
|
41
|
+
# Before every request check if the token is expired (OCLC tokens expire after 20 minutes)
|
42
|
+
def access_token
|
43
|
+
@token = token if token_expired?
|
44
|
+
@token[:access_token]
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def token_params
|
50
|
+
{
|
51
|
+
grant_type: 'client_credentials',
|
52
|
+
scope: 'wcapi:view_institution_holdings'
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def token_expired?
|
57
|
+
return true if @token.nil? || @token[:expires_at].nil?
|
58
|
+
|
59
|
+
Time.parse(@token[:expires_at]) <= Time.now
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: berkeley_library-location
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- UC Berkeley Library IT
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-01-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: berkeley_library-logging
|
@@ -44,6 +44,20 @@ dependencies:
|
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: 0.1.9
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: jsonpath
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.5.8
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.5.8
|
47
61
|
- !ruby/object:Gem::Dependency
|
48
62
|
name: marcel
|
49
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -218,14 +232,14 @@ dependencies:
|
|
218
232
|
requirements:
|
219
233
|
- - "~>"
|
220
234
|
- !ruby/object:Gem::Version
|
221
|
-
version:
|
235
|
+
version: 1.7.1
|
222
236
|
type: :development
|
223
237
|
prerelease: false
|
224
238
|
version_requirements: !ruby/object:Gem::Requirement
|
225
239
|
requirements:
|
226
240
|
- - "~>"
|
227
241
|
- !ruby/object:Gem::Version
|
228
|
-
version:
|
242
|
+
version: 1.7.1
|
229
243
|
- !ruby/object:Gem::Dependency
|
230
244
|
name: simplecov
|
231
245
|
requirement: !ruby/object:Gem::Requirement
|
@@ -240,6 +254,20 @@ dependencies:
|
|
240
254
|
- - "~>"
|
241
255
|
- !ruby/object:Gem::Version
|
242
256
|
version: '0.21'
|
257
|
+
- !ruby/object:Gem::Dependency
|
258
|
+
name: vcr
|
259
|
+
requirement: !ruby/object:Gem::Requirement
|
260
|
+
requirements:
|
261
|
+
- - "~>"
|
262
|
+
- !ruby/object:Gem::Version
|
263
|
+
version: '6.1'
|
264
|
+
type: :development
|
265
|
+
prerelease: false
|
266
|
+
version_requirements: !ruby/object:Gem::Requirement
|
267
|
+
requirements:
|
268
|
+
- - "~>"
|
269
|
+
- !ruby/object:Gem::Version
|
270
|
+
version: '6.1'
|
243
271
|
- !ruby/object:Gem::Dependency
|
244
272
|
name: webmock
|
245
273
|
requirement: !ruby/object:Gem::Requirement
|
@@ -276,6 +304,7 @@ files:
|
|
276
304
|
- lib/berkeley_library/location/world_cat.rb
|
277
305
|
- lib/berkeley_library/location/world_cat/config.rb
|
278
306
|
- lib/berkeley_library/location/world_cat/libraries_request.rb
|
307
|
+
- lib/berkeley_library/location/world_cat/oclc_auth.rb
|
279
308
|
- lib/berkeley_library/location/world_cat/symbols.rb
|
280
309
|
- lib/berkeley_library/location/xlsx_reader.rb
|
281
310
|
- lib/berkeley_library/location/xlsx_writer.rb
|
@@ -291,7 +320,7 @@ metadata:
|
|
291
320
|
source_code_uri: https://github.com/BerkeleyLibrary/location
|
292
321
|
changelog_uri: https://github.com/BerkeleyLibrary/location/CHANGELOG.md
|
293
322
|
rubygems_mfa_required: 'true'
|
294
|
-
post_install_message:
|
323
|
+
post_install_message:
|
295
324
|
rdoc_options: []
|
296
325
|
require_paths:
|
297
326
|
- lib
|
@@ -306,8 +335,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
306
335
|
- !ruby/object:Gem::Version
|
307
336
|
version: '0'
|
308
337
|
requirements: []
|
309
|
-
rubygems_version: 3.4.
|
310
|
-
signing_key:
|
338
|
+
rubygems_version: 3.4.19
|
339
|
+
signing_key:
|
311
340
|
specification_version: 4
|
312
341
|
summary: Locaton-related utilities for the UC Berkeley Library
|
313
342
|
test_files: []
|