alma 0.2.8 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.ruby-version +1 -1
- data/.travis.yml +10 -2
- data/Guardfile +75 -0
- data/README.md +136 -26
- data/alma.gemspec +9 -4
- data/lib/alma.rb +8 -4
- data/lib/alma/api_defaults.rb +30 -0
- data/lib/alma/availability_response.rb +1 -1
- data/lib/alma/bib.rb +4 -22
- data/lib/alma/bib_item.rb +8 -31
- data/lib/alma/bib_item_set.rb +60 -11
- data/lib/alma/bib_set.rb +7 -21
- data/lib/alma/config.rb +7 -3
- 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 +12 -1
- data/lib/alma/fine.rb +15 -0
- data/lib/alma/fine_set.rb +34 -23
- data/lib/alma/item_request_options.rb +22 -0
- data/lib/alma/loan.rb +18 -0
- data/lib/alma/loan_set.rb +59 -17
- data/lib/alma/renewal_response.rb +7 -3
- data/lib/alma/request.rb +165 -0
- data/lib/alma/request_options.rb +31 -18
- data/lib/alma/request_set.rb +62 -17
- data/lib/alma/response.rb +45 -0
- data/lib/alma/result_set.rb +27 -35
- data/lib/alma/user.rb +65 -57
- data/lib/alma/user_request.rb +17 -0
- data/lib/alma/user_set.rb +5 -6
- data/lib/alma/version.rb +1 -1
- data/log/.gitignore +4 -0
- metadata +75 -8
- data/lib/alma/api.rb +0 -33
data/lib/alma/bib.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Alma
|
2
2
|
class Bib
|
3
|
+
extend Alma::ApiDefaults
|
3
4
|
extend Forwardable
|
4
5
|
|
5
6
|
def self.find(ids, args)
|
@@ -9,8 +10,9 @@ module Alma
|
|
9
10
|
def self.get_bibs(ids, args={})
|
10
11
|
response = HTTParty.get(
|
11
12
|
self.bibs_base_path,
|
12
|
-
query: {mms_id: ids_from_array(ids)},
|
13
|
-
headers: headers
|
13
|
+
query: {mms_id: ids_from_array(ids) }.merge(args),
|
14
|
+
headers: headers,
|
15
|
+
timeout: timeout
|
14
16
|
)
|
15
17
|
|
16
18
|
if response.code == 200
|
@@ -48,33 +50,13 @@ module Alma
|
|
48
50
|
|
49
51
|
private
|
50
52
|
|
51
|
-
def self.bibs_base_path
|
52
|
-
"#{self.region}/almaws/v1/bibs"
|
53
|
-
end
|
54
|
-
|
55
53
|
def bibs_base_path
|
56
54
|
self.class.bibs_base_path
|
57
55
|
end
|
58
56
|
|
59
|
-
def self.headers
|
60
|
-
{ "Authorization": "apikey #{self.apikey}",
|
61
|
-
"Accept": "application/json",
|
62
|
-
"Content-Type": "application/json" }
|
63
|
-
end
|
64
|
-
|
65
|
-
|
66
57
|
def headers
|
67
58
|
self.class.headers
|
68
59
|
end
|
69
|
-
|
70
|
-
|
71
|
-
def self.apikey
|
72
|
-
Alma.configuration.apikey
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.region
|
76
|
-
Alma.configuration.region
|
77
|
-
end
|
78
60
|
|
79
61
|
def self.get_body_from(response)
|
80
62
|
JSON.parse(response.body)
|
data/lib/alma/bib_item.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'alma/bib_item_set'
|
2
2
|
module Alma
|
3
3
|
class BibItem
|
4
|
+
extend Alma::ApiDefaults
|
4
5
|
extend Forwardable
|
5
6
|
|
6
7
|
attr_reader :item
|
@@ -15,8 +16,8 @@ module Alma
|
|
15
16
|
holding_id = options.delete(:holding_id) || "ALL"
|
16
17
|
options.select! {|k,_| PERMITTED_ARGS.include? k }
|
17
18
|
url = "#{bibs_base_path}/#{mms_id}/holdings/#{holding_id}/items"
|
18
|
-
response = HTTParty.get(url, headers: headers, query: options)
|
19
|
-
BibItemSet.new(response)
|
19
|
+
response = HTTParty.get(url, headers: headers, query: options, timeout: timeout)
|
20
|
+
BibItemSet.new(response, options.merge({mms_id: mms_id, holding_id: holding_id}))
|
20
21
|
end
|
21
22
|
|
22
23
|
def initialize(item)
|
@@ -51,7 +52,6 @@ module Alma
|
|
51
52
|
in_temp_location? ? temp_location_name : holding_location_name
|
52
53
|
end
|
53
54
|
|
54
|
-
|
55
55
|
def holding_library
|
56
56
|
item_data.dig("library", "value")
|
57
57
|
end
|
@@ -93,13 +93,11 @@ module Alma
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def call_number
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
holding_data.fetch("call_number","")
|
102
|
-
end
|
96
|
+
if has_temp_call_number?
|
97
|
+
holding_data.fetch("temp_call_number")
|
98
|
+
else
|
99
|
+
holding_data.fetch("call_number","")
|
100
|
+
end
|
103
101
|
end
|
104
102
|
|
105
103
|
def has_alt_call_number?
|
@@ -149,26 +147,5 @@ module Alma
|
|
149
147
|
def public_note
|
150
148
|
item_data.fetch("public_note", "")
|
151
149
|
end
|
152
|
-
|
153
|
-
private
|
154
|
-
|
155
|
-
def self.region
|
156
|
-
Alma.configuration.region
|
157
|
-
end
|
158
|
-
|
159
|
-
def self.bibs_base_path
|
160
|
-
"#{region}/almaws/v1/bibs"
|
161
|
-
end
|
162
|
-
|
163
|
-
def self.headers
|
164
|
-
{ "Authorization": "apikey #{apikey}",
|
165
|
-
"Accept": "application/json",
|
166
|
-
"Content-Type": "application/json" }
|
167
|
-
end
|
168
|
-
|
169
|
-
def self.apikey
|
170
|
-
Alma.configuration.apikey
|
171
|
-
end
|
172
150
|
end
|
173
|
-
|
174
151
|
end
|
data/lib/alma/bib_item_set.rb
CHANGED
@@ -1,23 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Alma
|
2
|
-
class BibItemSet
|
3
|
-
extend Forwardable
|
4
4
|
|
5
|
-
|
6
|
-
# by delegating responsibility for #each to items
|
7
|
-
include Enumerable
|
8
|
-
attr_accessor :items
|
9
|
-
def_delegators :items, :each
|
5
|
+
class BibItemSet < ResultSet
|
10
6
|
|
11
|
-
|
7
|
+
class ResponseError < ::Alma::StandardError
|
8
|
+
end
|
12
9
|
|
10
|
+
attr_accessor :items
|
13
11
|
attr_reader :raw_response, :total_record_count
|
12
|
+
|
13
|
+
def_delegators :items, :[], :[]=, :empty?, :size, :each
|
14
14
|
def_delegators :raw_response, :response, :request
|
15
15
|
|
16
|
-
def initialize(response)
|
16
|
+
def initialize(response, options={})
|
17
17
|
@raw_response = response
|
18
|
-
parsed =
|
18
|
+
parsed = response.parsed_response
|
19
19
|
@total_record_count = parsed["total_record_count"]
|
20
|
-
@
|
20
|
+
@options = options
|
21
|
+
@mms_id = @options.delete(:mms_id)
|
22
|
+
|
23
|
+
validate(response)
|
24
|
+
@items = parsed.fetch(key, []).map { |item| single_record_class.new(item) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def loggable
|
28
|
+
{ total_record_count: @total_record_count,
|
29
|
+
mms_id: @mms_id,
|
30
|
+
uri: @raw_response&.request&.uri.to_s
|
31
|
+
}.select { |k, v| !(v.nil? || v.empty?) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate(response)
|
35
|
+
if response.code != 200
|
36
|
+
log = loggable.merge(response.parsed_response)
|
37
|
+
raise ResponseError.new("Could not get bib items.", log)
|
38
|
+
end
|
21
39
|
end
|
22
40
|
|
23
41
|
def grouped_by_library
|
@@ -29,5 +47,36 @@ module Alma
|
|
29
47
|
clone.items = reject(&:missing_or_lost?)
|
30
48
|
clone
|
31
49
|
end
|
50
|
+
|
51
|
+
def all
|
52
|
+
Enumerator.new do |yielder|
|
53
|
+
offset = 0
|
54
|
+
loop do
|
55
|
+
r = (offset == 0) ? self : single_record_class.find(@mms_id, options=@options.merge({limit: 100, offset: offset}))
|
56
|
+
unless r.empty?
|
57
|
+
r.map { |item| yielder << item }
|
58
|
+
offset += 100
|
59
|
+
else
|
60
|
+
raise StopIteration
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def each(&block)
|
67
|
+
@items.each(&block)
|
68
|
+
end
|
69
|
+
|
70
|
+
def success?
|
71
|
+
raw_response.response.code.to_s == "200"
|
72
|
+
end
|
73
|
+
|
74
|
+
def key
|
75
|
+
"item"
|
76
|
+
end
|
77
|
+
|
78
|
+
def single_record_class
|
79
|
+
Alma::BibItem
|
80
|
+
end
|
32
81
|
end
|
33
82
|
end
|
data/lib/alma/bib_set.rb
CHANGED
@@ -1,31 +1,17 @@
|
|
1
|
-
|
2
|
-
class BibSet
|
3
|
-
|
4
|
-
extend Forwardable
|
5
|
-
include Enumerable
|
6
|
-
#include Alma::Error
|
7
|
-
|
8
|
-
attr_reader :response
|
9
|
-
def_delegators :list, :each, :size
|
10
|
-
def_delegators :response, :[], :fetch
|
11
|
-
|
12
|
-
def initialize(response_body_hash)
|
13
|
-
@response = response_body_hash
|
14
|
-
end
|
1
|
+
# frozen_string_literal: true
|
15
2
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
3
|
+
module Alma
|
4
|
+
class BibSet < ResultSet
|
5
|
+
def key
|
6
|
+
"bib"
|
20
7
|
end
|
21
8
|
|
22
|
-
def
|
23
|
-
|
9
|
+
def single_record_class
|
10
|
+
Alma::Bib
|
24
11
|
end
|
25
12
|
|
26
13
|
def total_record_count
|
27
14
|
size
|
28
15
|
end
|
29
|
-
|
30
16
|
end
|
31
17
|
end
|
data/lib/alma/config.rb
CHANGED
@@ -9,12 +9,16 @@ module Alma
|
|
9
9
|
end
|
10
10
|
|
11
11
|
class Configuration
|
12
|
-
attr_accessor :apikey, :region
|
12
|
+
attr_accessor :apikey, :region, :enable_loggable
|
13
|
+
attr_accessor :timeout, :http_retries, :logger
|
13
14
|
|
14
15
|
def initialize
|
15
16
|
@apikey = "TEST_API_KEY"
|
16
17
|
@region = 'https://api-na.hosted.exlibrisgroup.com'
|
18
|
+
@enable_loggable = false
|
19
|
+
@timeout = 5
|
20
|
+
@http_retries = 3
|
21
|
+
@logger = Logger.new(STDOUT)
|
17
22
|
end
|
18
|
-
|
19
23
|
end
|
20
|
-
end
|
24
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "httparty"
|
3
|
+
require "active_support"
|
4
|
+
require "active_support/core_ext"
|
5
|
+
require "alma/config"
|
6
|
+
|
7
|
+
module Alma
|
8
|
+
# Alma::Electronic APIs wrapper.
|
9
|
+
class Electronic
|
10
|
+
|
11
|
+
class ElectronicError < ArgumentError
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.get(params = {})
|
15
|
+
retries_count = 0
|
16
|
+
response = nil
|
17
|
+
while retries_count < http_retries do
|
18
|
+
begin
|
19
|
+
response = get_api(params)
|
20
|
+
break;
|
21
|
+
|
22
|
+
rescue Net::ReadTimeout
|
23
|
+
retries_count += 1
|
24
|
+
log.error("Retrying http after timeout with : #{params}")
|
25
|
+
no_more_retries_left = retries_count == http_retries
|
26
|
+
|
27
|
+
raise Net::ReadTimeout.new("Failed due to net timeout after #{http_retries}: #{params}") if no_more_retries_left
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
return response
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.get_totals
|
35
|
+
@totals ||= get(limit: "0").data["total_record_count"]
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.log
|
39
|
+
Alma.configuration.logger
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.get_ids
|
43
|
+
total = get_totals()
|
44
|
+
limit = 100
|
45
|
+
offset = 0
|
46
|
+
log.info("Retrieving #{total} collection ids.")
|
47
|
+
groups = Array.new(total / limit + 1, limit)
|
48
|
+
@ids ||= groups.map { |limit|
|
49
|
+
prev_offset = offset
|
50
|
+
offset += limit
|
51
|
+
{ offset: prev_offset, limit: limit }
|
52
|
+
}
|
53
|
+
.map { |params| Thread.new { self.get(params) } }
|
54
|
+
.map(&:value).map(&:data)
|
55
|
+
.map { |data| data["electronic_collection"].map { |coll| coll["id"] } }
|
56
|
+
.flatten.uniq
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.http_retries
|
60
|
+
Alma.configuration.http_retries
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
class ElectronicAPI
|
65
|
+
include ::HTTParty
|
66
|
+
include ::Enumerable
|
67
|
+
extend ::Forwardable
|
68
|
+
|
69
|
+
REQUIRED_PARAMS = []
|
70
|
+
RESOURCE = "/almaws/v1/electronic"
|
71
|
+
|
72
|
+
attr_reader :params, :data
|
73
|
+
def_delegators :@data, :each, :each_pair, :fetch, :values, :keys, :dig,
|
74
|
+
:slice, :except, :to_h, :to_hash, :[], :with_indifferent_access
|
75
|
+
|
76
|
+
def initialize(params = {})
|
77
|
+
@params = params
|
78
|
+
headers = self.class::headers
|
79
|
+
log.info(url: url, query: params)
|
80
|
+
response = self.class::get(url, headers: headers, query: params, timeout: timeout)
|
81
|
+
@data = JSON.parse(response.body) rescue {}
|
82
|
+
end
|
83
|
+
|
84
|
+
def url
|
85
|
+
"#{Alma.configuration.region}#{resource}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def timeout
|
89
|
+
Alma.configuration.timeout
|
90
|
+
end
|
91
|
+
|
92
|
+
def log
|
93
|
+
Alma::Electronic.log
|
94
|
+
end
|
95
|
+
|
96
|
+
def resource
|
97
|
+
@params.inject(self.class::RESOURCE) { |path, param|
|
98
|
+
key = param.first
|
99
|
+
value = param.last
|
100
|
+
|
101
|
+
if key && value
|
102
|
+
path.gsub(/:#{key}/, value.to_s)
|
103
|
+
else
|
104
|
+
path
|
105
|
+
end
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.can_process?(params = {})
|
110
|
+
type = self.to_s.split("::").last.parameterize
|
111
|
+
self::REQUIRED_PARAMS.all? { |param| params.include? param } &&
|
112
|
+
params[:type].blank? || params[:type] == type
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
def self.headers
|
117
|
+
{ "Authorization": "apikey #{apikey}",
|
118
|
+
"Accept": "application/json",
|
119
|
+
"Content-Type": "application/json" }
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.apikey
|
123
|
+
Alma.configuration.apikey
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class Portfolio < ElectronicAPI
|
128
|
+
REQUIRED_PARAMS = [ :collection_id, :service_id, :portfolio_id ]
|
129
|
+
RESOURCE = "/almaws/v1/electronic/e-collections/:collection_id/e-services/:service_id/portfolios/:portfolio_id"
|
130
|
+
end
|
131
|
+
|
132
|
+
class Service < ElectronicAPI
|
133
|
+
REQUIRED_PARAMS = [ :collection_id, :service_id ]
|
134
|
+
RESOURCE = "/almaws/v1/electronic/e-collections/:collection_id/e-services/:service_id"
|
135
|
+
end
|
136
|
+
|
137
|
+
class Services < ElectronicAPI
|
138
|
+
REQUIRED_PARAMS = [ :collection_id, :type ]
|
139
|
+
RESOURCE = "/almaws/v1/electronic/e-collections/:collection_id/e-services"
|
140
|
+
end
|
141
|
+
|
142
|
+
class Collection < ElectronicAPI
|
143
|
+
REQUIRED_PARAMS = [ :collection_id ]
|
144
|
+
RESOURCE = "/almaws/v1/electronic/e-collections/:collection_id"
|
145
|
+
end
|
146
|
+
|
147
|
+
# Catch all Electronic API.
|
148
|
+
# By default returns all collections
|
149
|
+
class Collections < ElectronicAPI
|
150
|
+
REQUIRED_PARAMS = []
|
151
|
+
RESOURCE = "/almaws/v1/electronic/e-collections"
|
152
|
+
|
153
|
+
def self.can_process?(params = {})
|
154
|
+
true
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Order matters because parameters can repeat.
|
159
|
+
REGISTERED_APIs = [Portfolio, Service, Services, Collection, Collections]
|
160
|
+
|
161
|
+
def self.get_api(params)
|
162
|
+
REGISTERED_APIs
|
163
|
+
.find { |m| m.can_process? params }
|
164
|
+
.new(params)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|