Skip to content

Commit 56e683e

Browse files
authored
Merge pull request #279 from samvera/refactor_opt_attrs
refactor optional attributes for linked data search/fetch to pass as hash
2 parents 559393b + 1d405a2 commit 56e683e

File tree

9 files changed

+396
-140
lines changed

9 files changed

+396
-140
lines changed

app/controllers/qa/linked_data_terms_controller.rb

+16-51
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ class Qa::LinkedDataTermsController < ::ApplicationController
77
before_action :check_show_subauthority, :check_id_param, only: :show
88
before_action :check_uri_param, only: :fetch
99
before_action :validate_auth_reload_token, only: :reload
10+
before_action :create_request_header_service, only: [:search, :show, :fetch]
1011

1112
delegate :cors_allow_origin_header, to: Qa::ApplicationController
1213

14+
class_attribute :request_header_service_class
15+
self.request_header_service_class = Qa::LinkedData::RequestHeaderService
16+
17+
attr_reader :request_header_service
18+
1319
# Provide a warning if there is a request for all terms.
1420
def index
1521
logger.warn 'Linked data authorities do not support retrieving all terms.'
@@ -35,8 +41,8 @@ def reload
3541
# Return a list of terms based on a query
3642
# get "/search/linked_data/:vocab(/:subauthority)"
3743
# @see Qa::Authorities::LinkedData::SearchQuery#search
38-
def search # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
39-
terms = @authority.search(query, subauth: subauthority, language: language, replacements: replacement_params, context: context?, performance_data: performance_data?)
44+
def search # rubocop:disable Metrics/MethodLength
45+
terms = @authority.search(query, request_header: request_header_service.search_header)
4046
cors_allow_origin_header(response)
4147
render json: terms
4248
rescue Qa::ServiceUnavailable
@@ -59,9 +65,9 @@ def search # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
5965
# get "/show/linked_data/:vocab/:subauthority/:id
6066
# @see Qa::Authorities::LinkedData::FindTerm#find
6167
def show # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
62-
term = @authority.find(id, subauth: subauthority, language: language, replacements: replacement_params, format: format, performance_data: performance_data?)
68+
term = @authority.find(id, request_header: request_header_service.fetch_header)
6369
cors_allow_origin_header(response)
64-
render json: term, content_type: content_type_for_format
70+
render json: term, content_type: request_header_service.content_type_for_format
6571
rescue Qa::TermNotFound
6672
msg = "Term Not Found - Fetch term #{id} unsuccessful for#{subauth_warn_msg} authority #{vocab_param}"
6773
logger.warn msg
@@ -89,9 +95,9 @@ def show # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
8995
# get "/fetch/linked_data/:vocab"
9096
# @see Qa::Authorities::LinkedData::FindTerm#find
9197
def fetch # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
92-
term = @authority.find(uri, subauth: subauthority, language: language, replacements: replacement_params, format: format, performance_data: performance_data?)
98+
term = @authority.find(uri, request_header: request_header_service.fetch_header)
9399
cors_allow_origin_header(response)
94-
render json: term, content_type: content_type_for_format
100+
render json: term, content_type: request_header_service.content_type_for_format
95101
rescue Qa::TermNotFound
96102
msg = "Term Not Found - Fetch term #{uri} unsuccessful for#{subauth_warn_msg} authority #{vocab_param}"
97103
logger.warn msg
@@ -147,6 +153,10 @@ def check_show_subauthority
147153
end
148154
end
149155

156+
def create_request_header_service
157+
@request_header_service = request_header_service_class.new(request, params)
158+
end
159+
150160
def init_authority
151161
@authority = Qa::Authorities::LinkedData::GenericAuthority.new(vocab_param)
152162
rescue Qa::InvalidLinkedDataAuthority => e
@@ -190,64 +200,19 @@ def id
190200
params[:id]
191201
end
192202

193-
def language
194-
request_language = request.env['HTTP_ACCEPT_LANGUAGE']
195-
request_language = request_language.scan(/^[a-z]{2}/).first if request_language.present?
196-
params[:lang] || request_language
197-
end
198-
199203
def subauthority
200204
params[:subauthority]
201205
end
202206

203-
def replacement_params
204-
params.reject { |k, _v| ['q', 'vocab', 'controller', 'action', 'subauthority', 'lang', 'id'].include?(k) }
205-
end
206-
207207
def subauth_warn_msg
208208
subauthority.blank? ? "" : " sub-authority #{subauthority} in"
209209
end
210210

211-
def format
212-
return 'json' unless params.key?(:format)
213-
return 'json' if params[:format].blank?
214-
params[:format]
215-
end
216-
217-
def jsonld?
218-
format.casecmp?('jsonld')
219-
end
220-
221-
def n3?
222-
format.casecmp?('n3')
223-
end
224-
225-
def ntriples?
226-
format.casecmp?('ntriples')
227-
end
228-
229-
def content_type_for_format
230-
return 'application/ld+json' if jsonld?
231-
return 'text/n3' if n3?
232-
return 'application/n-triples' if ntriples?
233-
'application/json'
234-
end
235-
236-
def context?
237-
context = params.fetch(:context, 'false')
238-
context.casecmp?('true')
239-
end
240-
241211
def details?
242212
details = params.fetch(:details, 'false')
243213
details.casecmp?('true')
244214
end
245215

246-
def performance_data?
247-
performance_data = params.fetch(:performance_data, 'false')
248-
performance_data.casecmp?('true')
249-
end
250-
251216
def validate_auth_reload_token
252217
token = params.key?(:auth_token) ? params[:auth_token] : nil
253218
valid = Qa.config.valid_authority_reload_token?(token)

app/services/qa/linked_data/authority_url_service.rb

+35-21
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@ class << self
77
# @param action_config [Qa::Authorities::LinkedData::SearchConfig | Qa::Authorities::LinkedData::TermConfig] action configuration for the authority
88
# @param action [Symbol] action with valid values :search or :term
99
# @param action_request [String] the request the user is making of the authority (e.g. query text or term id/uri)
10-
# @param substitutions [Hash] variable-value pairs to substitute into the URL template (optional)
11-
# @param subauthority [String] name of a subauthority (optional)
12-
# @param language [Array<Symbol>] languages for filtering returned literals (optional)
10+
# @param request_header [Hash] optional attributes that can be appended to the generated URL
11+
# @option replacements [Hash] variable-value pairs to substitute into the URL template
12+
# @option subauthority [String] name of a subauthority
13+
# @option language [Array<Symbol>] languages for filtering returned literals
1314
# @return a valid URL that submits the action request to the external authority
14-
def build_url(action_config:, action:, action_request:, substitutions: {}, subauthority: nil, language: nil) # rubocop:disable Metrics/ParameterLists
15+
# @note All parameters after request_header are deprecated and will be removed in the next major release.
16+
def build_url(action_config:, action:, action_request:, request_header: {}, substitutions: {}, subauthority: nil, language: nil) # rubocop:disable Metrics/ParameterLists
17+
request_header = build_request_header(substitutions, subauthority, language) if request_header.empty?
1518
action_validation(action)
1619
url_config = action_config.url_config
17-
selected_substitutions = url_config.extract_substitutions(combined_substitutions(action_config, action, action_request, substitutions, subauthority, language))
20+
selected_substitutions = url_config.extract_substitutions(combined_substitutions(action_config, action, action_request, request_header))
1821
Qa::IriTemplateService.build_url(url_config: url_config, substitutions: selected_substitutions)
1922
end
2023

@@ -25,10 +28,12 @@ def action_validation(action)
2528
raise Qa::UnsupportedAction, "#{action} Not Supported - Action must be one of the supported actions (e.g. :term, :search)"
2629
end
2730

28-
def combined_substitutions(action_config, action, action_request, substitutions, subauthority, language) # rubocop:disable Metrics/ParameterLists
31+
def combined_substitutions(action_config, action, action_request, request_header)
32+
substitutions = request_header.fetch(:replacements, {})
2933
substitutions[action_request_variable(action_config, action)] = action_request
30-
substitutions[action_subauth_variable(action_config)] = action_subauth_variable_value(action_config, subauthority) if supports_subauthorities?(action_config) && subauthority.present?
31-
substitutions[action_language_variable(action_config)] = language_value(language) if supports_language_parameter?(action_config) && language.present?
34+
substitutions[action_subauth_variable(action_config)] = action_subauth_variable_value(action_config, request_header)
35+
substitutions[action_language_variable(action_config)] = language_value(action_config, request_header)
36+
substitutions.reject { |_k, v| v.nil? }
3237
substitutions
3338
end
3439

@@ -37,29 +42,38 @@ def action_request_variable(action_config, action)
3742
action_config.qa_replacement_patterns[key]
3843
end
3944

40-
def supports_subauthorities?(action_config)
41-
action_config.supports_subauthorities?
42-
end
43-
4445
def action_subauth_variable(action_config)
4546
action_config.qa_replacement_patterns[:subauth]
4647
end
4748

48-
def action_subauth_variable_value(action_config, subauthority)
49-
action_config.subauthorities[subauthority.to_sym]
50-
end
51-
52-
def supports_language_parameter?(action_config)
53-
action_config.supports_language_parameter?
49+
def action_subauth_variable_value(action_config, request_header)
50+
subauth = request_header.fetch(:subauthority, nil)
51+
return nil unless subauth && action_config.supports_subauthorities?
52+
action_config.subauthorities[subauth.to_sym]
5453
end
5554

