qa 5.14.0 → 5.16.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 790243f02ba25c268235f6247e96f2824bbc1d971463992c17fabc9f6a8109a0
4
- data.tar.gz: e5fd83a8fc8aac46cb0c991184c6ceb8d39dd64a9fae10fff0f4edd6d30ae4c2
3
+ metadata.gz: 72045748a59fdac5d424b0f6f0703e55282b3971380bc8c8288ccc66677d38d8
4
+ data.tar.gz: b03d37809c9d45a4c20b2b3dce9ce3a650fbb187580b21f6fdc956f549fef710
5
5
  SHA512:
6
- metadata.gz: a4a2fca9475e5c1b94960b2bcead2fa7d7b504b5a08767797148e59a017178677d3691c92d0a2fc785e4bc53021968850e558bff33fe1739ea344c8abc4a32db
7
- data.tar.gz: 6ac6e9ffb528f9dfb7c8a5818793ddb29f83e572a5f61ed3864185407eaab503353ab9c222bc1ea8c0216cd52c00402353652e1e86f535d608ce21505756c27d
6
+ metadata.gz: be2bdd94bae283c15a285c3213be0d780053948da33da5e933cc790cdf3ba2fac4c343380b68b80853d2c0fd83aee7c15ed059372fe314af3094376df855b8d7
7
+ data.tar.gz: 85500cf3c3b1d1a86b002ec6ef748d70b91a0f021caec6ef6ba94861766b67035bfb926fcd59c9e6a6d67abf107c99bf5d0a475b64f122604a006d1386753b29
@@ -0,0 +1,6 @@
1
+ {
2
+ "search": {
3
+ "urls" : { "fastsuggest": "https://fast.oclc.org/searchfast/fastsuggest" },
4
+ "connection": { "timeout":"5"}
5
+ }
6
+ }
@@ -19,6 +19,7 @@ module Qa::Authorities
19
19
  Faraday.get(url) do |req|
20
20
  req.options.params_encoder = space_fix_encoder
21
21
  req.headers['Accept'] = 'application/json'
22
+ req.options.timeout = connection_timeout_in_seconds unless connection_timeout_in_seconds.nil?
22
23
  end
23
24
  end
24
25
 
@@ -49,11 +50,25 @@ module Qa::Authorities
49
50
 
50
51
  # sort=usage+desc is not documented by OCLC but seems necessary to get the sort
51
52
  # we formerly got without specifying, that is most useful in our use case.
52
- "http://fast.oclc.org/searchfast/fastsuggest?&query=#{escaped_query}&queryIndex=#{index}&queryReturn=#{return_data}&suggest=autoSubject&rows=#{num_rows}&sort=usage+desc"
53
+ "#{assign_fast_url}?&query=#{escaped_query}&queryIndex=#{index}&queryReturn=#{return_data}&suggest=autoSubject&rows=#{num_rows}&sort=usage+desc"
53
54
  end
54
55
 
55
56
  private
56
57
 
58
+ # See https://github.com/samvera/questioning_authority/wiki/Connecting-to-OCLC-FAST
59
+ # for more info about config settings for this authority.
60
+ def assign_fast_config
61
+ Qa.config.assign_fast_authority_configs
62
+ end
63
+
64
+ def connection_timeout_in_seconds
65
+ assign_fast_config.dig(:OCLC_ASSIGN_FAST, :search, :connection, :timeout)&.to_i
66
+ end
67
+
68
+ def assign_fast_url
69
+ assign_fast_config.dig(:OCLC_ASSIGN_FAST, :search, :urls, :fastsuggest) || 'https://fast.oclc.org/searchfast/fastsuggest'
70
+ end
71
+
57
72
  # Removes characters from the query string that are not tolerated by the API
58
73
  # See oclc sample code at
59
74
  # http://experimental.worldcat.org/fast/assignfast/js/assignFASTComplete.js
@@ -1,12 +1,15 @@
1
1
  require 'rdf'
2
2
  require 'rdf/ntriples'
3
+ require 'net/http'
4
+ require 'json'
5
+
3
6
  module Qa::Authorities
