Skip to content

Commit b2d9762

Browse files
committed
Initial tabbed Primo/TIMDEX interface
Why these changes are being introduced: USE UI needs affordances to switch between results from different sources. Primo results should be the default view. Relevant ticket(s): * [USE-31](https://mitlibraries.atlassian.net/browse/USE-31) * [TIMX-549](https://mitlibraries.atlassian.net/browse/TIMX-549) How this addresses that need: * Integrates Primo search into the controller and view layers * Adds Turbo frame 'tabs' to switch between Primo and TIMDEX results. Side effects of this change: * Maintaining separate views for each result type is probably not ideal, but I'm accepting it as a risk until we normalize TIMDEX records similarly to how we normalize Primo. * Several tests have been skipped for features that are no longer relevant to USE UI, but are core to GDT. We will need to revise our overall test strategy such that these features are tested as part of GDT. * FRBRized full record links don't always work. It the method we use breaks FRBR links for CDI records. Predictably, Ex Libris documentation on FRBR does not address this issue. We might decide to forgo FRBR links in general, or perhaps limit them by content type (assuming book results always come from Alma). * Pagination is not yet implemented.
1 parent 5ec7532 commit b2d9762

File tree

7 files changed

+675
-403
lines changed

7 files changed

+675
-403
lines changed

app/controllers/search_controller.rb

Lines changed: 91 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,59 +15,102 @@ def results
1515
# inject session preference for boolean type if it is present
1616
params[:booleanType] = cookies[:boolean_type] || 'AND'
1717

18-
# hand off to Enhancer chain
18+
# Determine which tab to load - default to primo unless gdt is enabled
19+
@active_tab = if Flipflop.enabled?(:gdt)
20+
'gdt' # Keep existing GDT behavior unchanged
21+
else
22+
params[:tab] || 'primo' # Default to primo for new tabbed interface
23+
end
1924
@enhanced_query = Enhancer.new(params).enhanced_query
2025

21-
# hand off enhanced query to builder
22-
query = QueryBuilder.new(@enhanced_query).query
26+
# Route to appropriate search based on active tab
27+
if Flipflop.enabled?(:gdt)
28+
# Keep existing GDT behavior unchanged
29+
load_gdt_results
30+
else
31+
case @active_tab
32+
when 'primo'
33+
load_primo_results
34+
when 'timdex'
35+
load_timdex_results
36+
end
37+
end
38+
end
2339

24-
# Create cache key for this query
25-
# Sorting query hash to ensure consistent key generation regardless of the parameter order
26-
sorted_query = query.sort_by { |k, v| k.to_sym }.to_h
27-
cache_key = Digest::MD5.hexdigest(sorted_query.to_s)
40+
private
2841

29-
# builder hands off to wrapper which returns raw results here
30-
# We are using two difference caches to allow for Geo and USE to be cached separately. This ensures we don't have
31-
# cache key collission for these two different query types. In practice, the likelihood of this happening is low,
32-
# as the query parameters are different for each type and they won't often be run with the same cache backend other
33-
# than locally, but this is a safeguard.
34-
# The response type is a GraphQL::Client::Response, which is not directly serializable, so we convert it to a hash.
35-
response = if Flipflop.enabled?(:gdt)
36-
Rails.cache.fetch("#{cache_key}/geo", expires_in: 12.hours) do
37-
raw = execute_geospatial_query(query)
38-
{
39-
data: raw.data.to_h,
40-
errors: raw.errors.details.to_h
41-
}
42-
end
43-
else
44-
Rails.cache.fetch("#{cache_key}/use", expires_in: 12.hours) do
45-
raw = TimdexBase::Client.query(TimdexSearch::BaseQuery, variables: query)
46-
{
47-
data: raw.data.to_h,
48-
errors: raw.errors.details.to_h
49-
}
50-
end
51-
end
42+
def load_gdt_results
43+
query = QueryBuilder.new(@enhanced_query).query
44+
45+
response = cache_timdex_query(query)
5246

5347
# Handle errors
5448
@errors = extract_errors(response)
55-
56-
# Analayze results
57-
# The @pagination instance variable includes info about next/previous pages (where they exist) to assist the UI.
5849
@pagination = Analyzer.new(@enhanced_query, response).pagination if @errors.nil?
59-
60-
# Display results
6150
@results = extract_results(response)
6251
@filters = extract_filters(response)
6352
end
6453

65-
private
54+
def load_primo_results
55+
begin
56+
primo_search = PrimoSearch.new
57+
per_page = params[:per_page] || 20
58+
primo_response = primo_search.search(params[:q], per_page)
59+
60+
@results = NormalizePrimoResults.new(primo_response, params[:q]).normalize
61+
62+
# Basic pagination for now.
63+
if @results.present?
64+
@pagination = {
65+
hits: @results.count,
66+
start: 1,
67+
end: @results.count
68+
}
69+
end
70+
71+
rescue StandardError => e
72+
@errors = handle_primo_errors(e)
73+
end
74+
end
75+
76+
def load_timdex_results
77+
query = QueryBuilder.new(@enhanced_query).query
78+
response = cache_timdex_query(query)
79+
80+
@errors = extract_errors(response)
81+
@pagination = Analyzer.new(@enhanced_query, response).pagination if @errors.nil?
82+
@results = extract_results(response)
83+
end
6684

6785
def active_filters
6886
ENV.fetch('ACTIVE_FILTERS', '').split(',').map(&:strip)
6987
end
7088

89+
def cache_timdex_query(query)
90+
# Create cache key for this query
91+
# Sorting query hash to ensure consistent key generation regardless of the parameter order
92+
sorted_query = query.sort_by { |k, v| k.to_sym }.to_h
93+
cache_key = Digest::MD5.hexdigest(sorted_query.to_s)
94+
95+
# builder hands off to wrapper which returns raw results here
96+
# We are using two difference caches to allow for Geo and USE to be cached separately. This ensures we don't have
97+
# cache key collision for these two different query types. In practice, the likelihood of this happening is low,
98+
# as the query parameters are different for each type and they won't often be run with the same cache backend other
99+
# than locally, but this is a safeguard.
100+
# The response type is a GraphQL::Client::Response, which is not directly serializable, so we convert it to a hash.
101+
Rails.cache.fetch("#{cache_key}/#{@active_tab}", expires_in: 12.hours) do
102+
raw = if @active_tab == 'gdt'
103+
execute_geospatial_query(query)
104+
elsif @active_tab == 'timdex'
105+
TimdexBase::Client.query(TimdexSearch::BaseQuery, variables: query)
106+
end
107+
{
108+
data: raw.data.to_h,
109+
errors: raw.errors.details.to_h
110+
}
111+
end
112+
end
113+
71114
def execute_geospatial_query(query)
72115
if query['geobox'] == 'true' && query[:geodistance] == 'true'
73116
TimdexBase::Client.query(TimdexSearch::AllQuery, variables: query)
@@ -214,4 +257,16 @@ def validate_geobox_values!
214257
flash[:error] = 'Maximum latitude cannot exceed minimum latitude.'
215258
redirect_to root_url
216259
end
260+
261+
def handle_primo_errors(error)
262+
Rails.logger.error("Primo search error: #{error.message}")
263+
264+
if error.is_a?(ArgumentError)
265+
[{ 'message' => 'Primo search is not properly configured.' }]
266+
elsif error.is_a?(HTTP::TimeoutError)
267+
[{ 'message' => 'The Primo service is currently slow to respond. Please try again.' }]
268+
else
269+
[{ 'message' => error.message }]
270+
end
271+
end
217272
end

0 commit comments

Comments
 (0)