alma 0.2.6 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +54 -0
- data/.circleci/setup-rubygems.sh +3 -0
- data/.github/dependabot.yml +7 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +134 -0
- data/.ruby-version +1 -1
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile +4 -3
- data/Guardfile +75 -0
- data/README.md +136 -26
- data/Rakefile +3 -1
- data/alma.gemspec +21 -16
- data/lib/alma/alma_record.rb +3 -3
- data/lib/alma/api_defaults.rb +39 -0
- data/lib/alma/availability_response.rb +50 -53
- data/lib/alma/bib.rb +26 -42
- data/lib/alma/bib_holding.rb +25 -0
- data/lib/alma/bib_item.rb +28 -38
- data/lib/alma/bib_item_set.rb +72 -12
- data/lib/alma/bib_set.rb +7 -21
- data/lib/alma/config.rb +10 -4
- data/lib/alma/course.rb +47 -0
- data/lib/alma/course_set.rb +17 -0
- data/lib/alma/electronic/README.md +20 -0
- data/lib/alma/electronic/batch_utils.rb +224 -0
- data/lib/alma/electronic/business.rb +29 -0
- data/lib/alma/electronic.rb +167 -0
- data/lib/alma/error.rb +16 -4
- data/lib/alma/fine.rb +16 -0
- data/lib/alma/fine_set.rb +36 -21
- data/lib/alma/item_request_options.rb +23 -0
- data/lib/alma/library.rb +29 -0
- data/lib/alma/library_set.rb +21 -0
- data/lib/alma/loan.rb +31 -2
- data/lib/alma/loan_set.rb +62 -15
- data/lib/alma/location.rb +29 -0
- data/lib/alma/location_set.rb +21 -0
- data/lib/alma/renewal_response.rb +19 -11
- data/lib/alma/request.rb +167 -0
- data/lib/alma/request_options.rb +36 -17
- data/lib/alma/request_set.rb +64 -15
- data/lib/alma/response.rb +45 -0
- data/lib/alma/result_set.rb +27 -35
- data/lib/alma/user.rb +111 -92
- data/lib/alma/user_request.rb +19 -0
- data/lib/alma/user_set.rb +5 -6
- data/lib/alma/version.rb +3 -1
- data/lib/alma.rb +34 -22
- data/log/.gitignore +4 -0
- metadata +118 -10
- data/.travis.yml +0 -5
- data/lib/alma/api.rb +0 -33
    
        data/lib/alma/loan_set.rb
    CHANGED
    
    | @@ -1,29 +1,76 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Alma
         | 
| 2 | 
            -
              class LoanSet
         | 
| 3 | 
            -
                 | 
| 4 | 
            -
                 | 
| 5 | 
            -
             | 
| 4 | 
            +
              class LoanSet < ResultSet
         | 
| 5 | 
            +
                class ResponseError < Alma::StandardError
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                alias :total_records :total_record_count
         | 
| 9 | 
            +
             | 
| 10 | 
            +
             | 
| 11 | 
            +
                attr_reader :results, :raw_response
         | 
| 12 | 
            +
                def_delegators :results, :empty?
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def initialize(raw_response, search_args = {})
         | 
| 15 | 
            +
                  @raw_response = raw_response
         | 
| 16 | 
            +
                  @response = raw_response.parsed_response
         | 
| 17 | 
            +
                  @search_args = search_args
         | 
| 18 | 
            +
                  validate(raw_response)
         | 
| 19 | 
            +
                  @results = @response.fetch(key, [])
         | 
| 20 | 
            +
                    .map { |item| single_record_class.new(item) }
         | 
| 21 | 
            +
                  # args passed to the search that returned this set
         | 
| 22 | 
            +
                  # such as limit, expand, order_by, etc
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def loggable
         | 
| 26 | 
            +
                  { search_args: @search_args,
         | 
| 27 | 
            +
                    uri: @raw_response&.request&.uri.to_s
         | 
| 28 | 
            +
                  }.select { |k, v| !(v.nil? || v.empty?) }
         | 
| 29 | 
            +
                end
         | 
| 6 30 |  | 
| 7 | 
            -
                 | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 31 | 
            +
                def validate(response)
         | 