4
7
  class Discogs::GenericAuthority < Base
5
8
  include WebServiceBase
6
9
  include Discogs::DiscogsTranslation
7
10
  include Discogs::DiscogsUtils
8
11
 
9
- class_attribute :discogs_secret, :discogs_key
12
+ class_attribute :discogs_secret, :discogs_key, :discogs_user_token
10
13
  attr_accessor :primary_artists, :selected_format, :work_uri, :instance_uri
11
14
 
12
15
  # @param [String] subauthority to use
@@ -26,8 +29,8 @@ module Qa::Authorities
26
29
  # physical or digital object and a master represents a set of similar releases. Use of a
27
30
  # subauthority (e.g., /qa/search/discogs/master) will target a specific type. Using the "all"
28
31
  # subauthority will search for both types.
29
- unless discogs_key && discogs_secret
30
- Rails.logger.error "Questioning Authority tried to call Discogs, but no secret and/or key were set."
32
+ unless discogs_user_token.present? || (discogs_key && discogs_secret)
33
+ Rails.logger.error "Questioning Authority tried to call Discogs, but no user token, secret and/or key were set."
31
34
  return []
32
35
  end
33
36
  response = json(build_query_url(q, tc))
@@ -72,7 +75,9 @@ module Qa::Authorities
72
75
  per_page = tc.params["per_page"]
73
76
  end
74
77
  escaped_q = ERB::Util.url_encode(q)
75
- "https://api.discogs.com/database/search?q=#{escaped_q}&type=#{tc.params['subauthority']}&page=#{page}&per_page=#{per_page}&key=#{discogs_key}&secret=#{discogs_secret}"
78
+ url = "https://api.discogs.com/database/search?q=#{escaped_q}&type=#{tc.params['subauthority']}&page=#{page}&per_page=#{per_page}"
79
+ url += "&key=#{discogs_key}&secret=#{discogs_secret}" if discogs_user_token.blank?
80
+ url
76
81
  end
77
82
 
78
83
  # @param [String] the id of the selected item
@@ -82,6 +87,23 @@ module Qa::Authorities
82
87
  "https://api.discogs.com/#{subauthority}s/#{id}"
83
88
  end
84
89
 
90
+ def json(url)
91
+ if discogs_user_token.present?
92
+ uri = URI(url)
93
+ http = Net::HTTP.new(uri.host, uri.port)
94
+ http.use_ssl = true
95
+
96
+ request = Net::HTTP::Get.new(uri)
97
+ request['Authorization'] = "Discogs token=#{discogs_user_token}"
98
+ request['User-Agent'] = 'HykuApp/1.0'
99
+
100
+ response = http.request(request)
101
+ JSON.parse(response.body)
102
+ else
103
+ super
104
+ end
105
+ end
106
+
85
107
  private
86
108
 
87
109
  # In the unusual case that we have an id and the subauthority is "all", we don't know which Discogs url to
@@ -28,8 +28,8 @@ module Qa
28
28
  token == authorized_reload_token
29
29
  end
30
30
 
31
- # Hold linked data authority configs
32
- attr_accessor :linked_data_authority_configs
31
+ # Hold authority configs
32
+ attr_accessor :linked_data_authority_configs, :assign_fast_authority_configs
33
33
 
34
34
  # For linked data access, specify default language for sorting and selection. The default is only used if a language is not
35
35
  # specified in the authority's configuration file and not passed in as a parameter. (e.g. :en, [:en], or [:en, :fr])
@@ -2,25 +2,45 @@
2
2
  module Qa
3
3
  module LinkedData
4
4
  class AuthorityService
5
- # Load or reload the linked data configuration files
6
5
  def self.load_authorities
7
- auth_cfg = {}
8
- # load QA configured linked data authorities
6
+ load_linked_data_config
7
+ load_assign_fast_config
8
+ end
9
+
10
+ # Load or reload the linked data configuration files
11
+ def self.load_linked_data_config
12
+ ld_auth_cfg = {}
13
+ # Linked data settings
9
14
  Dir[File.join(Qa::Engine.root, 'config', 'authorities', 'linked_data', '*.json')].each do |fn|
