diff --git a/app/models/alma_sru.rb b/app/models/alma_sru.rb new file mode 100644 index 00000000..942ab257 --- /dev/null +++ b/app/models/alma_sru.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +# Queries the Alma SRU endpoint for holdings data +# +# @reference https://developers.exlibrisgroup.com/alma/integrations/SRU/ +class AlmaSru + class LookupFailure < StandardError; end + + class InvalidAlmaId < StandardError; end + + # lookup will receive an Alma ID, validate it, look it up in the Alma SRU, and return a formatted result. + # + # It accepts an "alma_client" argument for use when testing, but this is not used in normal operations. + def self.lookup(raw_identifier, alma_client: nil) + return nil unless alma_base_url + + # Validate the raw identifier received. This will raise an InvalidAlmaId if validation fails. + identifier = validate_alma_id(raw_identifier) + + # Build URL + url = alma_sru_url(identifier) + + # Retrieve that URL + alma_http = setup(url, alma_client) + + parse_response(alma_http.timeout(6).get(url)) + rescue InvalidAlmaId + Rails.logger.error("Invalid Alma ID: #{raw_identifier}") + rescue LookupFailure => e + Rails.logger.error("Alma lookup failure: #{e}") + end + + def self.parse_response(raw_response) + raise LookupFailure, raw_response.status unless raw_response.status == 200 + + raw_response + end + + # validate_alma_id ensures we are only submitting valid Alma IDs to the SRU endpoint. + # + # It needs to do two thigns: + # 1. Remove the "alma" prefix if one is present. Otherwise, no manipulation of the submitted value should occur. + # 2. Enforce the formatting requirements for a valid alma identifier (start with "99", and end with "6761"). + def self.validate_alma_id(raw) + parsed = if raw.start_with?('alma') + raw.delete_prefix('alma') + else + raw + end + + raise InvalidAlmaId unless parsed.to_s.start_with?('99') + raise InvalidAlmaId unless parsed.to_s.end_with?('6761') + + parsed + end + + def self.alma_base_url + ENV.fetch('MIT_ALMA_URL', nil) + end + + def self.alma_sru_url(identifier) + # example identifier: 990000959610106761 + "#{alma_base_url}/view/sru/#{ENV.fetch('EXL_INST_ID')}?version=1.2&operation=searchRetrieve&recordSchema=marcxml" \ + "&query=alma.all_for_ui=#{identifier}" + end + + def self.setup(url, alma_client) + alma_client || HTTP.persistent(url) + end +end diff --git a/test/models/alma_sru_test.rb b/test/models/alma_sru_test.rb new file mode 100644 index 00000000..47a1705d --- /dev/null +++ b/test/models/alma_sru_test.rb @@ -0,0 +1,23 @@ +require 'test_helper' + +class AlmaSruTest < ActiveSupport::TestCase + test 'validate_alma_id succeeds with valid numeric input' do + needle = '990002935920106761' + assert_nothing_raised do + AlmaSru.validate_alma_id(needle) + end + end + + test 'validate_alma_id succeeds despite and "alma" prefix' do + needle = 'alma990002935920106761' + assert_nothing_raised do + AlmaSru.validate_alma_id(needle) + end + end + + test 'validate_alma_id raises InvalidAlmaId with invalid input' do + assert_raises(AlmaSru::InvalidAlmaId) do + AlmaSru.validate_alma_id('foo') + end + end +end