| 32 | 
            +
                  if response.code != 200
         | 
| 33 | 
            +
                    error = "Could not find loans info."
         | 
| 34 | 
            +
                    log = loggable.merge(response.parsed_response)
         | 
| 35 | 
            +
                    raise ResponseError.new(error, log)
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 10 38 |  | 
| 11 | 
            -
                def  | 
| 12 | 
            -
                   | 
| 39 | 
            +
                def all
         | 
| 40 | 
            +
                  Enumerator.new do |yielder|
         | 
| 41 | 
            +
                    offset = 0
         | 
| 42 | 
            +
                    loop do
         | 
| 43 | 
            +
                      extra_args = @search_args.merge({ limit: 100, offset: offset })
         | 
| 44 | 
            +
                      r = (offset == 0) ? self : single_record_class.where_user(user_id, extra_args)
         | 
| 45 | 
            +
                      unless r.empty?
         | 
| 46 | 
            +
                        r.map { |item| yielder << item }
         | 
| 47 | 
            +
                        offset += 100
         | 
| 48 | 
            +
                      else
         | 
| 49 | 
            +
                        raise StopIteration
         | 
| 50 | 
            +
                      end
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                  end
         | 
| 13 53 | 
             
                end
         | 
| 14 54 |  | 
| 15 | 
            -
                def  | 
| 16 | 
            -
                   | 
| 55 | 
            +
                def each(&block)
         | 
| 56 | 
            +
                  @results.each(&block)
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def success?
         | 
| 60 | 
            +
                  raw_response.response.code.to_s == "200"
         | 
| 17 61 | 
             
                end
         | 
| 18 62 |  | 
| 19 63 | 
             
                def key
         | 
| 20 | 
            -
                   | 
| 64 | 
            +
                  "item_loan"
         | 
| 21 65 | 
             
                end
         | 
| 22 66 |  | 
| 23 | 
            -
                def  | 
| 24 | 
            -
                   | 
| 67 | 
            +
                def single_record_class
         | 
| 68 | 
            +
                  Alma::Loan
         | 
| 25 69 | 
             
                end
         | 
| 26 | 
            -
                alias :total_records :total_record_count
         | 
| 27 70 |  | 
| 71 | 
            +
                private
         | 
| 72 | 
            +
                  def user_id
         | 
| 73 | 
            +
                    @user_id ||= results.first.user_id
         | 
| 74 | 
            +
                  end
         | 
| 28 75 | 
             
              end
         | 
| 29 76 | 
             
            end
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Alma
         | 
| 4 | 
            +
              class Location < AlmaRecord
         | 
| 5 | 
            +
                extend Alma::ApiDefaults
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def self.all(library_code:, args: {})
         | 
| 8 | 
            +
                  response = HTTParty.get("#{configuration_base_path}/libraries/#{library_code}/locations", query: args, headers: headers, timeout: timeout)
         | 
| 9 | 
            +
                  if response.code == 200
         | 
| 10 | 
            +
                    LocationSet.new(response)
         | 
| 11 | 
            +
                  else
         | 
| 12 | 
            +
                    raise StandardError, get_body_from(response)
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def self.find(library_code:, location_code:, args: {})
         | 
| 17 | 
            +
                  response = HTTParty.get("#{configuration_base_path}/libraries/#{library_code}/locations/#{location_code}", query: args, headers: headers, timeout: timeout)
         | 
| 18 | 
            +
                  if response.code == 200
         | 
| 19 | 
            +
                    AlmaRecord.new(response)
         | 
| 20 | 
            +
                  else
         | 
| 21 | 
            +
                    raise StandardError, get_body_from(response)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def self.get_body_from(response)
         | 
| 26 | 
            +
                  JSON.parse(response.body)
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Alma
         | 
| 4 | 
            +
              class LocationSet < ResultSet
         | 
| 5 | 
            +
                def_delegators :results, :[], :empty?
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def each(&block)
         | 
| 8 | 
            +
                  results.each(&block)
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def results
         | 
| 12 | 
            +
                  @results ||= @response.fetch(key, [])
         | 
