alma 0.2.8 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,34 +1,76 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Alma
2
- class LoanSet
3
- extend Forwardable
4
- include Enumerable
5
- #include Alma::Error
4
+ class LoanSet < ResultSet
5
+ class ResponseError < Alma::StandardError
6
+ end
7
+
8
+ alias :total_records :total_record_count
9
+
6
10
 
7
- attr_reader :response
8
- def_delegators :response, :[], :fetch
11
+ attr_reader :results, :raw_response
12
+ def_delegators :results, :empty?
9
13
 
10
- def initialize(response_body_hash)
11
- @response = response_body_hash
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
12
23
  end
13
24
 
14
- def each
15
- @response.fetch(key, []).map{|item| Alma::Loan.new(item)}
25
+ def loggable
26
+ { search_args: @search_args,
27
+ uri: @raw_response&.request&.uri.to_s
28
+ }.select { |k, v| !(v.nil? || v.empty?) }
16
29
  end
17
- alias list each
18
30
 
19
- def size
20
- each.count
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
21
37
  end
22
38
 
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
53
+ end
54
+
55
+ def each(&block)
56
+ @results.each(&block)
57
+ end
58
+
59
+ def success?
60
+ raw_response.response.code.to_s == "200"
61
+ end
23
62
 
24
63
  def key
25
- 'item_loan'
64
+ "item_loan"
26
65
  end
27
66
 
28
- def total_record_count
29
- fetch('total_record_count', 0)
67
+ def single_record_class
68
+ Alma::Loan
30
69
  end
31
- alias :total_records :total_record_count
32
70
 
71
+ private
72
+ def user_id
73
+ @user_id ||= results.first.user_id
74
+ end
33
75
  end
34
76
  end
@@ -1,13 +1,17 @@
1
1
  module Alma
2
2
  class RenewalResponse
3
3
 
4
-
5
-
6
4
  def initialize(response)
7
- @response = response
5
+ @raw_response = response
6
+ @response = response.parsed_response
8
7
  @success = response.has_key?('loan_id')
9
8
  end
10
9
 
10
+ def loggable
11
+ { uri: @raw_response&.request&.uri.to_s
12
+ }.select { |k, v| !(v.nil? || v.empty?) }
13
+ end
14
+
11
15
  def renewed?
12
16
  @success
13
17
  end
