alma 0.2.4 → 0.3.2
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 +5 -5
- 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/Gemfile +5 -1
- data/Guardfile +75 -0
- data/README.md +146 -57
- data/Rakefile +3 -1
- data/alma.gemspec +21 -12
- data/lib/alma.rb +34 -54
- data/lib/alma/alma_record.rb +3 -3
- data/lib/alma/api_defaults.rb +39 -0
- data/lib/alma/availability_response.rb +69 -31
- data/lib/alma/bib.rb +54 -29
- data/lib/alma/bib_holding.rb +25 -0
- data/lib/alma/bib_item.rb +164 -0
- data/lib/alma/bib_item_set.rb +93 -0
- data/lib/alma/bib_set.rb +5 -10
- 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.rb +167 -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/error.rb +16 -4
- data/lib/alma/fine.rb +16 -0
- data/lib/alma/fine_set.rb +41 -8
- 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 -4
- data/lib/alma/location.rb +29 -0
- data/lib/alma/location_set.rb +21 -0
- data/lib/alma/renewal_response.rb +25 -14
- data/lib/alma/request.rb +167 -0
- data/lib/alma/request_options.rb +66 -0
- data/lib/alma/request_set.rb +69 -5
- data/lib/alma/response.rb +45 -0
- data/lib/alma/result_set.rb +27 -35
- data/lib/alma/user.rb +142 -86
- data/lib/alma/user_request.rb +19 -0
- data/lib/alma/user_set.rb +5 -6
- data/lib/alma/version.rb +3 -1
- data/log/.gitignore +4 -0
- metadata +149 -10
- data/.travis.yml +0 -5
- data/lib/alma/api.rb +0 -33
data/lib/alma/request_set.rb
CHANGED
@@ -1,14 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Alma
|
2
4
|
class RequestSet < ResultSet
|
5
|
+
class ResponseError < Alma::StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
alias :total_records :total_record_count
|
9
|
+
|
10
|
+
attr_reader :results, :raw_response
|
11
|
+
def_delegators :results, :empty?
|
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) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def loggable
|
22
|
+
{ uri: @raw_response&.request&.uri.to_s
|
23
|
+
}.select { |k, v| !(v.nil? || v.empty?) }
|
24
|
+
end
|
3
25
|
|
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"
|
55
|
+
end
|
4
56
|
|
5
|
-
def
|
6
|
-
|
57
|
+
def key
|
58
|
+
"user_request"
|
7
59
|
end
|
8
60
|
|
9
|
-
def
|
10
|
-
|
61
|
+
def single_record_class
|
62
|
+
Alma::UserRequest
|
11
63
|
end
|
12
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
|
13
77
|
end
|
14
|
-
end
|
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
|
data/lib/alma/user.rb
CHANGED
@@ -1,34 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Alma
|
2
|
-
class User
|
3
|
-
|
4
|
+
class User
|
5
|
+
class ResponseError < Alma::StandardError
|
6
|
+
end
|
7
|
+
extend Forwardable
|
8
|
+
extend Alma::ApiDefaults
|
4
9
|
|
5
|
-
|
10
|
+
def self.find(user_id, args = {})
|
11
|
+
args[:expand] ||= "fees,requests,loans"
|
12
|
+
response = HTTParty.get("#{self.users_base_path}/#{user_id}", query: args, headers: headers, timeout: timeout)
|
6
13
|
|
7
|
-
|
8
|
-
@id = response['primary_id'].to_s
|
9
|
-
@recheck_loans = true
|
14
|
+
Alma::User.new response
|
10
15
|
end
|
11
16
|
|
12
|
-
|
13
|
-
|
17
|
+
# Authenticates a Alma user with their Alma Password
|
18
|
+
# @param [Hash] args
|
19
|
+
# @option args [String] :user_id The unique id of the user
|
20
|
+
# @option args [String] :password The users local alma password
|
21
|
+
# @return [Boolean] Whether or not the user Successfully authenticated
|
22
|
+
def self.authenticate(args)
|
23
|
+
user_id = args.delete(:user_id) { raise ArgumentError }
|
24
|
+
args.merge!({ op: "auth" })
|
25
|
+
response = HTTParty.post("#{users_base_path}/#{user_id}", query: args, headers: headers, timeout: timeout)
|
26
|
+
response.code == 204
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# The User object can respond directly to Hash like access of attributes
|
31
|
+
def_delegators :response, :[], :[]=, :has_key?, :keys, :to_json
|
32
|
+
|
33
|
+
def initialize(response)
|
34
|
+
@raw_response = response
|
35
|
+
@response = response.parsed_response
|
36
|
+
validate(response)
|
14
37
|
end
|
15
38
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
39
|
+
def loggable
|
40
|
+
{ uri: @raw_response&.request&.uri.to_s
|
41
|
+
}.select { |k, v| !(v.nil? || v.empty?) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate(response)
|
45
|
+
if response.code != 200
|
46
|
+
log = loggable.merge(response.parsed_response)
|
47
|
+
error = "The user was not found."
|
48
|
+
raise ResponseError.new(error, log)
|
20
49
|
end
|
21
|
-
|
50
|
+
end
|
51
|
+
|
52
|
+
def response
|
53
|
+
@response
|
54
|
+
end
|
55
|
+
|
56
|
+
def id
|
57
|
+
self["primary_id"]
|
58
|
+
end
|
59
|
+
|
60
|
+
def total_fines
|
61
|
+
response.dig("fees", "value") || "0"
|
62
|
+
end
|
63
|
+
|
64
|
+
def total_requests
|
65
|
+
response.dig("requests", "value") || "0"
|
66
|
+
end
|
67
|
+
|
68
|
+
def total_loans
|
69
|
+
response.dig("loans", "value") || "0"
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# Access the top level JSON attributes as object methods
|
74
|
+
def method_missing(name)
|
75
|
+
return response[name.to_s] if has_key?(name.to_s)
|
76
|
+
super.method_missing name
|
77
|
+
end
|
78
|
+
|
79
|
+
def respond_to_missing?(name, include_private = false)
|
80
|
+
has_key?(name.to_s) || super
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# Persist the user in it's current state back to Alma
|
85
|
+
def save!
|
86
|
+
response = HTTParty.put("#{users_base_path}/#{id}", timeout: timeout, headers: headers, body: to_json)
|
87
|
+
get_body_from(response)
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def fines
|
92
|
+
Alma::Fine.where_user(id)
|
93
|
+
end
|
94
|
+
|
95
|
+
def requests
|
96
|
+
Alma::UserRequest.where_user(id)
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def loans(args = {})
|
101
|
+
@loans ||= Alma::Loan.where_user(id, args)
|
22
102
|
end
|
23
103
|
|
24
104
|
def renew_loan(loan_id)
|
25
|
-
response = self.class.
|
105
|
+
response = self.class.send_loan_renewal_request({ user_id: id, loan_id: loan_id })
|
26
106
|
if response.renewed?
|
27
107
|
@recheck_loans ||= true
|
28
108
|
end
|
29
|
-
response
|
30
109
|
end
|
31
110
|
|
111
|
+
|
32
112
|
def renew_multiple_loans(loan_ids)
|
33
113
|
loan_ids.map { |id| renew_loan(id) }
|
34
114
|
end
|
@@ -37,68 +117,40 @@ module Alma
|
|
37
117
|
renew_multiple_loans(loans.map(&:loan_id))
|
38
118
|
end
|
39
119
|
|
40
|
-
def
|
41
|
-
|
120
|
+
def preferred_email
|
121
|
+
self["contact_info"]["email"].select { |k, v| k["preferred"] }.first["email_address"]
|
42
122
|
end
|
43
123
|
|
44
|
-
def
|
45
|
-
self.
|
124
|
+
def email
|
125
|
+
self["contact_info"]["email"].map { |e| e["email_address"] }
|
46
126
|
end
|
47
127
|
|
48
|
-
|
49
|
-
|
128
|
+
def preferred_first_name
|
129
|
+
pref_first = self["pref_first_name"] unless self["pref_first_name"] == ""
|
130
|
+
pref_first || self["first_name"] || ""
|
131
|
+
end
|
50
132
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
133
|
+
def preferred_middle_name
|
134
|
+
pref_middle = self["pref_middle_name"] unless self["pref_middle_name"] == ""
|
135
|
+
pref_middle || self["middle_name"] || ""
|
136
|
+
end
|
55
137
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
end
|
138
|
+
def preferred_last_name
|
139
|
+
pref_last = self["pref_last_name"] unless self["pref_last_name"] == ""
|
140
|
+
pref_last || self["last_name"]
|
141
|
+
end
|
61
142
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
User.new(response['user'])
|
66
|
-
end
|
143
|
+
def preferred_suffix
|
144
|
+
self["pref_name_suffix"] || ""
|
145
|
+
end
|
67
146
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
#TODO Handle looping through all results
|
72
|
-
params = query_merge args
|
73
|
-
response = resources.almaws_v1_users.user_id_fees.get(params)
|
74
|
-
Alma::FineSet.new(response)
|
75
|
-
end
|
147
|
+
def preferred_name
|
148
|
+
"#{preferred_first_name} #{preferred_middle_name} #{preferred_last_name} #{preferred_suffix}"
|
149
|
+
end
|
76
150
|
|
77
|
-
def get_loans(args)
|
78
|
-
#TODO Handle Additional Parameters
|
79
|
-
#TODO Handle Pagination
|
80
|
-
#TODO Handle looping through all results
|
81
|
-
params = query_merge args
|
82
|
-
response = resources.almaws_v1_users.user_id_loans.get(params)
|
83
|
-
Alma::LoanSet.new(response)
|
84
|
-
end
|
85
151
|
|
86
|
-
def get_requests(args)
|
87
|
-
#TODO Handle Additional Parameters
|
88
|
-
#TODO Handle Pagination
|
89
|
-
#TODO Handle looping through all results
|
90
|
-
params = query_merge args
|
91
|
-
response = resources.almaws_v1_users.user_id_requests.get(params)
|
92
|
-
Alma::RequestSet.new(response)
|
93
|
-
end
|
94
152
|
|
95
|
-
|
96
|
-
# Authenticates a Alma user with their Alma Password
|
97
|
-
args.merge!({op: 'auth'})
|
98
|
-
params = query_merge args
|
99
|
-
response = resources.almaws_v1_users.user_id.post(params)
|
100
|
-
response.code == 204
|
101
|
-
end
|
153
|
+
private
|
102
154
|
|
103
155
|
# Attempts to renew a single item for a user
|
104
156
|
# @param [Hash] args
|
@@ -106,36 +158,40 @@ module Alma
|
|
106
158
|
# @option args [String] :loan_id The unique id of the loan
|
107
159
|
# @option args [String] :user_id_type Type of identifier being used to search. OPTIONAL
|
108
160
|
# @return [RenewalResponse] Object indicating the renewal message
|
109
|
-
def
|
110
|
-
args.
|
111
|
-
|
112
|
-
|
161
|
+
def self.send_loan_renewal_request(args)
|
162
|
+
loan_id = args.delete(:loan_id) { raise ArgumentError }
|
163
|
+
user_id = args.delete(:user_id) { raise ArgumentError }
|
164
|
+
params = { op: "renew" }
|
165
|
+
response = HTTParty.post("#{users_base_path}/#{user_id}/loans/#{loan_id}", query: params, headers: headers)
|
113
166
|
RenewalResponse.new(response)
|
114
167
|
end
|
115
168
|
|
116
|
-
# Attempts to renew
|
169
|
+
# Attempts to renew multiple items for a user
|
117
170
|
# @param [Hash] args
|
118
171
|
# @option args [String] :user_id The unique id of the user
|
119
|
-
# @option args [Array<String>] :loan_ids
|
172
|
+
# @option args [Array<String>] :loan_ids The unique ids of the loans
|
120
173
|
# @option args [String] :user_id_type Type of identifier being used to search. OPTIONAL
|
121
|
-
# @return [Array<RenewalResponse>]
|
122
|
-
def
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
[]
|
130
|
-
end
|
174
|
+
# @return [Array<RenewalResponse>] Array of Objects indicating the renewal messages
|
175
|
+
def self.send_multiple_loan_renewal_requests(args)
|
176
|
+
loan_ids = args.delete(:loan_ids) { raise ArgumentError }
|
177
|
+
loan_ids.map { |id| Alma::User.send_loan_renewal_request(args.merge(loan_id: id)) }
|
178
|
+
end
|
179
|
+
|
180
|
+
def get_body_from(response)
|
181
|
+
JSON.parse(response.body)
|
131
182
|
end
|
132
183
|
|
133
184
|
|
185
|
+
def self.users_base_path
|
186
|
+
"https://api-na.hosted.exlibrisgroup.com/almaws/v1/users"
|
187
|
+
end
|
134
188
|
|
189
|
+
def users_base_path
|
190
|
+
self.class.users_base_path
|
191
|
+
end
|
135
192
|
|
136
|
-
def
|
137
|
-
|
193
|
+
def headers
|
194
|
+
self.class.headers
|
138
195
|
end
|
139
|
-
end
|
140
196
|
end
|
141
|
-
end
|
197
|
+
end
|