• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pulibrary / bibdata / 373ad6ff-fad2-405a-ab26-3d30fb5ceecf

24 Dec 2024 08:24PM UTC coverage: 91.938% (+0.08%) from 91.859%
373ad6ff-fad2-405a-ab26-3d30fb5ceecf

Pull #2563

circleci

maxkadel
Put attaching xml files in their own batch
Pull Request #2563: I2321 Shift SCSB full index tasks into separate background jobs

152 of 156 new or added lines in 10 files covered. (97.44%)

65 existing lines in 17 files now uncovered.

3478 of 3783 relevant lines covered (91.94%)

366.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.0
/app/adapters/alma_adapter.rb
1
require 'open-uri'
1✔
2

3
class AlmaAdapter
1✔
4
  class ::Alma::PerSecondThresholdError < Alma::StandardError; end
1✔
5
  class ::Alma::NotFoundError < Alma::StandardError; end
1✔
6

7
  attr_reader :connection
1✔
8
  def initialize(connection: AlmaAdapter::Connector.connection)
1✔
9
    @connection = connection
96✔
10
  end
11

12
  # Get /almaws/v1/bibs Retrieve bibs
13
  # @param id [String] e.g. id = "991227830000541"
14
  # @see https://developers.exlibrisgroup.com/console/?url=/wp-content/uploads/alma/openapi/bibs.json#/Catalog/get%2Falmaws%2Fv1%2Fbibs Values that could be passed to the alma API
15
  # get one bib record is supported in the bibdata UI and in the bibliographic_controller
16
  # @return [AlmaAdapter::MarcRecord]
17
  def get_bib_record(id, show_suppressed: false)
1✔
18
    return nil unless /\A\d+\z/.match? id
24✔
19
    get_bib_records([id], show_suppressed:)&.first
23✔
20
  end
21

22
  # Get /almaws/v1/bibs Retrieve bibs
23
  # @param ids [Array] e.g. ids = ["991227850000541","991227840000541","99222441306421"]
24
  # @see https://developers.exlibrisgroup.com/console/?url=/wp-content/uploads/alma/openapi/bibs.json#/Catalog/get%2Falmaws%2Fv1%2Fbibs Values that could be passed to the alma API
25
  # @return [Array<AlmaAdapter::MarcRecord>]
26
  def get_bib_records(ids, show_suppressed: false)
1✔
27
    cql_query = show_suppressed ? "" : "alma.mms_tagSuppressed=false%20and%20"
27✔
28
    cql_query << ids.map { |id| "alma.mms_id=#{id}" }.join("%20or%20")
61✔
29
    sru_url = "#{Rails.configuration.alma['sru_url']}?"\
27✔
30
              "version=1.2&operation=searchRetrieve&"\
31
              "recordSchema=marcxml&query=#{cql_query}"\
32
              "&maximumRecords=#{ids.count}"
33
    MARC::XMLReader.new(URI(sru_url).open, parser: :nokogiri).map do |record|
27✔
34
      MarcRecord.new(nil, record)
16✔
35
    end
36
  end
37

38
  def get_availability(ids:)
1✔
UNCOV
39
    bibs = Alma::Bib.find(Array.wrap(ids), expand: ["p_avail", "e_avail", "d_avail", "requests"].join(",")).each
×
UNCOV
40
    AvailabilityStatus.from_bib(bib: bibs&.first).to_h
×
41
  end
42

43
  # Returns availability for one bib id
44
  def get_availability_one(id:, deep_check: false)
1✔
45
    get_availability_status = lambda do |bib|
15✔
46
      av_info = AvailabilityStatus.new(bib:, deep_check:).bib_availability
11✔
47
      temp_locations = av_info.any? { |_key, value| value[:temp_location] }
21✔
48
      if temp_locations && deep_check
11✔
49
        # We don't get enough information at the holding level for items located
50
        # in temporary locations. But if the client requests it we can drill into
51
        # the item information to get all the information (keep in mind that this
52
        # involves an extra API call that is slow-ish.)
53
        AvailabilityStatus.new(bib:).bib_availability_from_items
1✔
54
      else
55
        av_info
10✔
56
      end
57
    end
58

59
    Alma::Bib.find(Array.wrap(id), expand: ["p_avail", "e_avail", "d_avail", "requests"].join(","))
15✔
60
             .each
61
             .lazy
62
             .map { |bib| { bib.id => get_availability_status.call(bib) } }
11✔
63
             .first
64
  rescue Alma::StandardError => e
65
    handle_alma_error(client_error: e)
2✔
66
  end
67

68
  # Returns availability for a list of bib ids
69
  def get_availability_many(ids:, deep_check: false)
1✔
70
    options = { timeout: 20 }
3✔
71
    AlmaAdapter::Execute.call(options:, message: "Find bibs #{ids.join(',')}") do
3✔
72
      bibs = Alma::Bib.find(Array.wrap(ids), expand: ["p_avail", "e_avail", "d_avail", "requests"].join(",")).each
3✔
73
      return nil if bibs.count == 0
1✔
74
      availability = bibs.each_with_object({}) do |bib, acc|
1✔
75
        acc[bib.id] = AvailabilityStatus.new(bib:, deep_check:).bib_availability
2✔
76
      end
77
      availability
1✔
78
    end
79
  rescue Alma::StandardError => e
80
    handle_alma_error(client_error: e)
2✔
81
  end
82