@@ -0,0 +1,165 @@
1
+ module Alma
2
+ class BibRequest
3
+ class ItemAlreadyExists < Alma::StandardError
4
+ end
5
+
6
+ extend Alma::ApiDefaults
7
+
8
+ REQUEST_TYPES = %w[HOLD DIGITIZATION BOOKING]
9
+
10
+ def self.submit(args)
11
+ request = new(args)
12
+ response = HTTParty.post(
13
+ "#{bibs_base_path}/#{request.mms_id}/requests",
14
+ query: {user_id: request.user_id},
15
+ headers: headers,
16
+ body: request.body.to_json
17
+ )
18
+ Alma::Response.new(response)
19
+ end
20
+
21
+ attr_reader :mms_id, :user_id, :body, :request_type
22
+ def initialize(args)
23
+ @mms_id = args.delete(:mms_id) { raise ArgumentError.new(":mms_id option must be specified to create request") }
24
+ @user_id = args.delete(:user_id) { raise ArgumentError.new(":user_id option must be specified to create request") }
25
+ @request_type = args.fetch(:request_type, "NOT_SPECIFIED")
26
+ validate!(args)
27
+ normalize!(args)
28
+ @body = args
29
+ end
30
+
31
+
32
+ def normalize!(args)
33
+ request_type_normalization!(args)
34
+ additional_normalization!(args)
35
+ end
36
+
37
+ def request_type_normalization!(args)
38
+ method = "#{@request_type.downcase}_normalization".to_sym
39
+ send(method, args) if respond_to? method
40
+ end
41
+
42
+ # Intended to be overridden by subclasses, allowing extra normalization logic to be provided
43
+ def additional_normalization!(args)
44
+ end
45
+
46
+ def validate!(args)
47
+ unless REQUEST_TYPES.include?(request_type)
48
+ raise ArgumentError.new(":request_type option must be specified and one of #{REQUEST_TYPES.join(", ")} to submit a request")
49
+ end
50
+ request_type_validation!(args)
51
+ additional_validation!(args)
52
+ end
53
+
54
+ def request_type_validation!(args)
55
+ method = "#{@request_type.downcase}_validation".to_sym
56
+ send(method, args) if respond_to? method
57
+ end
58
+
59
+ # Intended to be overridden by subclasses, allowing extra validation logic to be provided
60
+ def additional_validation!(args)
61
+ end
62
+
63
+ def digitization_normalization(args)
64
+ if args[:target_destination].is_a? String
65
+ args[:target_destination] = { value: args[:target_destination] }
66
+ end
67
+ end
68
+
69
+ def digitization_validation(args)
70
+ args.fetch(:target_destination) do
71
+ raise ArgumentError.new(
72
+ ":target_destination option must be specified when request_type is DIGITIZATION"
73
+ )
74
+ end
75
+ pd = args.fetch(:partial_digitization) do
76
+ raise ArgumentError.new(
77
+ ":partial_digitization option must be specified when request_type is DIGITIZATION"
78
+ )
79
+ end
80
+ if pd == true
81
+ args.fetch(:comment) do
82
+ raise ArgumentError.new(
83
+ ":comment option must be specified when :request_type is DIGITIZATION and :partial_digitization is true"
84
+ )
85
+ end
86
+ end
87
+ end
88
+
89
+ def booking_normalization(args)
90
+ if args[:material_type].is_a? String
91
+ args[:material_type] = { value: args[:material_type] }
92
+ end
93
+ end
94
+
95
+ def booking_validation(args)
96
+ args.fetch(:booking_start_date) do
97
+ raise ArgumentError.new(
98
+ ":booking_start_date option must be specified when request_type is BOOKING"
99
+ )
100
+ end
101
+ args.fetch(:booking_end_date) do
102
+ raise ArgumentError.new(
103
+ ":booking_end_date option must be specified when request_type is BOOKING"
104
+ )
105
+ end
106
+ args.fetch(:pickup_location_type) do
107
+ raise ArgumentError.new(
108
+ ":pickup_location_type option must be specified when request_type is BOOKING"
109
+ )
110
+ end
111
+ args.fetch(:pickup_location_library) do
112
+ raise ArgumentError.new(
113
+ ":pickup_location_library option must be specified when request_type is BOOKING"
114
+ )
115
+ end
116
+ end
117
+
118
+ def hold_normalization(args)
119
+ # if args[:material_type].is_a? String
120
+ # args[:material_type] = { value: args[:material_type] }
121
+ # end
122
+ end
123
+
124
+ def hold_validation(args)
125
+ args.fetch(:pickup_location_type) do
126
+ raise ArgumentError.new(
127
+ ":pickup_location_type option must be specified when request_type is HOLD"
128
+ )
129
+ end
130
+ args.fetch(:pickup_location_library) do
131
+ raise ArgumentError.new(
132
+ ":pickup_location_library option must be specified when request_type is HOLD"
133
+ )
134
+ end
135
+ end
136
+ end
137
+
138
+ class ItemRequest < BibRequest
139
+ def self.submit(args)
140
+ request = new(args)
141
+ response = HTTParty.post(
142
+ "#{bibs_base_path}/#{request.mms_id}/holdings/#{request.holding_id}/items/#{request.item_pid}/requests",
143
+ query: {user_id: request.user_id},
144
+ headers: headers,
145
+ body: request.body.to_json
146
+ )
147
+ Alma::Response.new(response)
148
+ end
149
+
150
+ attr_reader :holding_id, :item_pid
151
+ def initialize(args)
152
+ super(args)
153
+ @holding_id = args.delete(:holding_id) { raise ArgumentError.new(":holding_id option must be specified to create request") }
154
+ @item_pid = args.delete(:item_pid) { raise ArgumentError.new(":item_pid option must be specified to create request") }
155
+ end
156
+
157
+ def additional_validation!(args)
158
+ args.fetch(:description) do
159
+ raise ArgumentError.new(
160
+ ":description option must be specified when request_type is DIGITIZATION"
161
+ )
162
+ end
163
+ end
164
+ end
165
+ end
@@ -1,6 +1,10 @@
1
1
  module Alma
2
2
  class RequestOptions
3
+ class ResponseError < Alma::StandardError
4
+ end
5
+
3
6
  extend Forwardable