10
- auth = File.basename(fn, '.json').upcase.to_sym
11
- json = File.read(File.expand_path(fn, __FILE__))
12
- cfg = JSON.parse(json).deep_symbolize_keys
13
- auth_cfg[auth] = cfg
15
+ process_config_file(file_path: fn, config_hash: ld_auth_cfg)
14
16
  end
15
-
16
- # load app configured linked data authorities and overrides
17
+ # Optional local (app) linked data settings overrides
17
18
  Dir[Rails.root.join('config', 'authorities', 'linked_data', '*.json')].each do |fn|
18
- auth = File.basename(fn, '.json').upcase.to_sym
19
- json = File.read(File.expand_path(fn, __FILE__))
20
- cfg = JSON.parse(json).deep_symbolize_keys
21
- auth_cfg[auth] = cfg
19
+ process_config_file(file_path: fn, config_hash: ld_auth_cfg)
22
20
  end
23
- Qa.config.linked_data_authority_configs = auth_cfg
21
+ Qa.config.linked_data_authority_configs = ld_auth_cfg
22
+ end
23
+
24
+ # similar to the above; these settings are for getting (non-linked-data) FAST subject headings from OCLC.
25
+ def self.load_assign_fast_config
26
+ assign_fast_auth_cfg = {}
27
+ # assign_fast settings
28
+ Dir[File.join(Qa::Engine.root, 'config', 'authorities', 'assign_fast', '*.json')].each do |fn|
29
+ process_config_file(file_path: fn, config_hash: assign_fast_auth_cfg)
30
+ end
31
+ # Optional local (app) assign_fast settings overrides
32
+ Dir[Rails.root.join('config', 'authorities', 'assign_fast', '*.json')].each do |fn|
33
+ process_config_file(file_path: fn, config_hash: assign_fast_auth_cfg)
34
+ end
35
+ Qa.config.assign_fast_authority_configs = assign_fast_auth_cfg
36
+ end
37
+
38
+ # load settings into a configuration hash:
39
+ def self.process_config_file(file_path:, config_hash:)
40
+ file_key = File.basename(file_path, '.json').upcase.to_sym
41
+ json = File.read(File.expand_path(file_path, __FILE__))
42
+ cfg = JSON.parse(json).deep_symbolize_keys
43
+ config_hash[file_key] = cfg
24
44
  end
25
45
 
26
46
  # Get the list of names of the loaded authorities
data/lib/qa/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Qa
2
- VERSION = "5.14.0".freeze
2
+ VERSION = "5.16.0".freeze
3
3
  end
@@ -199,7 +199,7 @@ describe Qa::TermsController, type: :controller do
199
199
 
200
200
  context "assign_fast" do
201
201
  before do
202
- stub_request(:get, "http://fast.oclc.org/searchfast/fastsuggest?query=word&queryIndex=suggest50&queryReturn=suggest50,idroot,auth,type&rows=20&suggest=autoSubject&sort=usage+desc")
202
+ stub_request(:get, "https://fast.oclc.org/searchfast/fastsuggest?query=word&queryIndex=suggest50&queryReturn=suggest50,idroot,auth,type&rows=20&suggest=autoSubject&sort=usage+desc")
203
203
  .with(headers: { 'Accept' => 'application/json' })
204
204
  .to_return(body: webmock_fixture("assign-fast-topical-result.json"), status: 200, headers: {})
205
205
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Qa::Authorities::AssignFast do
4
4
  let(:query) { "word (ling" }
5
- let(:expected_url) { "http://fast.oclc.org/searchfast/fastsuggest?&query=word%20ling&queryIndex=suggestall&queryReturn=suggestall%2Cidroot%2Cauth%2Ctype&suggest=autoSubject&rows=20&sort=usage+desc" }
5
+ let(:expected_url) { "https://fast.oclc.org/searchfast/fastsuggest?&query=word%20ling&queryIndex=suggestall&queryReturn=suggestall%2Cidroot%2Cauth%2Ctype&suggest=autoSubject&rows=20&sort=usage+desc" }
6
6
 
