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

pulibrary / bibdata / 2ee2c4fc-5ef0-4806-b86e-01bf70aa67a0

24 Dec 2024 04:55PM UTC coverage: 91.859% (-0.04%) from 91.902%
2ee2c4fc-5ef0-4806-b86e-01bf70aa67a0

Pull #2569

circleci

christinach
Generate new .rubocop_todo.yml
rubocop fix
Pull Request #2569: Rubocop gems

335 of 378 new or added lines in 57 files covered. (88.62%)

2 existing lines in 2 files now uncovered.

3385 of 3685 relevant lines covered (91.86%)

377.92 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

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

13
  # Get /almaws/v1/bibs Retrieve bibs
14
  # @param id [String] e.g. id = "991227830000541"
15
  # @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
16
  # get one bib record is supported in the bibdata UI and in the bibliographic_controller
17
  # @return [AlmaAdapter::MarcRecord]
18
  def get_bib_record(id, show_suppressed: false)
1✔
19
    return nil unless /\A\d+\z/.match? id
24✔
20

21
    get_bib_records([id], show_suppressed:)&.first
23✔
22
  end
23

24
  # Get /almaws/v1/bibs Retrieve bibs
25
  # @param ids [Array] e.g. ids = ["991227850000541","991227840000541","99222441306421"]
26
  # @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
27
  # @return [Array<AlmaAdapter::MarcRecord>]
28
  def get_bib_records(ids, show_suppressed: false)
1✔
29
    cql_query = show_suppressed ? '' : 'alma.mms_tagSuppressed=false%20and%20'
27✔
30
    cql_query << ids.map { |id| "alma.mms_id=#{id}" }.join('%20or%20')
61✔
31
    sru_url = "#{Rails.configuration.alma['sru_url']}?"\
27✔
32
              'version=1.2&operation=searchRetrieve&'\
33
              "recordSchema=marcxml&query=#{cql_query}"\
34
              "&maximumRecords=#{ids.count}"
35
    MARC::XMLReader.new(URI(sru_url).open, parser: :nokogiri).map do |record|
27✔
36
      MarcRecord.new(nil, record)
16✔
37
    end
38
  end
39

40
  def get_availability(ids:)
1✔
NEW
41
    bibs = Alma::Bib.find(Array.wrap(ids), expand: %w[p_avail e_avail d_avail requests].join(',')).each
×
42
    AvailabilityStatus.from_bib(bib: bibs&.first).to_h
×
43
  end
44

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

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

70
  # Returns availability for a list of bib ids
71
  def get_availability_many(ids:, deep_check: false)
1✔
72
    options = { timeout: 20 }
3✔
73
    AlmaAdapter::Execute.call(options:, message: "Find bibs #{ids.join(',')}") do
3✔
74
      bibs = Alma::Bib.find(Array.wrap(ids), expand: %w[p_avail e_avail d_avail requests].join(',')).each
3✔
75
      return nil if bibs.count == 0
1✔
76

77
      availability = bibs.each_with_object({}) do |bib, acc|
1✔
78
        acc[bib.id] = AvailabilityStatus.new(bib:, deep_check:).bib_availability
2✔
79
      end
80
      availability
1✔
81
    end
82
  rescue Alma::StandardError => e
83
    handle_alma_error(client_error: e)
2✔
84
  end
85

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

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

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

108
    res.body.force_encoding('utf-8') if validate_response!(response: res)
5✔
109
  end
110

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

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

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

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

154
      handle_alma_error(client_error: e)
1✔
155
    end
156
  end
157

158
  private
1✔
159

160
    def apikey
1✔
161
      Rails.configuration.alma[:read_only_apikey]
8✔
162
    end
163

164
    def build_alma_error_from(json:)
1✔
165
      error = json.deep_symbolize_keys
13✔
166
      error_code = error[:errorCode]
13✔
167

168
      case error_code
13✔
169
      when 'PER_SECOND_THRESHOLD'
170
        Alma::PerSecondThresholdError.new(error[:errorMessage])
12✔
171
      else
172
        Alma::StandardError.new(error[:errorMessage])
1✔
173
      end
174
    end
175

176
    def build_alma_errors_from(json:)
1✔
177
      error_list = json['errorList']
13✔
178
      errors = error_list['error']
13✔
179
      errors.map { |error| build_alma_error_from(json: error) }
26✔
180
    end
181

182
    def build_alma_errors(from:)
1✔
183
      message = from.message.gsub('=>', ':').gsub('nil', '"null"')
10✔
184
      parsed_message = JSON.parse(message)
10✔
185
      build_alma_errors_from(json: parsed_message)
10✔
186
    end
187

188
    def validate_response!(response:)
1✔
189
      return true if response.status == 200
5✔
190

191
      response_body = JSON.parse(response.body)
3✔
192
      errors = build_alma_errors_from(json: response_body)
3✔
193
      return true if errors.empty?
3✔
194

195
      raise(errors.first)
3✔
196
    end
197

198
    def handle_alma_error(client_error:)
1✔
199
      errors = build_alma_errors(from: client_error)
10✔
200
      raise errors.first if errors.first.is_a?(Alma::PerSecondThresholdError)
10✔
201

202
      raise client_error
1✔
203
    end
204
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