7
+ extend Alma::ApiDefaults
4
8
 
5
9
  attr_accessor :request_options, :raw_response
6
10
  def_delegators :raw_response, :response, :request
@@ -9,43 +13,52 @@ module Alma
9
13
 
10
14
  def initialize(response)
11
15
  @raw_response = response
12
- @request_options = JSON.parse(response.body)["request_option"]
16
+ validate(response)
17
+ @request_options = response.parsed_response["request_option"]
13
18
  end
14
19
 
20
+
15
21
  def self.get(mms_id, options={})
16
22
  url = "#{bibs_base_path}/#{mms_id}/request-options"
17
23
  options.select! {|k,_| REQUEST_OPTIONS_PERMITTED_ARGS.include? k }
18
- response = HTTParty.get(url, headers: headers, query: options)
24
+ response = HTTParty.get(url, headers: headers, query: options, timeout: timeout)
19
25
  new(response)
20
26
  end
21
27
 
22
- def hold_allowed?
23
- !request_options.select {|option| option["type"]["value"] == "HOLD" }.empty?
28
+ def loggable
29
+ { uri: @raw_response&.request&.uri.to_s
30
+ }.select { |k, v| !(v.nil? || v.empty?) }
24
31
  end
25
32
 
26
- def digitization_allowed?
27
- !request_options.select {|option| option["type"]["value"] == "DIGITIZATION" }.empty?
33
+ def validate(response)
34
+ if response.code != 200
35
+ raise ResponseError.new("Could not get request options.", loggable.merge(response.parsed_response))
36
+ end
28
37
  end
29
38
 
30
- private
31
-
32
- def self.region
33
- Alma.configuration.region
39
+ def hold_allowed?
40
+ !request_options.nil? &&
41
+ !request_options.select {|option| option["type"]["value"] == "HOLD" }.empty?
34
42
  end
35
43
 
36
- def self.bibs_base_path
37
- "#{region}/almaws/v1/bibs"
44
+ def digitization_allowed?
45
+ !request_options.nil? &&
46
+ !request_options.select {|option| option["type"]["value"] == "DIGITIZATION" }.empty?
38
47
  end
39
48
 
40
- def self.headers
41
- { "Authorization": "apikey #{apikey}",
42
- "Accept": "application/json",
43
- "Content-Type": "application/json" }
49
+ def booking_allowed?
50
+ !request_options.nil? &&
51
+ !request_options.select {|option| option["type"]["value"] == "BOOKING" }.empty?
44
52
  end
45
53
 
46
- def self.apikey
47
- Alma.configuration.apikey
54
+ def resource_sharing_broker_allowed?
55
+ !request_options.nil? &&
56
+ !request_options.select {|option| option["type"]["value"] == "RS_BROKER" }.empty?
48
57
  end
49
58
 
59
+ def ez_borrow_link
60
+ broker = request_options.select {|option| option["type"]["value"] == "RS_BROKER" }
61
+ broker.collect { |opt| opt["request_url"] }.first
62
+ end
50
63
  end
51
64
  end
@@ -1,33 +1,78 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Alma
2
- class RequestSet
3
- extend Forwardable
4
- include Enumerable
5
- #include Alma::Error
4
+ class RequestSet < ResultSet
5
+ class ResponseError < Alma::StandardError
6
+ end
7
+
8
+ alias :total_records :total_record_count
6
9
 
7
- attr_reader :response
8
- def_delegators :response, :[], :fetch
10
+ attr_reader :results, :raw_response
11
+ def_delegators :results, :empty?
9
12
 
10
- def initialize(response_body_hash)
11
- @response = response_body_hash
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) }
12
19
  end
13
20
 
14
- def each
15
- @response.fetch(key, []).map{|item| Alma::AlmaRecord.new(item)}
21
+ def loggable
22
+ { uri: @raw_response&.request&.uri.to_s
23
+ }.select { |k, v| !(v.nil? || v.empty?) }
16
24
  end
17
- alias list each
18
25
 
19
- def size
20
- each.count
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
21
32
  end
22
33
 
23
- def total_record_count
24
- fetch('total_record_count', 0)
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"
25
55
  end
26
- alias :total_records :total_record_count
27
56
 
28
57
  def key
29
- 'user_request'
58
+ "user_request"
30
59
  end
31
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
32
77
  end
33
78
  end