7
7
  # subauthority infrastructure
8
8
  describe "#new" do
@@ -60,7 +60,7 @@ describe Qa::Authorities::AssignFast do
60
60
 
61
61
  context "when query is blank" do
62
62
  let(:query) { "" }
63
- let(:expected_url) { "http://fast.oclc.org/searchfast/fastsuggest?&query=&queryIndex=suggestall&queryReturn=suggestall%2Cidroot%2Cauth%2Ctype&suggest=autoSubject&rows=20&sort=usage+desc" }
63
+ let(:expected_url) { "https://fast.oclc.org/searchfast/fastsuggest?&query=&queryIndex=suggestall&queryReturn=suggestall%2Cidroot%2Cauth%2Ctype&suggest=autoSubject&rows=20&sort=usage+desc" }
64
64
 
65
65
  # server returns results but no results header
66
66
  let :results do
@@ -104,7 +104,7 @@ describe Qa::Authorities::AssignFast do
104
104
 
105
105
  context "with topical results" do
106
106
  let(:query) { "word" }
107
- let(:expected_url) { "http://fast.oclc.org/searchfast/fastsuggest?query=word&queryIndex=suggest50&queryReturn=suggest50,idroot,auth,type&rows=20&suggest=autoSubject&sort=usage+desc" }
107
+ let(:expected_url) { "https://fast.oclc.org/searchfast/fastsuggest?query=word&queryIndex=suggest50&queryReturn=suggest50,idroot,auth,type&rows=20&suggest=autoSubject&sort=usage+desc" }
108
108
 
109
109
  let :results do
110
110
  stub_request(:get, expected_url)
@@ -4,6 +4,7 @@ describe Qa::Authorities::Discogs::GenericAuthority do
4
4
  before do
5
5
  described_class.discogs_key = 'dummy_key'
6
6
  described_class.discogs_secret = 'dummy_secret'
7
+ described_class.discogs_user_token = nil
7
8
  end
8
9
 
9
10
  let(:authority) { described_class.new "all" }
@@ -29,6 +30,18 @@ describe Qa::Authorities::Discogs::GenericAuthority do
29
30
 
30
31
  it { is_expected.to eq 'https://api.discogs.com/database/search?q=foo&type=master&page=1&per_page=10&key=dummy_key&secret=dummy_secret' }
31
32
  end
33
+
34
+ context "with a user token" do
35
+ subject { authority.build_query_url("foo", tc) }
36
+ let(:tc) { instance_double(Qa::TermsController) }
37
+ before do
38
+ described_class.discogs_user_token = 'dummy_token'
39
+ allow(Qa::TermsController).to receive(:new).and_return(tc)
40
+ allow(tc).to receive(:params).and_return('page' => "1", 'per_page' => "10", 'subauthority' => "master")
41
+ end
42
+
43
+ it { is_expected.to eq 'https://api.discogs.com/database/search?q=foo&type=master&page=1&per_page=10' }
44
+ end
32
45
  end
33
46
 
34
47
  describe "#find_url" do
@@ -363,6 +376,33 @@ describe Qa::Authorities::Discogs::GenericAuthority do
363
376
  end
364
377
  end
365
378
 
379
+ context "with user token authentication" do
380
+ let(:tc) { instance_double(Qa::TermsController) }
381
+ let :results do
382
+ described_class.discogs_user_token = 'dummy_token'
383
+ described_class.discogs_key = nil
384
+ described_class.discogs_secret = nil
385
+ stub_request(:get, "https://api.discogs.com/database/search?q=melody+gardot&type=all&page=&per_page=")
386
+ .with(
387
+ headers: {
388
+ 'Authorization' => 'Discogs token=dummy_token',
389
+ 'User-Agent' => 'HykuApp/1.0'
390
+ }
391
+ )
392
+ .to_return(status: 200, body: webmock_fixture("discogs-search-response-no-subauth.json"))
393
+ authority.search("melody gardot", tc)
394
+ end
395
+ before do
396
+ allow(Qa::TermsController).to receive(:new).and_return(tc)
397
+ allow(tc).to receive(:params).and_return('subauthority' => "all")
398
+ end
399
+
400
+ it "has id and label keys" do
401
+ expect(results.first["uri"]).to eq("https://www.discogs.com/Melody-Gardot-Who-Will-Comfort-Me-Over-The-Rainbow/release/1750352")
402
+ expect(results.first["id"]).to eq "1750352"
403
+ end
404
+ end
405
+
366
406
  context "when authentication isn't set" do
