qa 5.13.0 → 5.14.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 +4 -4
- data/app/controllers/qa/linked_data_terms_controller.rb +10 -5
- data/app/controllers/qa/terms_controller.rb +9 -20
- data/lib/qa/authorities/base.rb +3 -0
- data/lib/qa/authorities/linked_data/generic_authority.rb +2 -0
- data/lib/qa/authorities/loc/generic_authority.rb +2 -1
- data/lib/qa/authorities/loc_subauthority.rb +18 -0
- data/lib/qa/authority_request_context.rb +49 -0
- data/lib/qa/authority_wrapper.rb +63 -0
- data/lib/qa/version.rb +1 -1
- data/lib/qa.rb +50 -0
- data/spec/lib/authorities/loc_spec.rb +12 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 790243f02ba25c268235f6247e96f2824bbc1d971463992c17fabc9f6a8109a0
|
4
|
+
data.tar.gz: e5fd83a8fc8aac46cb0c991184c6ceb8d39dd64a9fae10fff0f4edd6d30ae4c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4a2fca9475e5c1b94960b2bcead2fa7d7b504b5a08767797148e59a017178677d3691c92d0a2fc785e4bc53021968850e558bff33fe1739ea344c8abc4a32db
|
7
|
+
data.tar.gz: 6ac6e9ffb528f9dfb7c8a5818793ddb29f83e572a5f61ed3864185407eaab503353ab9c222bc1ea8c0216cd52c00402353652e1e86f535d608ce21505756c27d
|
@@ -42,7 +42,7 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
42
42
|
# get "/search/linked_data/:vocab(/:subauthority)"
|
43
43
|
# @see Qa::Authorities::LinkedData::SearchQuery#search
|
44
44
|
def search # rubocop:disable Metrics/MethodLength
|
45
|
-
terms = @authority.search(query
|
45
|
+
terms = @authority.search(query)
|
46
46
|
cors_allow_origin_header(response)
|
47
47
|
render json: terms
|
48
48
|
rescue Qa::ServiceUnavailable
|
@@ -65,7 +65,7 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
65
65
|
# get "/show/linked_data/:vocab/:subauthority/:id
|
66
66
|
# @see Qa::Authorities::LinkedData::FindTerm#find
|
67
67
|
def show # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
68
|
-
term = @authority.find(id
|
68
|
+
term = @authority.find(id)
|
69
69
|
cors_allow_origin_header(response)
|
70
70
|
render json: term, content_type: request_header_service.content_type_for_format
|
71
71
|
rescue Qa::TermNotFound
|
@@ -95,7 +95,7 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
95
95
|
# get "/fetch/linked_data/:vocab"
|
96
96
|
# @see Qa::Authorities::LinkedData::FindTerm#find
|
97
97
|
def fetch # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
98
|
-
term = @authority.find(uri
|
98
|
+
term = @authority.find(uri)
|
99
99
|
cors_allow_origin_header(response)
|
100
100
|
render json: term, content_type: request_header_service.content_type_for_format
|
101
101
|
rescue Qa::TermNotFound
|
@@ -157,9 +157,14 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
157
157
|
@request_header_service = request_header_service_class.new(request: request, params: params)
|
158
158
|
end
|
159
159
|
|
160
|
+
# @see Qa::AuthorityWrapper for these methods
|
161
|
+
delegate :search_header, :fetch_header, to: :request_header_service
|
162
|
+
|
160
163
|
def init_authority
|
161
|
-
@authority = Qa
|
162
|
-
|
164
|
+
@authority = Qa.authority_for(vocab: params[:vocab],
|
165
|
+
subauthority: params[:subauthority],
|
166
|
+
context: self)
|
167
|
+
rescue Qa::InvalidAuthorityError, Qa::InvalidLinkedDataAuthority => e
|
163
168
|
msg = e.message
|
164
169
|
logger.warn msg
|
165
170
|
render json: { errors: msg }, status: :bad_request
|
@@ -69,26 +69,15 @@ class Qa::TermsController < ::ApplicationController
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def init_authority # rubocop:disable Metrics/MethodLength
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
@authority = if mod.is_a? Class
|
82
|
-
mod.new
|
83
|
-
else
|
84
|
-
raise Qa::MissingSubAuthority, "No sub-authority provided" if params[:subauthority].blank?
|
85
|
-
mod.subauthority_for(params[:subauthority])
|
86
|
-
end
|
87
|
-
rescue Qa::InvalidSubAuthority, Qa::MissingSubAuthority => e
|
88
|
-
msg = e.message
|
89
|
-
logger.warn msg
|
90
|
-
render json: { errors: msg }, status: :bad_request
|
91
|
-
end
|
72
|
+
@authority = Qa.authority_for(vocab: params[:vocab],
|
73
|
+
subauthority: params[:subauthority],
|
74
|
+
# Included to preserve error message text
|
75
|
+
try_linked_data_config: false,
|
76
|
+
context: self)
|
77
|
+
rescue Qa::InvalidAuthorityError, Qa::InvalidSubAuthority, Qa::MissingSubAuthority => e
|
78
|
+
msg = e.message
|
79
|
+
logger.warn msg
|
80
|
+
render json: { errors: msg }, status: :bad_request
|
92
81
|
end
|
93
82
|
|
94
83
|
def check_query_param
|
data/lib/qa/authorities/base.rb
CHANGED
@@ -35,7 +35,8 @@ module Qa::Authorities
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def find_url(id)
|
38
|
-
|
38
|
+
root_fetch_slug = Loc.root_fetch_slug_for(@subauthority)
|
39
|
+
File.join("https://id.loc.gov/", root_fetch_slug, "/#{@subauthority}/#{id}.json")
|
39
40
|
end
|
40
41
|
|
41
42
|
private
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module Qa::Authorities::LocSubauthority
|
2
|
+
# @todo Rename to reflect that this is a URI encoded url fragement used only for searching.
|
2
3
|
def get_url_for_authority(authority)
|
3
4
|
if authorities.include?(authority) then authority_base_url
|
4
5
|
elsif vocabularies.include?(authority) then vocab_base_url
|
@@ -7,6 +8,23 @@ module Qa::Authorities::LocSubauthority
|
|
7
8
|
end
|
8
9
|
end
|
9
10
|
|
11
|
+
# @note The returned value is the root directory of the URL. The graphicMaterials sub-authority
|
12
|
+
# has a "type" of vocabulary. https://id.loc.gov/vocabulary/graphicMaterials/tgm008083.html
|
13
|
+
# In some cases, this is plural and in others this is singular.
|
14
|
+
#
|
15
|
+
# @param authority [String] the LOC authority that matches one of the types
|
16
|
+
# @return [String]
|
17
|
+
#
|
18
|
+
# @note there is a relationship between the returned value and the encoded URLs returned by
|
19
|
+
# {#get_url_for_authority}.
|
20
|
+
def root_fetch_slug_for(authority)
|
21
|
+
validate_subauthority!(authority)
|
22
|
+
return "authorities" if authorities.include?(authority)
|
23
|
+
return "vocabulary" if vocabularies.include?(authority)
|
24
|
+
return "datatype" if datatypes.include?(authority)
|
25
|
+
return "vocabulary/preservation" if preservation.include?(authority)
|
26
|
+
end
|
27
|
+
|
10
28
|
def authorities
|
11
29
|
[
|
12
30
|
"subjects",
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Qa
|
2
|
+
# @note THIS IS NOT TESTED NOR EXERCISED CODE IT IS PROVIDED AS CONJECTURE. FUTURE CHANGES MIGHT
|
3
|
+
# BUILD AND REFACTOR UPON THIS.
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
# @abstract
|
7
|
+
#
|
8
|
+
# This class is responsible for exposing methods that are required by both linked data and
|
9
|
+
# non-linked data authorities. As of v5.10.0, those three methods are: params, search_header,
|
10
|
+
# fetch_header. Those are the methods that are used in {Qa::LinkedData::RequestHeaderService} and
|
11
|
+
# in {Qa::Authorities::Discogs::GenericAuthority}.
|
12
|
+
#
|
13
|
+
# The intention is to provide a class that can behave like a controller object without being that
|
14
|
+
# entire controller object.
|
15
|
+
#
|
16
|
+
# @see Qa::LinkedData::RequestHeaderService
|
17
|
+
# @see Qa::Authorities::Discogs::GenericAuthority
|
18
|
+
class AuthorityRequestContext
|
19
|
+
def self.fallback
|
20
|
+
new
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(params: {}, headers: {}, **kwargs)
|
24
|
+
@params = params
|
25
|
+
@headers = headers
|
26
|
+
(SEARCH_HEADER_KEYS + FETCH_HEADER_KEYS).uniq.each do |key|
|
27
|
+
send("#{key}=", kwargs[key]) if kwargs.key?(key)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
SEARCH_HEADER_KEYS = %i[request request_id subauthority user_language performance_data context response_header replacements].freeze
|
32
|
+
FETCH_HEADER_KEYS = %i[request request_id subauthority user_language performance_data format response_header replacements].freeze
|
33
|
+
|
34
|
+
attr_accessor :params, :headers
|
35
|
+
attr_accessor(*(SEARCH_HEADER_KEYS + FETCH_HEADER_KEYS).uniq)
|
36
|
+
|
37
|
+
def search_header
|
38
|
+
SEARCH_HEADER_KEYS.each_with_object(headers.deep_dup) do |key, header|
|
39
|
+
header[key] = send(key) if send(key).present?
|
40
|
+
end.compact
|
41
|
+
end
|
42
|
+
|
43
|
+
def fetch_header
|
44
|
+
FETCH_HEADER_KEYS.each_with_object(headers.deep_dup) do |key, header|
|
45
|
+
header[key] = send(key) if send(key).present?
|
46
|
+
end.compact
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Qa
|
2
|
+
# @api public
|
3
|
+
# @since v5.11.0
|
4
|
+
#
|
5
|
+
# The intention of this wrapper is to provide a common interface that both linked and non-linked
|
6
|
+
# data can use. There are implementation differences between the two, but with this wrapper, the
|
7
|
+
# goal is to draw attention to those differences and insulate the end user from those issues.
|
8
|
+
#
|
9
|
+
# One benefit in introducing this class is that when interacting with a questioning authority
|
10
|
+
# implementation you don't need to consider "Hey when I instantiate an authority, is this linked
|
11
|
+
# data or not?" And what specifically are the parameter differences. You will need to perhaps
|
12
|
+
# include some additional values in the context if you don't call this from a controller.
|
13
|
+
class AuthorityWrapper
|
14
|
+
require 'qa/authority_request_context.rb'
|
15
|
+
# @param authority [#find, #search]
|
16
|
+
# @param subauthority [#to_s]
|
17
|
+
# @param context [#params, #search_header, #fetch_header]
|
18
|
+
def initialize(authority:, subauthority:, context:)
|
19
|
+
@authority = authority
|
20
|
+
@subauthority = subauthority
|
21
|
+
@context = context
|
22
|
+
configure!
|
23
|
+
end
|
24
|
+
attr_reader :authority, :context, :subauthority
|
25
|
+
|
26
|
+
def search(value)
|
27
|
+
if linked_data?
|
28
|
+
# should respond to search_header
|
29
|
+
authority.search(value, request_header: context.search_header)
|
30
|
+
elsif authority.method(:search).arity == 2
|
31
|
+
# This context should respond to params; see lib/qa/authorities/discogs/generic_authority.rb
|
32
|
+
authority.search(value, context)
|
33
|
+
else
|
34
|
+
authority.search(value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# context has params
|
39
|
+
def find(value)
|
40
|
+
if linked_data?
|
41
|
+
# should respond to fetch_header
|
42
|
+
authority.find(value, request_header: context.fetch_header)
|
43
|
+
elsif authority.method(:find).arity == 2
|
44
|
+
authority.find(value, context)
|
45
|
+
else
|
46
|
+
authority.find(value)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
alias fetch find
|
50
|
+
|
51
|
+
def method_missing(method_name, *arguments, &block)
|
52
|
+
authority.send(method_name, *arguments, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
def respond_to_missing?(method_name, include_private = false)
|
56
|
+
authority.respond_to?(method_name, include_private)
|
57
|
+
end
|
58
|
+
|
59
|
+
def configure!
|
60
|
+
@context.subauthority = @subauthority if @context.respond_to?(:subauthority)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/qa/version.rb
CHANGED
data/lib/qa.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "qa/engine"
|
2
2
|
require "active_record"
|
3
3
|
require "activerecord-import"
|
4
|
+
require "qa/authority_wrapper"
|
4
5
|
|
5
6
|
module Qa
|
6
7
|
extend ActiveSupport::Autoload
|
@@ -30,6 +31,13 @@ module Qa
|
|
30
31
|
warn "[DEPRECATED] #{in_msg}#{msg} It will be removed in the next major release."
|
31
32
|
end
|
32
33
|
|
34
|
+
# Raised when the authority is not valid
|
35
|
+
class InvalidAuthorityError < RuntimeError
|
36
|
+
def initialize(authority_class)
|
37
|
+
super "Unable to initialize authority #{authority_class}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
33
41
|
# Raised when the configuration directory for local authorities doesn't exist
|
34
42
|
class ConfigDirectoryNotFound < StandardError; end
|
35
43
|
|
@@ -67,4 +75,46 @@ module Qa
|
|
67
75
|
|
68
76
|
# Raised when data is returned but cannot be normalized
|
69
77
|
class DataNormalizationError < StandardError; end
|
78
|
+
|
79
|
+
# @api public
|
80
|
+
# @since 5.11.0
|
81
|
+
#
|
82
|
+
# @param vocab [String]
|
83
|
+
# @param subauthority [String]
|
84
|
+
# @param context [#params, #search_header, #fetch_header]
|
85
|
+
# @param try_linked_data_config [Boolean] when true attempt to check for a linked data authority;
|
86
|
+
# this is included as an option to help preserve error messaging from the 5.10.0 branch.
|
87
|
+
# Unless you want to mirror the error messages of `Qa::TermsController#init_authority` then
|
88
|
+
# use the default value.
|
89
|
+
#
|
90
|
+
# @note :try_linked_data_config is included to preserve error message text; something which is
|
91
|
+
# extensively tested in this gem.
|
92
|
+
#
|
93
|
+
# @return [#search, #find] an authority that will respond to #search and #find; and in some cases
|
94
|
+
# #fetch. This is provided as a means of normalizing how we initialize an authority.
|
95
|
+
# And to provide a means to request an authority both within a controller request cycle as
|
96
|
+
# well as outside of that cycle.
|
97
|
+
def self.authority_for(vocab:, context:, subauthority: nil, try_linked_data_config: true)
|
98
|
+
authority = build_authority_for(vocab: vocab,
|
99
|
+
subauthority: subauthority,
|
100
|
+
try_linked_data_config: try_linked_data_config)
|
101
|
+
AuthorityWrapper.new(authority: authority, subauthority: subauthority, context: context)
|
102
|
+
end
|
103
|
+
|
104
|
+
# @api private
|
105
|
+
def self.build_authority_for(vocab:, subauthority: nil, try_linked_data_config: true)
|
106
|
+
authority_constant_name = "Qa::Authorities::#{vocab.to_s.camelcase}"
|
107
|
+
authority_constant = authority_constant_name.safe_constantize
|
108
|
+
if authority_constant.nil?
|
109
|
+
return Qa::Authorities::LinkedData::GenericAuthority.new(vocab.upcase.to_sym) if try_linked_data_config
|
110
|
+
|
111
|
+
raise InvalidAuthorityError, authority_constant_name
|
112
|
+
end
|
113
|
+
|
114
|
+
return authority_constant.new if authority_constant.is_a?(Class)
|
115
|
+
return authority_constant.subauthority_for(subauthority) if subauthority.present?
|
116
|
+
|
117
|
+
raise Qa::MissingSubAuthority, "No sub-authority provided"
|
118
|
+
end
|
119
|
+
private_class_method :build_authority_for
|
70
120
|
end
|
@@ -42,6 +42,18 @@ describe Qa::Authorities::Loc do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
describe ".root_fetch_slug_for" do
|
46
|
+
it "raises an error for an invalid subauthority" do
|
47
|
+
expect do
|
48
|
+
described_class.root_fetch_slug_for("no-one-would-ever-have-this-one")
|
49
|
+
end.to raise_error Qa::InvalidSubAuthority
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns the corresponding type for the given subauthority" do
|
53
|
+
expect(described_class.root_fetch_slug_for("graphicMaterials")).to eq("vocabulary")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
45
57
|
describe "#response" do
|
46
58
|
subject { authority.response(url) }
|
47
59
|
let :authority do
|
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.
|
4
|
+
version: 5.14.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-
|
19
|
+
date: 2024-12-12 00:00:00.000000000 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
name: activerecord-import
|
@@ -117,7 +117,7 @@ dependencies:
|
|
117
117
|
version: '5.0'
|
118
118
|
- - "<"
|
119
119
|
- !ruby/object:Gem::Version
|
120
|
-
version: '8.
|
120
|
+
version: '8.1'
|
121
121
|
type: :runtime
|
122
122
|
prerelease: false
|
123
123
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -127,7 +127,7 @@ dependencies:
|
|
127
127
|
version: '5.0'
|
128
128
|
- - "<"
|
129
129
|
- !ruby/object:Gem::Version
|
130
|
-
version: '8.
|
130
|
+
version: '8.1'
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
132
|
name: rdf
|
133
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -501,6 +501,8 @@ files:
|
|
501
501
|
- lib/qa/authorities/oclcts/generic_oclc_authority.rb
|
502
502
|
- lib/qa/authorities/tgnlang.rb
|
503
503
|
- lib/qa/authorities/web_service_base.rb
|
504
|
+
- lib/qa/authority_request_context.rb
|
505
|
+
- lib/qa/authority_wrapper.rb
|
504
506
|
- lib/qa/configuration.rb
|
505
507
|
- lib/qa/data/TGN_LANGUAGES.xml
|
506
508
|
- lib/qa/engine.rb
|