qa 5.13.0 → 5.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|