5655
def action_language_variable(action_config)
5756
action_config.qa_replacement_patterns[:lang]
5857
end
5958

60-
def language_value(language)
61-
return nil if language.blank?
62-
language.first
59+
def language_value(action_config, request_header)
60+
return nil unless action_config.supports_language_parameter?
61+
request_header.fetch(:language, []).first
62+
end
63+
64+
# This is providing support for calling build_url with individual parameters instead of the request_header.
65+
# This is deprecated and will be removed in the next major release.
66+
def build_request_header(substitutions, subauthority, language) # rubocop:disable Metrics/CyclomaticComplexity
67+
return {} if substitutions.blank? && subauthority.blank? && language.blank?
68+
Qa.deprecation_warning(
69+
in_msg: 'Qa::LinkedData::AuthorityUrlService',
70+
msg: "individual attributes for options (e.g. substitutions, subauthority, language) are deprecated; use request_header instead"
71+
)
72+
request_header = {}
73+
request_header[:replacements] = substitutions unless substititions.blank?
74+
request_header[:subauthority] = subauthority unless subauthority.blank?
75+
request_header[:language] = language unless language.blank?
76+
request_header
6377
end
6478
end
6579
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Service to construct a request header that includes optional attributes for search and fetch requests.
2+
module Qa
3+
module LinkedData
4+
class RequestHeaderService
5+
attr_reader :request, :params
6+
7+
# @param request [HttpRequest] request from controller
8+
# @param params [Hash] attribute-value pairs holding the request parameters
9+
# @option subauthority [String] the subauthority to query
10+
# @option lang [Symbol] language used to select literals when multi-language is supported (e.g. :en, :fr, etc.)
11+
# @option performance_data [Boolean] true if include_performance_data should be returned with the results; otherwise, false (default: false)
12+
# @option context [Boolean] true if context should be returned with the results; otherwise, false (default: false) (search only)
13+
# @option format [String] return data in this format (fetch only)
14+
# @note params may have additional attribute-value pairs that are passed through via replacements (only configured replacements are used)
15+
def initialize(request, params)
16+
@request = request
17+
@params = params
18+
end
19+
20+
# Construct request parameters to pass to search_query (linked data module).
21+
# @returns [Hash] parsed out attribute-value pairs that are required for the search query
22+
# @see Qa::Authorities::LinkedData::SearchQuery
23+
def search_header
24+
header = {}
25+
header[:subauthority] = params.fetch(:subauthority, nil)
26+
header[:user_language] = user_language
27+
header[:performance_data] = performance_data?
28+
header[:context] = context?
29+
header[:replacements] = replacements
30+
header
31+
end
32+
33+
# Construct request parameters to pass to fetching a term (linked data module).
34+
# @returns [Hash] parsed out attribute-value pairs that are required for the term fetch.
35+
# @see Qa::Authorities::LinkedData::FindTerm
36+
def fetch_header
37+
header = {}
38+
header[:subauthority] = params.fetch(:subauthority, nil)
39+
header[:user_language] = user_language
40+
header[:performance_data] = performance_data?
41+
header[:format] = format
42+
header[:replacements] = replacements
43+
header
44+
end
45+
46+
# @returns [String] the response header content type based on requested format
47+
def content_type_for_format
48+
case format
49+
when 'jsonld'
50+
'application/ld+json'
51+
when 'n3'
52+
'text/n3'
53+
when 'ntriples'
54+
'application/n-triples'
55+
else
56+
'application/json'
57+
end
58+
end
59+
60+
private
61+
62+
# filter literals in results to this language
63+
def user_language
64+
request_language = request.env['HTTP_ACCEPT_LANGUAGE']
65+
request_language = request_language.scan(/^[a-z]{2}/).first if request_language.present?
66+
lang = params[:lang] || request_language
67+
lang.present? ? Array(lang) : nil
68+
end
69+
70+
# include extended context in the results if true (applies to search only)
71+
def context?
72+
context = params.fetch(:context, 'false')
73+
context.casecmp?('true')
74+
end
75+
76+
# include performance data in the results if true
77+
def performance_data?
78+
performance_data = params.fetch(:performance_data, 'false')
79+
performance_data.casecmp?('true')
80+
end
81+
82+
# any params not specifically handled are passed through via replacements
83+
def replacements
84+
params.reject do |k, _v|
85+
['q', 'vocab', 'controller', 'action', 'subauthority', 'lang', 'id',
86+
'context', 'performance_data', 'response_header', 'format'].include?(k)
87+
end
88+
end
89+
90+
# results are returned in the format (applies to fetch only)
91+
def format
92+
f = params.fetch(:format, 'json').downcase
93+
['jsonld', 'n3', 'ntriples'].include?(f) ? f : 'json'
94+
end
95+
end
96+
end
97+
end

0 commit comments

Comments
 (0)