367
407
  let(:tc) { instance_double(Qa::TermsController) }
368
408
  let :results do
@@ -373,12 +413,13 @@ describe Qa::Authorities::Discogs::GenericAuthority do
373
413
  before do
374
414
  described_class.discogs_secret = nil
375
415
  described_class.discogs_key = nil
416
+ described_class.discogs_user_token = nil
376
417
  allow(Qa::TermsController).to receive(:new).and_return(tc)
377
418
  allow(tc).to receive(:params).and_return('subauthority' => "master")
378
419
  end
379
420
 
380
421
  it "logs an error" do
381
- expect(Rails.logger).to receive(:error).with('Questioning Authority tried to call Discogs, but no secret and/or key were set.')
422
+ expect(Rails.logger).to receive(:error).with('Questioning Authority tried to call Discogs, but no user token, secret and/or key were set.')
382
423
  expect(results).to be_empty
383
424
  end
384
425
  end
data/spec/spec_helper.rb CHANGED
@@ -22,7 +22,11 @@ require 'pry'
22
22
  Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each { |f| require f }
23
23
 
24
24
  RSpec.configure do |config|
25
- config.fixture_path = File.expand_path("../fixtures", __FILE__)
25
+ if config.respond_to?(:fixture_paths=)
26
+ config.fixture_paths = [File.expand_path("../fixtures", __FILE__)]
27
+ else
28
+ config.fixture_path = File.expand_path("../fixtures", __FILE__)
29
+ end
26
30
 
27
31
  config.use_transactional_fixtures = true
28
32
 
@@ -21,4 +21,9 @@ group :development do
21
21
  # See https://stackoverflow.com/questions/70500220/rails-7-ruby-3-1-loaderror-cannot-load-such-file-net-smtp
22
22
  gem 'net-smtp', require: false
23
23
  end
24
+
25
+ if Gem::Version.new(ENV['RAILS_VERSION']) < Gem::Version.new('7.1')
26
+ gem 'concurrent-ruby', '1.3.4'
27
+ end
28
+
24
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qa
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.14.0
4
+ version: 5.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Anderson
@@ -16,7 +16,7 @@ authors:
16
16
  autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
- date: 2024-12-12 00:00:00.000000000 Z
19
+ date: 2025-12-10 00:00:00.000000000 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: activerecord-import
@@ -114,20 +114,20 @@ dependencies:
114
114
  requirements:
115
115
  - - ">="
116
116
  - !ruby/object:Gem::Version
117
- version: '5.0'
117
+ version: '6.0'
118
118
  - - "<"
119
119
  - !ruby/object:Gem::Version
120
- version: '8.1'
120
+ version: '8.2'
121
121
  type: :runtime
122
122
  prerelease: false
123
123
  version_requirements: !ruby/object:Gem::Requirement
124
124
  requirements:
125
125
  - - ">="
126
126
  - !ruby/object:Gem::Version
127
- version: '5.0'
127
+ version: '6.0'
128
128
  - - "<"
129
129
  - !ruby/object:Gem::Version
130
- version: '8.1'
130
+ version: '8.2'
131
131
  - !ruby/object:Gem::Dependency
132
132
  name: rdf
133
133
  requirement: !ruby/object:Gem::Requirement
@@ -413,6 +413,7 @@ files:
413
413
  - app/services/qa/pagination_service.rb
414
414
  - app/views/layouts/qa/application.html.erb
415
415
  - config/authorities.yml
416
+ - config/authorities/assign_fast/oclc_assign_fast.json
416
417
  - config/authorities/linked_data/loc.json
417
418
  - config/authorities/linked_data/oclc_fast.json
418
419
  - config/authorities/states.yml