From 4541c467757818a29502a63a95bfc4f5bcf440be Mon Sep 17 00:00:00 2001 From: Greg Tunink Date: Thu, 2 Jul 2026 15:47:05 -0500 Subject: [PATCH 1/3] Augment error handling and messages - Return raw `res` from `post` method so we can interact with it appropriately depending on the response from the search service - DRY up code in different search methods - Make default friendly error message less generic, indicating the search service encountered an error - Handle different errors returned from search service - RuntimeError - Failed to communicate with search service - Forbidden - 40x response indicating access denied, indicating IP or user/pass auth configuration requires attention - BadRequest - Malformed request or search service syntax error, including if the query includes a mix of smart and regular quotes - ExceptionWithResponse - Catch all for all other errors --- app/services/search_service.rb | 59 +++++++++++++++++----------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 78fc536..16796cd 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -34,7 +34,7 @@ def post(url_ending, json) {"content-type" => "json"} ) end - JSON.parse(res.body) + res rescue => e e end @@ -51,13 +51,7 @@ def search_collections }, "size" => 0 } - raw_res = post("_search", req) - if raw_res.class == RuntimeError - on_error(raw_res, req) - else - res = build_collections_response(raw_res) - on_success(req, res) - end + search(req) end def search_item(id) @@ -75,36 +69,17 @@ def search_item(id) if @params["collection"].present? req["query"]["bool"]["must"] << { "term" => { "collection" => @params["collection"] } } end - - raw_res = post("_search", req) - if raw_res.class == RuntimeError - on_error(raw_res, req) - elsif raw_res.class == RestClient::ExceptionWithResponse || - raw_res.class == RestClient::BadRequest - on_error(JSON.parse(raw_res.response), req) - else - res = build_item_response(raw_res) - on_success(req, res) - end + search(req) end def search_items req = build_item_request - raw_res = post("_search", req) - if raw_res.class == RuntimeError - on_error(raw_res.inspect, req) - elsif raw_res.class == RestClient::ExceptionWithResponse || - raw_res.class == RestClient::BadRequest - on_error(JSON.parse(raw_res.response), req) - else - res = build_item_response(raw_res) - on_success(req, res) - end + search(req) end protected - def on_error(error_msg, req, friendly_msg="Something went wrong") + def on_error(error_msg, req, friendly_msg="Search service error") { "req" => { "query_string" => @user_req, @@ -135,6 +110,30 @@ def on_success(req, res) json end + def search(req) + res = post("_search", req) + if res.class == RuntimeError + on_error(res.inspect, req, + "There was an error communicating with to the search service") + elsif res.class == RestClient::Forbidden + on_error(res.response, req, + "Communication with the search service was denied (40x "\ + "Forbidden). Please try again soon.") + elsif res.class == RestClient::BadRequest + on_error(res.response, req, + "Something went wrong with the request to the search service. "\ + "Check for a mix of smart and regular quotes in your search, "\ + "as the search service cannot handle this.") + elsif res.class == RestClient::ExceptionWithResponse + on_error(JSON.parse(res.response), req, + "An error occurred while the search service tried to process "\ + "your request.") + else + res = build_item_response(JSON.parse(res.body)) + on_success(req, res) + end + end + def build_collections_response(res) SearchCollRes.new(res).build_response end From c1fdf76338676e6c3183470e5e5e6ac083586157 Mon Sep 17 00:00:00 2001 From: Greg Tunink Date: Thu, 2 Jul 2026 16:04:12 -0500 Subject: [PATCH 2/3] Reorganize search_service.rb methods - Move post to protected methods - Alphabetize protected methods --- app/services/search_service.rb | 83 +++++++++++++++++----------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 16796cd..c55bc92 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -10,35 +10,6 @@ def initialize(url, params={}, user_req) @user_req = user_req end - def post(url_ending, json) - # Add Basic Authentication header if credentials present - if Rails.application.credentials.elasticsearch.present? && - Rails.application.credentials.elasticsearch[:user].present? && - Rails.application.credentials.elasticsearch[:password].present? - auth_hash = { - "Authorization" => "Basic " + - Base64::encode64( - Rails.application.credentials.elasticsearch[:user] + - ":" + Rails.application.credentials.elasticsearch[:password] - ) - } - res = RestClient.post( - @url + "/" + url_ending, - json.to_json, - auth_hash.merge({ "content-type" => "json" }) - ) - else - res = RestClient.post( - @url + "/" + url_ending, - json.to_json, - {"content-type" => "json"} - ) - end - res - rescue => e - e - end - def search_collections req = { "aggs" => { @@ -79,6 +50,18 @@ def search_items protected + def build_collections_response(res) + SearchCollRes.new(res).build_response + end + + def build_item_request + SearchItemReq.new(@params).build_request + end + + def build_item_response(res) + SearchItemRes.new(res).build_response + end + def on_error(error_msg, req, friendly_msg="Search service error") { "req" => { @@ -110,6 +93,35 @@ def on_success(req, res) json end + def post(url_ending, json) + # Add Basic Authentication header if credentials present + if Rails.application.credentials.elasticsearch.present? && + Rails.application.credentials.elasticsearch[:user].present? && + Rails.application.credentials.elasticsearch[:password].present? + auth_hash = { + "Authorization" => "Basic " + + Base64::encode64( + Rails.application.credentials.elasticsearch[:user] + + ":" + Rails.application.credentials.elasticsearch[:password] + ) + } + res = RestClient.post( + @url + "/" + url_ending, + json.to_json, + auth_hash.merge({ "content-type" => "json" }) + ) + else + res = RestClient.post( + @url + "/" + url_ending, + json.to_json, + {"content-type" => "json"} + ) + end + res + rescue => e + e + end + def search(req) res = post("_search", req) if res.class == RuntimeError @@ -133,17 +145,4 @@ def search(req) on_success(req, res) end end - - def build_collections_response(res) - SearchCollRes.new(res).build_response - end - - def build_item_request - SearchItemReq.new(@params).build_request - end - - def build_item_response(res) - SearchItemRes.new(res).build_response - end - end From 2fc50a341a9f6686d8816cd5fa9ff221ae73843d Mon Sep 17 00:00:00 2001 From: Greg Tunink Date: Thu, 2 Jul 2026 16:11:19 -0500 Subject: [PATCH 3/3] Update gems, drop Ruby version in Gemfile.lock --- Gemfile.lock | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2632547..9bf89f0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,46 +61,47 @@ GEM tzinfo (~> 2.0) zeitwerk (~> 2.3) base64 (0.3.0) - bootsnap (1.19.0) + bootsnap (1.24.6) msgpack (~> 1.2) builder (3.3.0) byebug (12.0.0) - concurrent-ruby (1.3.6) - crass (1.0.6) + concurrent-ruby (1.3.7) + crass (1.0.7) date (3.5.1) domain_name (0.6.20240107) erubi (1.13.1) - ffi (1.17.2-x86_64-linux-gnu) - globalid (1.3.0) + ffi (1.17.4-x86_64-linux-gnu) + globalid (1.4.0) activesupport (>= 6.1) http-accept (1.7.0) - http-cookie (1.1.0) + http-cookie (1.1.6) domain_name (~> 0.5) - i18n (1.14.7) + i18n (1.15.2) concurrent-ruby (~> 1.0) - listen (3.9.0) + listen (3.10.0) + logger rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) - loofah (2.25.0) + loofah (2.25.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) - mail (2.9.0) + mail (2.9.1) logger mini_mime (>= 0.1.1) net-imap net-pop net-smtp - marcel (1.1.0) + marcel (1.2.1) method_source (1.1.0) mime-types (3.7.0) logger mime-types-data (~> 3.2025, >= 3.2025.0507) - mime-types-data (3.2025.0924) + mime-types-data (3.2026.0701) mini_mime (1.1.5) minitest (5.27.0) - msgpack (1.8.0) - net-imap (0.5.13) + msgpack (1.8.3) + net-imap (0.5.15) date net-protocol net-pop (0.1.2) @@ -113,10 +114,10 @@ GEM nio4r (2.7.5) nokogiri (1.18.10-x86_64-linux-gnu) racc (~> 1.4) - puma (7.1.0) + puma (8.0.2) nio4r (~> 2.0) racc (1.8.1) - rack (2.2.21) + rack (2.2.23) rack-test (2.2.0) rack (>= 1.3) rails (6.1.7.10) @@ -138,8 +139,8 @@ GEM activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.2) - loofah (~> 2.21) + rails-html-sanitizer (1.7.0) + loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) railties (6.1.7.10) actionpack (= 6.1.7.10) @@ -147,7 +148,7 @@ GEM method_source rake (>= 12.2) thor (~> 1.0) - rake (13.3.1) + rake (13.4.2) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) @@ -156,7 +157,7 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) - spring (4.4.0) + spring (4.7.0) sprockets (4.2.2) concurrent-ruby (~> 1.0) logger @@ -166,11 +167,11 @@ GEM activesupport (>= 6.1) sprockets (>= 3.0.0) sqlite3 (1.7.3-x86_64-linux) - thor (1.4.0) - timeout (0.6.0) + thor (1.5.0) + timeout (0.6.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - websocket-driver (0.8.0) + websocket-driver (0.8.2) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -190,8 +191,5 @@ DEPENDENCIES sqlite3 (~> 1.4) tzinfo-data -RUBY VERSION - ruby 3.1.6p260 - BUNDLED WITH 2.3.27