alma 0.2.8 → 0.3.1
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/.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
|