83
  def get_availability_holding(id:, holding_id:)
1✔
84
    # Fetch the bib record and get the information for the individual holding
85
    bibs = Alma::Bib.find(Array.wrap(id), expand: ["p_avail", "e_avail", "d_avail", "requests"].join(",")).each
13✔
86
    return nil if bibs.count == 0
13✔
87

88
    # Fetch the items for the holding and create
89
    # the availability response for each item
90
    bib_status = AvailabilityStatus.from_bib(bib: bibs.first)
12✔
91
    holding_items = bib_status.holding_item_data(holding_id:)
12✔
92
    holding_items[:items].map(&:availability_summary)
10✔
93
  rescue Alma::StandardError => e
94
    handle_alma_error(client_error: e)
2✔
95
  end
96

97
  # Returns list of holding records for a given MMS
98
  # @param id [string]. e.g id = "991227850000541"
99
  def get_holding_records(id)
1✔
100
    res = connection.get(
8✔
101
      "bibs/#{id}/holdings",
102
      apikey:
103
    )
104

105
    res.body.force_encoding("utf-8") if validate_response!(response: res)
5✔
106
  end
107

108
  # @param id [String]. e.g id = "991227850000541"
109
  # @return [AlmaAdapter::BibItemSet]
110
  def get_items_for_bib(id)
1✔
111
    alma_options = { timeout: 10 }
15✔
112
    AlmaAdapter::Execute.call(options: alma_options, message: "Find items for bib #{id}") do
15✔
113
      find_options = { limit: Alma::BibItemSet::ITEMS_PER_PAGE, expand: "due_date_policy,due_date", order_by: "library", direction: "asc" }
15✔
114
      bib_items = Alma::BibItem.find(id, find_options).all.map { |item| AlmaAdapter::AlmaItem.new(item) }
38✔
115
      AlmaAdapter::BibItemSet.new(items: bib_items, adapter: self)
9✔
116
    end
117
  end
118

119
  def item_by_barcode(barcode)
1✔
120
    item = Alma::BibItem.find_by_barcode(barcode)
7✔
121
    if item["errorsExist"]
7✔
122
      # In this case although `item` is an object of type Alma::BibItem, its
123
      # content is really an HTTPartyResponse with the error information. :shrug:
124
      message = item.item.parsed_response.to_s
2✔
125
      error = message.include?("No items found") ? Alma::NotFoundError.new(message) : Alma::StandardError.new(message)
2✔
126
      handle_alma_error(client_error: error)
2✔
127
    end
128
    item
5✔
129
  end
130

131
  def holding_by_id(mms_id:, holding_id:)
1✔
132
    holding = Alma::BibHolding.find(mms_id:, holding_id:)
5✔
133
    if holding["errorsExist"]
5✔
134
      # In this case although `holding` is an object of type Alma::BibHolding, its
135
      # content is really an HTTPartyResponse with the error information. :shrug:
136
      error = Alma::StandardError.new(holding.holding.parsed_response.to_s)
1✔
137
      handle_alma_error(client_error: error)
1✔
138
    end
139
    holding
4✔
140
  end
141

142
  def find_user(patron_id)
1✔
143
    options = { enable_loggable: true }
10✔
144
    AlmaAdapter::Execute.call(options:, message: "Find user #{patron_id}") do
10✔
145
      return Alma::User.find(patron_id, expand: '')
10✔
146
    rescue Alma::StandardError => e
147
      # The Alma gem throws "not found" for all errors but only error code 401861
148
      # really represents a record not found.
149
      raise Alma::NotFoundError, "User #{patron_id} was not found" if e.message.include?('"errorCode":"401861"')
2✔
150
      handle_alma_error(client_error: e)
1✔
151
    end
152
  end
153

154
  private
1✔
155

156
    def apikey
1✔
157
      Rails.configuration.alma[:read_only_apikey]
8✔
158
    end
159

160
    def build_alma_error_from(json:)
1✔
161
      error = json.deep_symbolize_keys
13✔
162
      error_code = error[:errorCode]
13✔
163

164
      case error_code
13✔
165
      when "PER_SECOND_THRESHOLD"
166
        Alma::PerSecondThresholdError.new(error[:errorMessage])
12✔
167
      else
168
        Alma::StandardError.new(error[:errorMessage])
1✔
169
      end
170
    end
171

172
    def build_alma_errors_from(json:)
1✔
173
      error_list = json["errorList"]
13✔
174
      errors = error_list["error"]
13✔
175
      errors.map { |error| build_alma_error_from(json: error) }
26✔
176
    end
177

178
    def build_alma_errors(from:)
1✔
179
      message = from.message.gsub('=>', ':').gsub('nil', '"null"')
10✔
180
      parsed_message = JSON.parse(message)
10✔
181
      build_alma_errors_from(json: parsed_message)
10✔
182
    end
183

184
    def validate_response!(response:)
1✔
185
      return true if response.status == 200
5✔
186

187
      response_body = JSON.parse(response.body)
3✔
188
      errors = build_alma_errors_from(json: response_body)
3✔
189
      return true if errors.empty?
3✔
190

191
      raise(errors.first)
3✔
192
    end
193

194
    def handle_alma_error(client_error:)
1✔
195
      errors = build_alma_errors(from: client_error)
10✔
196
      raise errors.first if errors.first.is_a?(Alma::PerSecondThresholdError)
10✔
197
      raise client_error
1✔
198
    end
199
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc