Skip to content

Commit c3704b4

Browse files
v4.2.0: rename RLF to SLF
* Updates for RLF to SLF name change * Fix OCLC specs and coverage * Update upload-artifact to v4 * Set it up to over-write artifacts * Give artifacts unique names rather than over-write them
1 parent 38d7103 commit c3704b4

File tree

18 files changed

+279
-265
lines changed

18 files changed

+279
-265
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828

2929
- name: Upload artifacts
3030
if: ${{ always() }}
31-
uses: actions/upload-artifact@v3
31+
uses: actions/upload-artifact@v4
3232
with:
33-
name: artifacts
33+
name: artifacts-${{ matrix.os }}-${{ matrix.ruby }}
3434
path: artifacts/**

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 4.2.0 (2026-01-06)
2+
3+
- Rename RLF to SLF (NRLF -> SLFN, SRLF -> SLFS)
4+
- Added bypass to skip SSL verification ONLY when recording new VCR cassettes
5+
16
# 4.1.0 (2025-01-10)
27

38
- Fixed token refresh function and test

lib/berkeley_library/location/location_result.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ def initialize(oclc_number, wc_symbols: [], wc_error: nil, ht_record_url: nil, h
1313
@ht_error = ht_error
1414
end
1515

16-
def nrlf?
17-
@has_nrlf ||= wc_symbols.intersection(WorldCat::Symbols::NRLF).any?
16+
def slfn?
17+
@has_slfn ||= wc_symbols.intersection(WorldCat::Symbols::SLFN).any?
1818
end
1919

20-
def srlf?
21-
@has_srlf ||= wc_symbols.intersection(WorldCat::Symbols::SRLF).any?
20+
def slfs?
21+
@has_slfs ||= wc_symbols.intersection(WorldCat::Symbols::SLFS).any?
2222
end
2323

2424
def uc_symbols

lib/berkeley_library/location/world_cat/oclc_auth.rb

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,7 @@ def initialize
1717
end
1818

1919
def fetch_token
20-
url = oclc_token_url
21-
22-
http = Net::HTTP.new(url.host, url.port)
23-
http.use_ssl = url.scheme == 'https'
24-
25-
request = Net::HTTP::Post.new(url.request_uri)
26-
request.basic_auth(Config.api_key, Config.api_secret)
27-
request['Accept'] = 'application/json'
28-
response = http.request(request)
29-
20+
response = http_request(oclc_token_url)
3021
JSON.parse(response.body, symbolize_names: true)
3122
end
3223

@@ -42,6 +33,23 @@ def access_token
4233

4334
private
4435

36+
def http_request(url)
37+
http = build_http(url)
38+
request = Net::HTTP::Post.new(url.request_uri)
39+
request.basic_auth(Config.api_key, Config.api_secret)
40+
request['Accept'] = 'application/json'
41+
http.request(request)
42+
end
43+
44+
def build_http(url)
45+
http = Net::HTTP.new(url.host, url.port)
46+
http.use_ssl = url.scheme == 'https'
47+
48+
# Skip SSL verification ONLY when recording new VCR cassettes
49+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if ENV['RE_RECORD_VCR'] == 'true'
50+
http
51+
end
52+
4553
def token_params
4654
{
4755
grant_type: 'client_credentials',

lib/berkeley_library/location/world_cat/symbols.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ module BerkeleyLibrary
22
module Location
33
module WorldCat
44
module Symbols
5-
NRLF = %w[ZAP ZAPSP].freeze
6-
SRLF = %w[HH0 ZAS ZASSP].freeze
7-
RLF = (NRLF + SRLF).freeze
5+
SLFN = %w[ZAP ZAPSP].freeze
6+
SLFS = %w[HH0 ZAS ZASSP].freeze
7+
SLF = (SLFN + SLFS).freeze
88

99
UC = %w[CLU CRU CUI CUN CUS CUT CUV CUX CUY CUZ MERUC].freeze
10-
ALL = (RLF + UC).freeze
10+
ALL = (SLF + UC).freeze
1111

1212
class << self
1313
include Symbols

lib/berkeley_library/location/xlsx_writer.rb

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@ class XLSXWriter
77
include Constants
88
include BerkeleyLibrary::Logging
99

10-
COL_NRLF = 'NRLF'.freeze
11-
COL_SRLF = 'SRLF'.freeze
10+
COL_SLFN = 'SLFN'.freeze
11+
COL_SLFS = 'SLFS'.freeze
1212
COL_OTHER_UC = 'Other UC'.freeze
1313
COL_WC_ERROR = 'WorldCat Error'.freeze
1414

1515
COL_HATHI_TRUST = 'Hathi Trust'.freeze
1616
COL_HATHI_TRUST_ERROR = "#{COL_HATHI_TRUST} Error".freeze
1717

18-
V_NRLF = 'nrlf'.freeze
19-
V_SRLF = 'srlf'.freeze
18+
V_SLFN = 'slfn'.freeze
19+
V_SLFS = 'slfs'.freeze
2020

21-
attr_reader :ss, :rlf, :uc, :hathi_trust
21+
attr_reader :ss, :slf, :uc, :hathi_trust
2222

23-
def initialize(ss, rlf: true, uc: true, hathi_trust: true)
23+
def initialize(ss, slf: true, uc: true, hathi_trust: true)
2424
@ss = ss
25-
@rlf = rlf
25+
@slf = slf
2626
@uc = uc
2727
@hathi_trust = hathi_trust
2828

@@ -32,7 +32,7 @@ def initialize(ss, rlf: true, uc: true, hathi_trust: true)
3232
def <<(result)
3333
r_indices = row_indices_for(result.oclc_number)
3434
r_indices.each do |idx|
35-
write_wc_cols(idx, result) if rlf || uc
35+
write_wc_cols(idx, result) if slf || uc
3636
write_ht_cols(idx, result) if hathi_trust
3737
end
3838
end
@@ -41,7 +41,7 @@ def <<(result)
4141

4242
def write_wc_cols(r_index, result)
4343
write_wc_error(r_index, result)
44-
write_rlf(r_index, result) if rlf
44+
write_slf(r_index, result) if slf
4545
write_uc(r_index, result) if uc
4646
end
4747

@@ -51,9 +51,9 @@ def write_ht_cols(r_index, result)
5151
end
5252

5353
def ensure_columns!
54-
if rlf
55-
nrlf_col_index
56-
srlf_col_index
54+
if slf
55+
slfn_col_index
56+
slfs_col_index
5757
end
5858
uc_col_index if uc
5959
ht_col_index if hathi_trust
@@ -66,9 +66,9 @@ def row_indices_for(oclc_number)
6666
raise ArgumentError, "Unknown OCLC number: #{oclc_number}"
6767
end
6868

69-
def write_rlf(r_index, result)
70-
ss.set_value_at(r_index, nrlf_col_index, V_NRLF) if result.nrlf?
71-
ss.set_value_at(r_index, srlf_col_index, V_SRLF) if result.srlf?
69+
def write_slf(r_index, result)
70+
ss.set_value_at(r_index, slfn_col_index, V_SLFN) if result.slfn?
71+
ss.set_value_at(r_index, slfs_col_index, V_SLFS) if result.slfs?
7272
end
7373

7474
def write_uc(r_index, result)
@@ -99,12 +99,12 @@ def oclc_col_index
9999
@oclc_col_index ||= ss.find_column_index_by_header!(OCLC_COL_HEADER)
100100
end
101101

102-
def nrlf_col_index
103-
@nrlf_col_index ||= ss.ensure_column!(COL_NRLF)
102+
def slfn_col_index
103+
@slfn_col_index ||= ss.ensure_column!(COL_SLFN)
104104
end
105105

106-
def srlf_col_index
107-
@srlf_col_index ||= ss.ensure_column!(COL_SRLF)
106+
def slfs_col_index
107+
@slfs_col_index ||= ss.ensure_column!(COL_SLFS)
108108
end
109109

110110
def uc_col_index

spec/berkeley_library/location/world_cat/libraries_request_spec.rb

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,24 @@ module Location
33
module WorldCat
44
describe LibrariesRequest do
55
let(:oclc_number) { '85833285' }
6-
# let(:wc_base_url) { 'https://www.example.test/webservices/' }
76
let(:wc_base_url) { 'https://americas.discovery.api.oclc.org/worldcat/search/v2/' }
87
let(:wc_api_key) { '2lo55pdh7moyfodeo4gwgms0on65x31ghv0g6yg87ffwaljsdw' }
98
let(:wc_api_secret) { 'totallyfakesecret' }
109

11-
# before do
12-
# # Config.base_uri = wc_base_url
13-
# # Config.api_key = wc_api_key
14-
# # Config.api_secret = wc_api_secret
15-
# end
10+
before do
11+
fake_auth = instance_double(
12+
BerkeleyLibrary::Location::WorldCat::OCLCAuth,
13+
access_token: 'fake-access-token'
14+
)
15+
16+
allow(BerkeleyLibrary::Location::WorldCat::OCLCAuth)
17+
.to receive(:instance)
18+
.and_return(fake_auth)
19+
end
1620

1721
after do
18-
Config.send(:reset!)
22+
BerkeleyLibrary::Location::WorldCat::OCLCAuth
23+
.instance_variable_set(:@singleton__instance__, nil)
1924
end
2025

2126
describe :new do
@@ -65,7 +70,7 @@ module WorldCat
6570
end
6671

6772
it 'rejects an array containing nonexistent symbols' do
68-
bad_symbols = [Symbols::NRLF, ['not a WorldCat institution symbol'], Symbols::SRLF].flatten
73+
bad_symbols = [Symbols::SLFN, ['not a WorldCat institution symbol'], Symbols::SLFS].flatten
6974
expect { LibrariesRequest.new(oclc_number, symbols: bad_symbols) }.to raise_error(ArgumentError)
7075
end
7176
end
@@ -92,7 +97,7 @@ module WorldCat
9297

9398
it 'returns a specified subset of holdings' do
9499
holdings_expected = %w[ZAP]
95-
symbols = Symbols::RLF
100+
symbols = Symbols::SLF
96101
req = LibrariesRequest.new(oclc_number, symbols:)
97102

98103
VCR.use_cassette('libraries_request/execute_holdings_2') do
@@ -116,7 +121,7 @@ module WorldCat
116121

117122
it 'returns an empty list when no holdings are found' do
118123
oclc_number = '10045193'
119-
symbols = Symbols::RLF
124+
symbols = Symbols::SLF
120125
req = LibrariesRequest.new(oclc_number, symbols:)
121126

122127
VCR.use_cassette('libraries_request/execute_holdings_4') do

spec/berkeley_library/location/world_cat/oclc_auth_spec.rb

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
require 'spec_helper'
22
require 'time'
3+
require 'active_support/testing/time_helpers'
34

45
module BerkeleyLibrary
56
module Location
67
module WorldCat
78
describe OCLCAuth do
9+
include ActiveSupport::Testing::TimeHelpers
10+
811
it 'fetches a token' do
912
VCR.use_cassette('oclc_auth/fetch_token') do
1013
token = OCLCAuth.instance.token
@@ -14,25 +17,24 @@ module WorldCat
1417
end
1518

1619
it 'refreshes an expired token' do
17-
VCR.use_cassette('oclc_auth/refresh_token') do
18-
# First get a token....
19-
token = OCLCAuth.instance.token
20+
freeze_time do
21+
auth = OCLCAuth.instance
2022

21-
# Need to set the token expiration to a time in the past
22-
token[:expires_at] = (Time.now - 60).to_s
23-
token[:access_token] = 'expired_token'
23+
# Simulate an expired token
24+
expired_token = { access_token: 'expired_token', expires_at: (Time.current - 60).to_s }
25+
auth.token = expired_token
2426

25-
# Now we need to set the token instance to the token with the updated expiration
26-
OCLCAuth.instance.token = token
27+
# Stub fetch_token to return a fresh token
28+
new_token = { access_token: 'new_token', expires_at: (Time.current + 3600).to_s }
29+
allow(auth).to receive(:fetch_token).and_return(new_token)
2730

2831
# Trigger a refresh by calling access_token
29-
OCLCAuth.instance.access_token
32+
result = auth.access_token
3033

31-
# Now check that the token has been refreshed
32-
token = OCLCAuth.instance.token
33-
34-
expect(token[:access_token]).not_to eq('expired_token')
35-
expect(Time.parse(token[:expires_at])).to be >= Time.now
34+
# Check that the token was refreshed
35+
expect(result).to eq('new_token')
36+
expect(auth.token[:access_token]).to eq('new_token')
37+
expect(Time.parse(auth.token[:expires_at])).to be >= Time.current
3638
end
3739
end
3840

@@ -44,6 +46,58 @@ module WorldCat
4446
expect(oclc_auth.send(:token_expired?)).to be true
4547
end
4648
end
49+
50+
describe '#fetch_token SSL branch' do
51+
it 'does not disable SSL verification when RE_RECORD_VCR is not true' do
52+
# Clear RE_RECORD_VCR
53+
allow(ENV).to receive(:[]).with('RE_RECORD_VCR').and_return(nil)
54+
55+
url = URI('https://example.test/token')
56+
http = instance_double(Net::HTTP)
57+
allow(Net::HTTP).to receive(:new).and_return(http)
58+
allow(http).to receive(:use_ssl=)
59+
allow(http).to receive(:request).and_return(double(body: '{"access_token":"abc"}'))
60+
61+
auth = OCLCAuth.instance
62+
allow(auth).to receive(:oclc_token_url).and_return(url)
63+
allow(Config).to receive(:api_key).and_return('key')
64+
allow(Config).to receive(:api_secret).and_return('secret')
65+
66+
expect(http).not_to receive(:verify_mode=)
67+
auth.send(:fetch_token)
68+
end
69+
70+
it 'disables SSL verification when RE_RECORD_VCR is true' do
71+
# Force the env to true
72+
allow(ENV).to receive(:[]).with('RE_RECORD_VCR').and_return('true')
73+
74+
url = URI('https://example.test/token')
75+
http = instance_double(Net::HTTP)
76+
allow(Net::HTTP).to receive(:new).and_return(http)
77+
allow(http).to receive(:use_ssl=)
78+
allow(http).to receive(:request).and_return(double(body: '{"access_token":"abc"}'))
79+
80+
auth = OCLCAuth.instance
81+
allow(auth).to receive(:oclc_token_url).and_return(url)
82+
allow(Config).to receive(:api_key).and_return('key')
83+
allow(Config).to receive(:api_secret).and_return('secret')
84+
85+
# Expect that verify_mode is set when ENV is 'true'
86+
expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
87+
auth.send(:fetch_token)
88+
end
89+
end
90+
91+
describe '#access_token' do
92+
it 'returns existing token if not expired' do
93+
auth = OCLCAuth.instance
94+
future_token = { access_token: 'valid-token', expires_at: (Time.now + 3600).to_s }
95+
auth.token = future_token
96+
97+
expect(auth.access_token).to eq('valid-token')
98+
end
99+
end
100+
47101
end
48102
end
49103
end

0 commit comments

Comments
 (0)