| 13 | 
            +
                    .map { |item| single_record_class.new(item) }
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                protected
         | 
| 17 | 
            +
                  def key
         | 
| 18 | 
            +
                    "location"
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -1,31 +1,40 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Alma
         | 
| 2 4 | 
             
              class RenewalResponse
         | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 5 | 
             
                def initialize(response)
         | 
| 7 | 
            -
                  @ | 
| 8 | 
            -
                  @ | 
| 6 | 
            +
                  @raw_response = response
         | 
| 7 | 
            +
                  @response = response.parsed_response
         | 
| 8 | 
            +
                  @success  = response.has_key?("loan_id")
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def loggable
         | 
| 12 | 
            +
                  { uri: @raw_response&.request&.uri.to_s
         | 
| 13 | 
            +
                  }.select { |k, v| !(v.nil? || v.empty?) }
         | 
| 9 14 | 
             
                end
         | 
| 10 15 |  | 
| 11 16 | 
             
                def renewed?
         | 
| 12 17 | 
             
                  @success
         | 
| 13 18 | 
             
                end
         | 
| 14 19 |  | 
| 20 | 
            +
                def has_error?
         | 
| 21 | 
            +
                  !renewed?
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 15 24 | 
             
                def due_date
         | 
| 16 | 
            -
                  @response.fetch( | 
| 25 | 
            +
                  @response.fetch("due_date", "")
         | 
| 17 26 | 
             
                end
         | 
| 18 27 |  | 
| 19 28 |  | 
| 20 29 | 
             
                def due_date_pretty
         | 
| 21 | 
            -
                  Time.parse(due_date).strftime( | 
| 30 | 
            +
                  Time.parse(due_date).strftime("%m-%e-%y %H:%M")
         | 
| 22 31 | 
             
                end
         | 
| 23 32 |  | 
| 24 33 | 
             
                def item_title
         | 
| 25 34 | 
             
                  if renewed?
         | 
| 26 | 
            -
                    @response[ | 
| 35 | 
            +
                    @response["title"]
         | 
| 27 36 | 
             
                  else
         | 
| 28 | 
            -
                     | 
| 37 | 
            +
                    "This Item"
         | 
| 29 38 | 
             
                  end
         | 
| 30 39 | 
             
                end
         | 
| 31 40 |  | 
| @@ -38,8 +47,7 @@ module Alma | |
| 38 47 | 
             
                end
         | 
| 39 48 |  | 
| 40 49 | 
             
                def error_message
         | 
| 41 | 
            -
             | 
| 50 | 
            +
                  @response unless renewed?
         | 
| 42 51 | 
             
                end
         | 
| 43 | 
            -
             | 
| 44 52 | 
             
              end
         | 
| 45 53 | 
             
            end
         | 
    
        data/lib/alma/request.rb
    ADDED
    
    | @@ -0,0 +1,167 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Alma
         | 
| 4 | 
            +
              class BibRequest
         | 
| 5 | 
            +
                class  ItemAlreadyExists < Alma::StandardError
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                extend Alma::ApiDefaults
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                REQUEST_TYPES = %w[HOLD DIGITIZATION BOOKING]
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def self.submit(args)
         | 
| 13 | 
            +
                  request = new(args)
         | 
| 14 | 
            +
                  response = HTTParty.post(
         | 
| 15 | 
            +
                    "#{bibs_base_path}/#{request.mms_id}/requests",
         | 
| 16 | 
            +
                    query: { user_id: request.user_id },
         | 
| 17 | 
            +
                    headers: headers,
         | 
| 18 | 
            +
                    body: request.body.to_json
         | 
| 19 | 
            +
                    )
         | 
| 20 | 
            +
                  Alma::Response.new(response)
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                attr_reader :mms_id, :user_id, :body, :request_type
         | 
| 24 | 
            +
                def initialize(args)
         | 
| 25 | 
            +
                  @mms_id = args.delete(:mms_id) { raise ArgumentError.new(":mms_id option must be specified to create request") }
         | 
| 26 | 
            +
                  @user_id = args.delete(:user_id) { raise ArgumentError.new(":user_id option must be specified to create request") }
         | 
| 27 | 
            +
                  @request_type = args.fetch(:request_type, "NOT_SPECIFIED")
         | 
| 28 | 
            +
                  validate!(args)
         | 
| 29 | 
            +
                  normalize!(args)
         | 
| 30 | 
            +
                  @body = args
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
             | 
| 34 | 
            +
                def normalize!(args)
         | 
| 35 | 
            +
                  request_type_normalization!(args)
         | 
| 36 | 
            +
                  additional_normalization!(args)
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def request_type_normalization!(args)
         | 
| 40 | 
            +
                  method = "#{@request_type.downcase}_normalization".to_sym
         | 
| 41 | 
            +
                  send(method, args) if respond_to? method
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                # Intended to be overridden by subclasses, allowing extra normalization logic to be provided
         | 
| 45 | 
            +
                def additional_normalization!(args)
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def validate!(args)
         | 
| 49 | 
            +
                  unless REQUEST_TYPES.include?(request_type)
         | 
| 50 | 
            +
                    raise ArgumentError.new(":request_type option must be specified and one of #{REQUEST_TYPES.join(", ")} to submit a request")
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                  request_type_validation!(args)
         | 
| 53 | 
            +
                  additional_validation!(args)
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def request_type_validation!(args)
         | 
| 57 | 
            +
                  method = "#{@request_type.downcase}_validation".to_sym
         | 
| 58 | 
            +
                  send(method, args) if respond_to? method
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                # Intended to be overridden by subclasses, allowing extra validation logic to be provided
         | 
| 62 | 
            +
                def additional_validation!(args)
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                def digitization_normalization(args)
         | 
| 66 | 
            +
                  if args[:target_destination].is_a? String
         | 
| 67 | 
            +
                    args[:target_destination] = { value: args[:target_destination] }
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def digitization_validation(args)
         | 
| 72 | 
            +
                  args.fetch(:target_destination) do
         | 
| 73 | 
            +
                    raise ArgumentError.new(
         | 
| 74 | 
            +
                      ":target_destination option must be specified when request_type is DIGITIZATION"
         | 
| 75 | 
            +
                    )
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                  pd = args.fetch(:partial_digitization) do
         | 
| 78 | 
            +
                    raise ArgumentError.new(
         | 
| 79 | 
            +
                      ":partial_digitization option must be specified when request_type is DIGITIZATION"
         | 
| 80 | 
            +
                    )
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                  if pd == true
         | 
| 83 | 
            +
                    args.fetch(:comment) do
         | 
| 84 | 
            +
                      raise ArgumentError.new(
         | 
| 85 | 
            +
                        ":comment option must be specified when :request_type is DIGITIZATION and :partial_digitization is true"
         | 
| 86 | 
            +
                      )
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                def booking_normalization(args)
         | 
| 92 | 
            +
                  if args[:material_type].is_a? String
         | 
| 93 | 
            +
                    args[:material_type] = { value: args[:material_type] }
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                def booking_validation(args)
         | 
| 98 | 
            +
                  args.fetch(:booking_start_date) do
         | 
| 99 | 
            +
                    raise ArgumentError.new(
         | 
| 100 | 
            +
                      ":booking_start_date option must be specified when request_type is BOOKING"
         | 
| 101 | 
            +
                    )
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
                  args.fetch(:booking_end_date) do
         | 
| 104 | 
            +
                    raise ArgumentError.new(
         | 
| 105 | 
            +
                      ":booking_end_date option must be specified when request_type is BOOKING"
         | 
| 106 | 
            +
                    )
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
                  args.fetch(:pickup_location_type) do
         | 
| 109 | 
            +
                    raise ArgumentError.new(
         | 
| 110 | 
            +
                      ":pickup_location_type option must be specified when request_type is BOOKING"
         | 
| 111 | 
            +
                    )
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
                  args.fetch(:pickup_location_library) do
         | 
| 114 | 
            +
                    raise ArgumentError.new(
         | 
| 115 | 
            +
                      ":pickup_location_library option must be specified when request_type is BOOKING"
         | 
| 116 | 
            +
                    )
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                def hold_normalization(args)
         | 
| 121 | 
            +
                  # if args[:material_type].is_a? String
         | 
| 122 | 
            +
                  #   args[:material_type] = { value: args[:material_type] }
         | 
| 123 | 
            +
                  # end
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                def hold_validation(args)
         | 
| 127 | 
            +
                  args.fetch(:pickup_location_type) do
         | 
| 128 | 
            +
                    raise ArgumentError.new(
         | 
| 129 | 
            +
                      ":pickup_location_type option must be specified when request_type is HOLD"
         | 
| 130 | 
            +
                    )
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
                  args.fetch(:pickup_location_library) do
         | 
| 133 | 
            +
                    raise ArgumentError.new(
         | 
| 134 | 
            +
                      ":pickup_location_library option must be specified when request_type is HOLD"
         | 
| 135 | 
            +
                    )
         | 
| 136 | 
            +
                  end
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
              end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
              class ItemRequest < BibRequest
         | 
| 141 | 
            +
                def self.submit(args)
         | 
| 142 | 
            +
                  request = new(args)
         | 
| 143 | 
            +
                  response = HTTParty.post(
         | 
| 144 | 
            +
                    "#{bibs_base_path}/#{request.mms_id}/holdings/#{request.holding_id}/items/#{request.item_pid}/requests",
         | 
| 145 | 
            +
                    query: { user_id: request.user_id },
         | 
| 146 | 
            +
                    headers: headers,
         | 
| 147 | 
            +
                    body: request.body.to_json
         | 
| 148 | 
            +
                    )
         | 
| 149 | 
            +
                  Alma::Response.new(response)
         | 
| 150 | 
            +
                end
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                attr_reader :holding_id, :item_pid
         | 
| 153 | 
            +
                def initialize(args)
         | 
| 154 | 
            +
                  super(args)
         | 
| 155 | 
            +
                  @holding_id = args.delete(:holding_id) { raise ArgumentError.new(":holding_id option must be specified to create request") }
         | 
| 156 | 
            +
                  @item_pid = args.delete(:item_pid) { raise ArgumentError.new(":item_pid option must be specified to create request") }
         | 
| 157 | 
            +
                end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                def additional_validation!(args)
         | 
| 160 | 
            +
                  args.fetch(:description) do
         | 
| 161 | 
            +
                    raise ArgumentError.new(
         | 
| 162 | 
            +
                      ":description option must be specified when request_type is DIGITIZATION"
         | 
| 163 | 
            +
                    )
         | 
| 164 | 
            +
                  end
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
              end
         | 
| 167 | 
            +
            end
         | 
    
        data/lib/alma/request_options.rb
    CHANGED
    
    | @@ -1,6 +1,12 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Alma
         | 
| 2 4 | 
             
              class RequestOptions
         | 
| 5 | 
            +
                class ResponseError < Alma::StandardError
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 3 8 | 
             
                extend Forwardable
         | 
| 9 | 
            +
                extend Alma::ApiDefaults
         | 
| 4 10 |  | 
| 5 11 | 
             
                attr_accessor :request_options, :raw_response
         | 
| 6 12 | 
             
                def_delegators :raw_response, :response, :request
         | 
| @@ -9,39 +15,52 @@ module Alma | |
| 9 15 |  | 
| 10 16 | 
             
                def initialize(response)
         | 
| 11 17 | 
             
                  @raw_response = response
         | 
| 12 | 
            -
                   | 
| 18 | 
            +
                  validate(response)
         | 
| 19 | 
            +
                  @request_options = response.parsed_response["request_option"]
         | 
| 13 20 | 
             
                end
         | 
| 14 21 |  | 
| 15 | 
            -
             | 
| 22 | 
            +
             | 
| 23 | 
            +
                def self.get(mms_id, options = {})
         | 
| 16 24 | 
             
                  url = "#{bibs_base_path}/#{mms_id}/request-options"
         | 
| 17 | 
            -
                  options.select! {|k,_|  REQUEST_OPTIONS_PERMITTED_ARGS.include? k }
         | 
| 18 | 
            -
                  response = HTTParty.get(url, headers: headers, query: options)
         | 
| 25 | 
            +
                  options.select! { |k, _|  REQUEST_OPTIONS_PERMITTED_ARGS.include? k }
         | 
| 26 | 
            +
                  response = HTTParty.get(url, headers: headers, query: options, timeout: timeout)
         | 
| 19 27 | 
             
                  new(response)
         | 
| 20 28 | 
             
                end
         | 
| 21 29 |  | 
| 22 | 
            -
                def  | 
| 23 | 
            -
                   | 
| 30 | 
            +
                def loggable
         | 
| 31 | 
            +
                  { uri: @raw_response&.request&.uri.to_s
         | 
| 32 | 
            +
                  }.select { |k, v| !(v.nil? || v.empty?) }
         | 
| 24 33 | 
             
                end
         | 
| 25 34 |  | 
| 26 | 
            -
                 | 
| 35 | 
            +
                def validate(response)
         | 
| 36 | 
            +
                  if response.code != 200
         | 
| 37 | 
            +
                    raise ResponseError.new("Could not get request options.", loggable.merge(response.parsed_response))
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 27 40 |  | 
| 28 | 
            -
                def  | 
| 29 | 
            -
                   | 
| 41 | 
            +
                def hold_allowed?
         | 
| 42 | 
            +
                  !request_options.nil? &&
         | 
| 43 | 
            +
                    !request_options.select { |option| option["type"]["value"] == "HOLD" }.empty?
         | 
| 30 44 | 
             
                end
         | 
| 31 45 |  | 
| 32 | 
            -
                def  | 
| 33 | 
            -
                   | 
| 46 | 
            +
                def digitization_allowed?
         | 
| 47 | 
            +
                  !request_options.nil? &&
         | 
| 48 | 
            +
                    !request_options.select { |option| option["type"]["value"] == "DIGITIZATION" }.empty?
         | 
| 34 49 | 
             
                end
         | 
| 35 50 |  | 
| 36 | 
            -
                def  | 
| 37 | 
            -
                   | 
| 38 | 
            -
             | 
| 39 | 
            -
                   "Content-Type": "application/json" }
         | 
| 51 | 
            +
                def booking_allowed?
         | 
| 52 | 
            +
                  !request_options.nil? &&
         | 
| 53 | 
            +
                    !request_options.select { |option| option["type"]["value"] == "BOOKING" }.empty?
         | 
| 40 54 | 
             
                end
         | 
| 41 55 |  | 
| 42 | 
            -
                def  | 
| 43 | 
            -
                   | 
| 56 | 
            +
                def resource_sharing_broker_allowed?
         | 
| 57 | 
            +
                  !request_options.nil? &&
         | 
| 58 | 
            +
                    !request_options.select { |option| option["type"]["value"] == "RS_BROKER" }.empty?
         | 
| 44 59 | 
             
                end
         | 
| 45 60 |  | 
| 61 | 
            +
                def ez_borrow_link
         | 
| 62 | 
            +
                  broker = request_options.select { |option| option["type"]["value"] == "RS_BROKER" }
         | 
| 63 | 
            +
                  broker.collect { |opt| opt["request_url"] }.first
         | 
| 64 | 
            +
                end
         | 
| 46 65 | 
             
              end
         | 
| 47 66 | 
             
            end
         | 
    
        data/lib/alma/request_set.rb
    CHANGED
    
    | @@ -1,29 +1,78 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Alma
         | 
| 2 | 
            -
              class RequestSet
         | 
| 3 | 
            -
                 | 
| 4 | 
            -
                 | 
| 5 | 
            -
             | 
| 4 | 
            +
              class RequestSet < ResultSet
         | 
| 5 | 
            +
                class ResponseError < Alma::StandardError
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                alias :total_records :total_record_count
         | 
| 6 9 |  | 
| 7 | 
            -
                attr_reader : | 
| 8 | 
            -
                def_delegators : | 
| 9 | 
            -
                def_delegators :response, :[], :fetch
         | 
| 10 | 
            +
                attr_reader :results, :raw_response
         | 
| 11 | 
            +
                def_delegators :results, :empty?
         | 
| 10 12 |  | 
| 11 | 
            -
                def initialize( | 
| 12 | 
            -
                  @ | 
| 13 | 
            +
                def initialize(raw_response)
         | 
| 14 | 
            +
                  @raw_response = raw_response
         | 
| 15 | 
            +
                  @response = raw_response.parsed_response
         | 
| 16 | 
            +
                  validate(raw_response)
         | 
| 17 | 
            +
                  @results = @response.fetch(key, [])
         | 
| 18 | 
            +
                               .map { |item| single_record_class.new(item) }
         | 
| 13 19 | 
             
                end
         | 
| 14 20 |  | 
| 15 | 
            -
                def  | 
| 16 | 
            -
                   | 
| 21 | 
            +
                def loggable
         | 
| 22 | 
            +
                  { uri: @raw_response&.request&.uri.to_s
         | 
| 23 | 
            +
                  }.select { |k, v| !(v.nil? || v.empty?) }
         | 
| 17 24 | 
             
                end
         | 
| 18 25 |  | 
| 19 | 
            -
                def  | 
| 20 | 
            -
                   | 
| 26 | 
            +
                def validate(response)
         | 
| 27 | 
            +
                  if response.code != 200
         | 
| 28 | 
            +
                    error = "Could not find requests."
         | 
| 29 | 
            +
                    log = loggable.merge(response.parsed_response)
         | 
| 30 | 
            +
                    raise ResponseError.new(error, log)
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def all
         | 
| 35 | 
            +
                  Enumerator.new do |yielder|
         | 
| 36 | 
            +
                    offset = 0
         | 
| 37 | 
            +
                    loop do
         | 
| 38 | 
            +
                      r = (offset == 0) ? self : single_record_class.where_user(user_id, { limit: 100, offset: offset })
         | 
| 39 | 
            +
                      unless r.empty?
         | 
| 40 | 
            +
                        r.map { |item| yielder << item }
         | 
| 41 | 
            +
                        offset += 100
         | 
| 42 | 
            +
                      else
         | 
| 43 | 
            +
                        raise StopIteration
         | 
| 44 | 
            +
                      end
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def each(&block)
         | 
| 50 | 
            +
                  @results.each(&block)
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def success?
         | 
| 54 | 
            +
                  raw_response.response.code.to_s == "200"
         | 
| 21 55 | 
             
                end
         | 
| 22 | 
            -
                alias :total_records :total_record_count
         | 
| 23 56 |  | 
| 24 57 | 
             
                def key
         | 
| 25 | 
            -
                   | 
| 58 | 
            +
                  "user_request"
         | 
| 26 59 | 
             
                end
         | 
| 27 60 |  | 
| 61 | 
            +
                def single_record_class
         | 
| 62 | 
            +
                  Alma::UserRequest
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                private
         | 
| 66 | 
            +
                  def user_id
         | 
| 67 | 
            +
                    @user_id ||= get_user_id_from_path(raw_response.request.uri.path)
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  def get_user_id_from_path(path)
         | 
| 71 | 
            +
                    # Path in user api calls starts with "/almaws/v1/users/123/maybe_something/else"
         | 
| 72 | 
            +
                    split_path = path.split("/")
         | 
| 73 | 
            +
                    # the part immediately following the "users" is going to be the user_id
         | 
| 74 | 
            +
                    user_id_index = split_path.index("users") + 1
         | 
| 75 | 
            +
                    split_path[user_id_index]
         | 
| 76 | 
            +
                  end
         | 
| 28 77 | 
             
              end
         | 
| 29 78 | 
             
            end
         | 
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "forwardable"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Alma
         | 
| 6 | 
            +
              class Response
         | 
| 7 | 
            +
                class StandardError < Alma::StandardError
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                extend ::Forwardable
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                attr_reader :raw_response
         | 
| 13 | 
            +
                def_delegators :raw_response, :body, :success?, :response, :request
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def initialize(response)
         | 
| 16 | 
            +
                  @raw_response = response
         | 
| 17 | 
            +
                  # We could validate and throw an error here but currently a
         | 
| 18 | 
            +
                  validate(response)
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def loggable
         | 
| 22 | 
            +
                  { uri: @raw_response&.request&.uri.to_s
         | 
| 23 | 
            +
                  }.select { |k, v| !(v.nil? || v.empty?) }
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def validate(response)
         | 
| 27 | 
            +
                  if errors.first&.dig("errorCode") == "401136"
         | 
| 28 | 
            +
                    message = "The requested item already exists."
         | 
| 29 | 
            +
                    log = loggable.merge(response.parsed_response)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    raise Alma::BibRequest::ItemAlreadyExists.new(message, log)
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  if response.code != 200
         | 
| 35 | 
            +
                    log = loggable.merge(response.parsed_response)
         | 
| 36 | 
            +
                    raise StandardError.new("Invalid Response.", log)
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                # Returns an array of errors
         | 
| 41 | 
            +
                def errors
         | 
| 42 | 
            +
                  @raw_response.parsed_response&.dig("errorList", "error") || []
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
            end
         | 
    
        data/lib/alma/result_set.rb
    CHANGED
    
    | @@ -1,50 +1,42 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
              class ResultSet
         | 
| 3 | 
            -
                extend Forwardable
         | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 4 2 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
                include Alma::Error
         | 
| 3 | 
            +
            require "forwardable"
         | 
| 7 4 |  | 
| 8 | 
            -
             | 
| 5 | 
            +
            class Alma::ResultSet
         | 
| 6 | 
            +
              extend ::Forwardable
         | 
| 7 | 
            +
              include Enumerable
         | 
| 8 | 
            +
              include Alma::Error
         | 
| 9 9 |  | 
| 10 | 
            -
             | 
| 11 | 
            -
                  @response = ws_response
         | 
| 12 | 
            -
                end
         | 
| 10 | 
            +
              attr_reader :response
         | 
| 13 11 |  | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
                end
         | 
| 12 | 
            +
              def_delegators :response, :[], :fetch
         | 
| 13 | 
            +
              def_delegators :each, :each_with_index, :size
         | 
| 17 14 |  | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 15 | 
            +
              def initialize(response_body_hash)
         | 
| 16 | 
            +
                @response = response_body_hash
         | 
| 17 | 
            +
              end
         | 
| 21 18 |  | 
| 19 | 
            +
              def loggable
         | 
| 20 | 
            +
                { uri: @response&.request&.uri&.to_s }
         | 
| 21 | 
            +
                  .select { |k, v| !(v.nil? || v.empty?) }
         | 
| 22 | 
            +
              end
         | 
| 22 23 |  | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 24 | 
            +
              def each
         | 
| 25 | 
            +
                @results ||= @response.fetch(key, [])
         | 
| 26 | 
            +
                  .map { |item| single_record_class.new(item) }
         | 
| 27 | 
            +
              end
         | 
| 26 28 |  | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 29 | 
            +
              def total_record_count
         | 
| 30 | 
            +
                fetch("total_record_count", 0).to_i
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
              alias :total_records :total_record_count
         | 
| 30 33 |  | 
| 31 | 
            -
             | 
| 32 | 
            -
                def  | 
| 33 | 
            -
                   | 
| 34 | 
            +
              protected
         | 
| 35 | 
            +
                def key
         | 
| 36 | 
            +
                  raise NotImplementedError "Subclasses of ResultSet need to define a response key"
         | 
| 34 37 | 
             
                end
         | 
| 35 38 |  | 
| 36 | 
            -
                # Subclasses Can override this to use a Custom Class for single record objects.
         | 
| 37 39 | 
             
                def single_record_class
         | 
| 38 40 | 
             
                  Alma::AlmaRecord
         | 
| 39 41 | 
             
                end
         | 
| 40 | 
            -
             | 
| 41 | 
            -
                def list_results
         | 
| 42 | 
            -
                  #If there is only one record in the response, HTTParty returns as a hash, not
         | 
| 43 | 
            -
                  # an array of hashes, so wrap in array to normalize.
         | 
| 44 | 
            -
                  response_array = (response_records.is_a? Array) ? response_records : [response_records]
         | 
| 45 | 
            -
                  response_array.map do |record|
         | 
| 46 | 
            -
                    single_record_class.new(record)
         | 
| 47 | 
            -
                  end
         | 
| 48 | 
            -
                end
         | 
| 49 | 
            -
              end
         | 
| 50 42 | 
             
            end
         |