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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c2bc5171a4ed527a81eb174b2e5e7f0e99d86c2df285c2619d415ec4587af84
4
- data.tar.gz: 8fd957474f3f185867025fb18e08d4262e207ae051bca57bc43eaac10bc7b097
3
+ metadata.gz: 790243f02ba25c268235f6247e96f2824bbc1d971463992c17fabc9f6a8109a0
4
+ data.tar.gz: e5fd83a8fc8aac46cb0c991184c6ceb8d39dd64a9fae10fff0f4edd6d30ae4c2
5
5
  SHA512:
6
- metadata.gz: 54cc9b6d0289157de648f34c040355eb06f206ca0d616664d96e6ae3d3e5e754e950c447127d61cf16f6d3f15f874fd65d23845d1f912ce89c3e1571875a9d8e
7
- data.tar.gz: 44e459c8a7424a8116f075979fa7ce2023b5307edb48c2a044cfdea5e5239435b8a85375687a9f2a51b7c63af60e2435bf27769043c9fa8be1de9d0f98c8fe9e
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, request_header: request_header_service.search_header)
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, request_header: request_header_service.fetch_header)
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, request_header: request_header_service.fetch_header)
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::Authorities::LinkedData::GenericAuthority.new(vocab_param)
162
- rescue Qa::InvalidLinkedDataAuthority => e
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
- begin
73
- mod = authority_class.camelize.constantize
74
- rescue NameError
75
- msg = "Unable to initialize authority #{authority_class}"
76
- logger.warn msg
77
- render json: { errors: msg }, status: :bad_request
78
- return
79
- end
80
- begin
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
@@ -29,5 +29,8 @@ module Qa::Authorities
29
29
  def find(_id)
30
30
  raise NotImplementedError, "#{self.class}#find is unimplemented."
31
31
  end
32
+
33
+ class_attribute :linked_data, instance_writer: false
34
+ self.linked_data = false
32
35
  end
33
36
  end
@@ -13,6 +13,8 @@ module Qa::Authorities
13
13
  attr_reader :authority_config
14
14
  private :authority_config
15
15
 
16
+ self.linked_data = true
17
+
16
18
  delegate :supports_term?, :term_subauthorities?, :term_subauthority?,
17
19
  :term_id_expects_uri?, :term_id_expects_id?, to: :term_config
18
20
 
@@ -35,7 +35,8 @@ module Qa::Authorities
35
35
  end
36
36
 
37
37
  def find_url(id)
38
- "https://id.loc.gov/authorities/#{@subauthority}/#{id}.json"
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
@@ -1,3 +1,3 @@
1
1
  module Qa
2
- VERSION = "5.13.0".freeze
2
+ VERSION = "5.14.0".freeze
3
3
  end
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.13.0
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-08-20 00:00:00.000000000 Z
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.0'
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.0'
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
OSZAR »