folio_client 0.21.0 → 1.1.0
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/.rubocop.yml +2 -0
- data/Gemfile.lock +6 -6
- data/README.md +7 -1
- data/lib/folio_client/version.rb +1 -1
- data/lib/folio_client.rb +215 -135
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 38e080519e200ca466c29ce2aff8f3e1d9940a7d6f4ea0b5eb559e6a75b4b44c
|
|
4
|
+
data.tar.gz: a92ff5b5fa997ab8515cab2669914c4c283e323db39bb3c2ae2c3c1ee7a6aac4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 27b02c487f0179288265f738c2ba674b7670e0f1a0859152ffff5a934b10a1bfdf6a1152b235126ce6e60a71584b333d5b19c8f792fc3aa3015bdbb1d810551e
|
|
7
|
+
data.tar.gz: 43b31dccad99d032255cd3376d281e558674baf69f6bb486cc118a621f0453dba652f969e610c931a7ddb0903ce6578c8bf455d37842aceafd91a4659fe1ea69
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
folio_client (
|
|
4
|
+
folio_client (1.1.0)
|
|
5
5
|
activesupport (>= 4.2)
|
|
6
6
|
deprecation
|
|
7
7
|
dry-monads
|
|
@@ -66,7 +66,7 @@ GEM
|
|
|
66
66
|
faraday-net_http (3.4.2)
|
|
67
67
|
net-http (~> 0.5)
|
|
68
68
|
hashdiff (1.2.1)
|
|
69
|
-
http-cookie (1.1.
|
|
69
|
+
http-cookie (1.1.4)
|
|
70
70
|
domain_name (~> 0.5)
|
|
71
71
|
i18n (1.14.8)
|
|
72
72
|
concurrent-ruby (~> 1.0)
|
|
@@ -93,7 +93,7 @@ GEM
|
|
|
93
93
|
nokogiri (1.19.2-x86_64-linux-gnu)
|
|
94
94
|
racc (~> 1.4)
|
|
95
95
|
ostruct (0.6.3)
|
|
96
|
-
parallel (
|
|
96
|
+
parallel (2.0.1)
|
|
97
97
|
parser (3.3.11.1)
|
|
98
98
|
ast (~> 2.4.1)
|
|
99
99
|
racc
|
|
@@ -129,11 +129,11 @@ GEM
|
|
|
129
129
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
130
130
|
rspec-support (~> 3.13.0)
|
|
131
131
|
rspec-support (3.13.7)
|
|
132
|
-
rubocop (1.86.
|
|
132
|
+
rubocop (1.86.1)
|
|
133
133
|
json (~> 2.3)
|
|
134
134
|
language_server-protocol (~> 3.17.0.2)
|
|
135
135
|
lint_roller (~> 1.1.0)
|
|
136
|
-
parallel (
|
|
136
|
+
parallel (>= 1.10)
|
|
137
137
|
parser (>= 3.3.0.2)
|
|
138
138
|
rainbow (>= 2.2.2, < 4.0)
|
|
139
139
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
@@ -203,4 +203,4 @@ DEPENDENCIES
|
|
|
203
203
|
webmock
|
|
204
204
|
|
|
205
205
|
BUNDLED WITH
|
|
206
|
-
4.0.
|
|
206
|
+
4.0.10
|
data/README.md
CHANGED
|
@@ -18,7 +18,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
|
18
18
|
|
|
19
19
|
## Usage
|
|
20
20
|
|
|
21
|
-
The gem should be configured first, and then you can either call API endpoints directly using GET
|
|
21
|
+
The gem should be configured first, and then you can either call API endpoints directly using GET, POST, PUT, and DELETE. It may be more convenient to use the helper methods provided, as described in the section below, if your use case is already covered by what's been implemented already.
|
|
22
22
|
|
|
23
23
|
```ruby
|
|
24
24
|
require 'folio_client'
|
|
@@ -34,6 +34,12 @@ client = FolioClient.configure(
|
|
|
34
34
|
response = client.get('/organizations/organizations', {query_string_param: 'abcdef'})
|
|
35
35
|
|
|
36
36
|
response = client.post('/some/post/endpoint', params_hash.to_json)
|
|
37
|
+
|
|
38
|
+
# If you want direct access to the response object for your own handling, you can also
|
|
39
|
+
# pass a block to the get, post, put, and delete methods:
|
|
40
|
+
response = client.post('/some/post/endpoint', params_hash.to_json) do |resp|
|
|
41
|
+
# Do something with resp.status, resp.headers, resp.body, etc.
|
|
42
|
+
end
|
|
37
43
|
```
|
|
38
44
|
|
|
39
45
|
Note that the settings will live in the consumer of this gem and would typically be used like this:
|
data/lib/folio_client/version.rb
CHANGED
data/lib/folio_client.rb
CHANGED
|
@@ -11,45 +11,47 @@ require 'ostruct'
|
|
|
11
11
|
require 'singleton'
|
|
12
12
|
require 'zeitwerk'
|
|
13
13
|
|
|
14
|
-
#
|
|
14
|
+
# Autoload gem internals.
|
|
15
15
|
Zeitwerk::Loader.for_gem.setup
|
|
16
16
|
|
|
17
|
-
# Client for interacting with the Folio API
|
|
18
|
-
# rubocop:disable Metrics/ClassLength
|
|
19
|
-
class FolioClient
|
|
17
|
+
# Client for interacting with the Folio API.
|
|
18
|
+
class FolioClient # rubocop:disable Metrics/ClassLength
|
|
20
19
|
include Singleton
|
|
21
20
|
|
|
22
|
-
# Base class for all FolioClient errors
|
|
21
|
+
# Base class for all FolioClient errors.
|
|
23
22
|
class Error < StandardError; end
|
|
24
23
|
|
|
25
|
-
#
|
|
24
|
+
# Raised when the Folio Auth API returns 401 Unauthorized.
|
|
26
25
|
class UnauthorizedError < Error; end
|
|
27
26
|
|
|
28
|
-
#
|
|
27
|
+
# Raised when the Folio API returns 404 Not Found, or when 0 results are
|
|
28
|
+
# returned where one was expected.
|
|
29
29
|
class ResourceNotFound < Error; end
|
|
30
30
|
|
|
31
|
-
#
|
|
31
|
+
# Raised when exactly one resource was expected but multiple were returned.
|
|
32
32
|
class MultipleResourcesFound < Error; end
|
|
33
33
|
|
|
34
|
-
#
|
|
34
|
+
# Raised when the Folio API returns 403 Forbidden.
|
|
35
35
|
class ForbiddenError < Error; end
|
|
36
36
|
|
|
37
|
-
#
|
|
37
|
+
# Raised when the Folio API returns 500-level availability errors.
|
|
38
38
|
class ServiceUnavailable < Error; end
|
|
39
39
|
|
|
40
|
-
#
|
|
40
|
+
# Raised when the Folio API returns 422 Unprocessable Entity.
|
|
41
41
|
class ValidationError < Error; end
|
|
42
42
|
|
|
43
|
-
#
|
|
43
|
+
# Raised when the Folio API returns 409 Conflict.
|
|
44
44
|
class ConflictError < Error; end
|
|
45
45
|
|
|
46
|
-
#
|
|
46
|
+
# Raised when the Folio API returns 400 Bad Request.
|
|
47
47
|
class BadRequestError < Error; end
|
|
48
48
|
|
|
49
49
|
class << self
|
|
50
50
|
extend Deprecation
|
|
51
51
|
|
|
52
52
|
Config = Struct.new('Config', :url, :login_params, :timeout, :tenant_id, :user_agent) do
|
|
53
|
+
# Build default headers for Folio-bound requests.
|
|
54
|
+
# @return [Hash<Symbol,String>] default request headers
|
|
53
55
|
def headers
|
|
54
56
|
{
|
|
55
57
|
accept: 'application/json, text/plain',
|
|
@@ -60,47 +62,49 @@ class FolioClient
|
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
64
|
|
|
63
|
-
#
|
|
64
|
-
# @
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
# @
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
# Configure the singleton FolioClient instance.
|
|
66
|
+
# @example
|
|
67
|
+
# FolioClient.configure(
|
|
68
|
+
# url: 'https://folio.example.edu',
|
|
69
|
+
# login_params: { username: 'svc-user', password: 'secret' },
|
|
70
|
+
# tenant_id: 'sul'
|
|
71
|
+
# )
|
|
72
|
+
# @param url [String] Folio API base URL
|
|
73
|
+
# @param login_params [Hash] authentication payload (e.g., +username+, +password+)
|
|
74
|
+
# @param tenant_id [String, nil] Folio tenant identifier
|
|
75
|
+
# @param user_agent [String, nil] user agent string included on outbound requests
|
|
76
|
+
# @param timeout [Integer] request timeout, in seconds
|
|
77
|
+
# @return [Class<FolioClient>] the configured singleton class for chaining
|
|
78
|
+
def configure(url:, login_params:, tenant_id: nil, user_agent: nil, timeout: nil)
|
|
78
79
|
instance.config = Config.new(
|
|
79
80
|
url: url,
|
|
80
81
|
login_params: login_params,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
user_agent: user_agent
|
|
82
|
+
tenant_id: tenant_id,
|
|
83
|
+
timeout: timeout || default_timeout,
|
|
84
|
+
user_agent: user_agent || default_user_agent
|
|
84
85
|
)
|
|
85
86
|
|
|
86
87
|
self
|
|
87
88
|
end
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
# The client is intended to be used as a singleton via {.configure}, but the
|
|
91
|
+
# instance methods are also available on the class itself for convenience.
|
|
92
|
+
# Instead of maintaining a giant list of delegations to the singleton
|
|
93
|
+
# instance, we can just delegate everything. This makes the client easier to
|
|
94
|
+
# extend with additional instance methods without needing to update the
|
|
95
|
+
# delegation list.
|
|
96
|
+
delegate_missing_to :instance
|
|
96
97
|
end
|
|
97
98
|
|
|
99
|
+
# @return [FolioClient::Config, nil] active runtime configuration
|
|
98
100
|
attr_accessor :config
|
|
99
101
|
|
|
100
|
-
# Send an authenticated
|
|
101
|
-
# @param path [String]
|
|
102
|
-
# @param params [Hash]
|
|
103
|
-
# @return [Hash, nil]
|
|
102
|
+
# Send an authenticated GET request.
|
|
103
|
+
# @param path [String] API path relative to configured +url+
|
|
104
|
+
# @param params [Hash] query parameters
|
|
105
|
+
# @return [Hash, Array, nil] parsed JSON body, or +nil+ for empty body
|
|
106
|
+
# @yield [Faraday::Response] optional block to receive the raw +Faraday::Response+ object
|
|
107
|
+
# @raise [FolioClient::Error] when Folio responds with an unexpected status
|
|
104
108
|
def get(path, params = {})
|
|
105
109
|
response = with_token_refresh_when_unauthorized do
|
|
106
110
|
connection.get(path, params)
|
|
@@ -108,14 +112,20 @@ class FolioClient
|
|
|
108
112
|
|
|
109
113
|
UnexpectedResponse.call(response) unless response.success?
|
|
110
114
|
|
|
115
|
+
yield response if block_given?
|
|
116
|
+
|
|
111
117
|
JSON.parse(response.body) if response.body.present?
|
|
112
118
|
end
|
|
113
119
|
|
|
114
|
-
# Send an authenticated
|
|
115
|
-
# If
|
|
116
|
-
#
|
|
117
|
-
# @param
|
|
118
|
-
# @
|
|
120
|
+
# Send an authenticated POST request.
|
|
121
|
+
# If +content_type+ is +application/json+, +body+ is serialized with +to_json+.
|
|
122
|
+
# Otherwise +body+ is sent unchanged.
|
|
123
|
+
# @param path [String] API path relative to configured +url+
|
|
124
|
+
# @param body [Hash, String, nil] request payload
|
|
125
|
+
# @param content_type [String] MIME type of request body
|
|
126
|
+
# @return [Hash, Array, nil] parsed JSON body, or +nil+ for empty body
|
|
127
|
+
# @yield [Faraday::Response] optional block to receive the raw +Faraday::Response+ object
|
|
128
|
+
# @raise [FolioClient::Error] when Folio responds with an unexpected status
|
|
119
129
|
def post(path, body = nil, content_type: 'application/json')
|
|
120
130
|
req_body = content_type == 'application/json' ? body&.to_json : body
|
|
121
131
|
response = with_token_refresh_when_unauthorized do
|
|
@@ -124,14 +134,21 @@ class FolioClient
|
|
|
124
134
|
|
|
125
135
|
UnexpectedResponse.call(response) unless response.success?
|
|
126
136
|
|
|
137
|
+
yield response if block_given?
|
|
138
|
+
|
|
127
139
|
JSON.parse(response.body) if response.body.present?
|
|
128
140
|
end
|
|
129
141
|
|
|
130
|
-
# Send an authenticated
|
|
131
|
-
# If
|
|
132
|
-
#
|
|
133
|
-
# @param
|
|
134
|
-
# @
|
|
142
|
+
# Send an authenticated PUT request.
|
|
143
|
+
# If +content_type+ is +application/json+, +body+ is serialized with +to_json+.
|
|
144
|
+
# Otherwise +body+ is sent unchanged.
|
|
145
|
+
# @param path [String] API path relative to configured +url+
|
|
146
|
+
# @param body [Hash, String, nil] request payload
|
|
147
|
+
# @param content_type [String] MIME type of request body
|
|
148
|
+
# @param exception_args [Hash] supplemental context forwarded to +UnexpectedResponse+
|
|
149
|
+
# @return [Hash, Array, nil] parsed JSON body, or +nil+ for empty body
|
|
150
|
+
# @yield [Faraday::Response] optional block to receive the raw +Faraday::Response+ object
|
|
151
|
+
# @raise [FolioClient::Error] when Folio responds with an unexpected status
|
|
135
152
|
def put(path, body = nil, content_type: 'application/json', **exception_args)
|
|
136
153
|
req_body = content_type == 'application/json' ? body&.to_json : body
|
|
137
154
|
response = with_token_refresh_when_unauthorized do
|
|
@@ -140,10 +157,32 @@ class FolioClient
|
|
|
140
157
|
|
|
141
158
|
UnexpectedResponse.call(response, **exception_args) unless response.success?
|
|
142
159
|
|
|
160
|
+
yield response if block_given?
|
|
161
|
+
|
|
162
|
+
JSON.parse(response.body) if response.body.present?
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Send an authenticated DELETE request
|
|
166
|
+
# @note None of the current FolioClient services use this method, but it's provided
|
|
167
|
+
# primarily to accommodate work in folio-tasks
|
|
168
|
+
# @param path [String] API path relative to configured +url+
|
|
169
|
+
# @return [Hash, Array, nil] parsed JSON body, or +nil+ for empty body
|
|
170
|
+
# @yield [Faraday::Response] optional block to receive the raw +Faraday::Response+ object
|
|
171
|
+
# @raise [FolioClient::Error] when Folio responds with an unexpected status
|
|
172
|
+
def delete(path)
|
|
173
|
+
response = with_token_refresh_when_unauthorized do
|
|
174
|
+
connection.delete(path)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
UnexpectedResponse.call(response) unless response.success?
|
|
178
|
+
|
|
179
|
+
yield response if block_given?
|
|
180
|
+
|
|
143
181
|
JSON.parse(response.body) if response.body.present?
|
|
144
182
|
end
|
|
145
183
|
|
|
146
|
-
#
|
|
184
|
+
# Build (or memoize) the base Faraday connection.
|
|
185
|
+
# @return [Faraday::Connection] configured HTTP connection
|
|
147
186
|
def connection
|
|
148
187
|
@connection ||= Faraday.new(
|
|
149
188
|
url: config.url,
|
|
@@ -155,174 +194,215 @@ class FolioClient
|
|
|
155
194
|
end
|
|
156
195
|
end
|
|
157
196
|
|
|
197
|
+
# Build (or memoize) the cookie jar used by Faraday to store authentication cookies.
|
|
198
|
+
# @return [HTTP::CookieJar] cookie storage for session-aware requests
|
|
158
199
|
def cookie_jar
|
|
159
200
|
@cookie_jar ||= HTTP::CookieJar.new
|
|
160
201
|
end
|
|
161
202
|
|
|
162
|
-
#
|
|
163
|
-
|
|
203
|
+
# Fetch a Folio HRID by instance identifier or query context.
|
|
164
204
|
# @see Inventory#fetch_hrid
|
|
205
|
+
# @return [Object] delegated return value from +Inventory#fetch_hrid+
|
|
165
206
|
def fetch_hrid(...)
|
|
166
|
-
|
|
167
|
-
.new
|
|
168
|
-
.fetch_hrid(...)
|
|
207
|
+
inventory.fetch_hrid(...)
|
|
169
208
|
end
|
|
170
209
|
|
|
210
|
+
# Fetch the Folio external id for a matching record.
|
|
171
211
|
# @see Inventory#fetch_external_id
|
|
212
|
+
# @return [Object] delegated return value from +Inventory#fetch_external_id+
|
|
172
213
|
def fetch_external_id(...)
|
|
173
|
-
|
|
174
|
-
.new
|
|
175
|
-
.fetch_external_id(...)
|
|
214
|
+
inventory.fetch_external_id(...)
|
|
176
215
|
end
|
|
177
216
|
|
|
217
|
+
# Fetch inventory instance details.
|
|
178
218
|
# @see Inventory#fetch_instance_info
|
|
219
|
+
# @return [Object] delegated return value from +Inventory#fetch_instance_info+
|
|
179
220
|
def fetch_instance_info(...)
|
|
180
|
-
|
|
181
|
-
.new
|
|
182
|
-
.fetch_instance_info(...)
|
|
221
|
+
inventory.fetch_instance_info(...)
|
|
183
222
|
end
|
|
184
223
|
|
|
224
|
+
# Fetch location details from inventory.
|
|
185
225
|
# @see Inventory#fetch_location
|
|
226
|
+
# @return [Object] delegated return value from +Inventory#fetch_location+
|
|
186
227
|
def fetch_location(...)
|
|
187
|
-
|
|
188
|
-
.new
|
|
189
|
-
.fetch_location(...)
|
|
228
|
+
inventory.fetch_location(...)
|
|
190
229
|
end
|
|
191
230
|
|
|
231
|
+
# Fetch holdings associated with a record.
|
|
192
232
|
# @see Inventory#fetch_holdings
|
|
233
|
+
# @return [Object] delegated return value from +Inventory#fetch_holdings+
|
|
193
234
|
def fetch_holdings(...)
|
|
194
|
-
|
|
195
|
-
.new
|
|
196
|
-
.fetch_holdings(...)
|
|
235
|
+
inventory.fetch_holdings(...)
|
|
197
236
|
end
|
|
198
237
|
|
|
238
|
+
# Update an existing holdings record.
|
|
199
239
|
# @see Inventory#update_holdings
|
|
240
|
+
# @return [Object] delegated return value from +Inventory#update_holdings+
|
|
200
241
|
def update_holdings(...)
|
|
201
|
-
|
|
202
|
-
.new
|
|
203
|
-
.update_holdings(...)
|
|
242
|
+
inventory.update_holdings(...)
|
|
204
243
|
end
|
|
205
244
|
|
|
245
|
+
# Create a new holdings record.
|
|
206
246
|
# @see Inventory#create_holdings
|
|
247
|
+
# @return [Object] delegated return value from +Inventory#create_holdings+
|
|
207
248
|
def create_holdings(...)
|
|
208
|
-
|
|
209
|
-
.new
|
|
210
|
-
.create_holdings(...)
|
|
249
|
+
inventory.create_holdings(...)
|
|
211
250
|
end
|
|
212
251
|
|
|
252
|
+
# Fetch MARC data as a Ruby hash.
|
|
213
253
|
# @see SourceStorage#fetch_marc_hash
|
|
254
|
+
# @return [Object] delegated return value from +SourceStorage#fetch_marc_hash+
|
|
214
255
|
def fetch_marc_hash(...)
|
|
215
|
-
|
|
216
|
-
.new
|
|
217
|
-
.fetch_marc_hash(...)
|
|
256
|
+
source_storage.fetch_marc_hash(...)
|
|
218
257
|
end
|
|
219
258
|
|
|
259
|
+
# Fetch MARC data as XML.
|
|
220
260
|
# @see SourceStorage#fetch_marc_xml
|
|
261
|
+
# @return [Object] delegated return value from +SourceStorage#fetch_marc_xml+
|
|
221
262
|
def fetch_marc_xml(...)
|
|
222
|
-
|
|
223
|
-
.new
|
|
224
|
-
.fetch_marc_xml(...)
|
|
263
|
+
source_storage.fetch_marc_xml(...)
|
|
225
264
|
end
|
|
226
265
|
|
|
266
|
+
# Determine whether an instance has the requested status.
|
|
227
267
|
# @see Inventory#has_instance_status?
|
|
268
|
+
# @return [Boolean] delegated predicate result
|
|
228
269
|
def has_instance_status?(...) # rubocop:disable Naming/PredicatePrefix
|
|
229
|
-
|
|
230
|
-
.new
|
|
231
|
-
.has_instance_status?(...)
|
|
270
|
+
inventory.has_instance_status?(...)
|
|
232
271
|
end
|
|
233
272
|
|
|
234
|
-
#
|
|
273
|
+
# Run an inventory data import workflow.
|
|
274
|
+
# @see DataImport#import
|
|
275
|
+
# @return [Object] delegated return value from +DataImport#import+
|
|
235
276
|
def data_import(...)
|
|
236
|
-
|
|
237
|
-
.new
|
|
238
|
-
.import(...)
|
|
277
|
+
data_import_service.import(...)
|
|
239
278
|
end
|
|
240
279
|
|
|
241
|
-
#
|
|
280
|
+
# List available data-import job profiles.
|
|
281
|
+
# @see DataImport#job_profiles
|
|
282
|
+
# @return [Object] delegated return value from +DataImport#job_profiles+
|
|
242
283
|
def job_profiles(...)
|
|
243
|
-
|
|
244
|
-
.new
|
|
245
|
-
.job_profiles(...)
|
|
284
|
+
data_import_service.job_profiles(...)
|
|
246
285
|
end
|
|
247
286
|
|
|
287
|
+
# Edit MARC-in-JSON records.
|
|
248
288
|
# @see RecordsEditor#edit_marc_json
|
|
289
|
+
# @return [Object] delegated return value from +RecordsEditor#edit_marc_json+
|
|
249
290
|
def edit_marc_json(...)
|
|
250
|
-
|
|
251
|
-
.new
|
|
252
|
-
.edit_marc_json(...)
|
|
291
|
+
records_editor.edit_marc_json(...)
|
|
253
292
|
end
|
|
254
293
|
|
|
294
|
+
# List organizations.
|
|
255
295
|
# @see Organizations#fetch_list
|
|
296
|
+
# @return [Object] delegated return value from +Organizations#fetch_list+
|
|
256
297
|
def organizations(...)
|
|
257
|
-
|
|
258
|
-
.new
|
|
259
|
-
.fetch_list(...)
|
|
298
|
+
organizations_service.fetch_list(...)
|
|
260
299
|
end
|
|
261
300
|
|
|
301
|
+
# List interfaces for organizations.
|
|
262
302
|
# @see Organizations#fetch_interface_list
|
|
303
|
+
# @return [Object] delegated return value from +Organizations#fetch_interface_list+
|
|
263
304
|
def organization_interfaces(...)
|
|
264
|
-
|
|
265
|
-
.new
|
|
266
|
-
.fetch_interface_list(...)
|
|
305
|
+
organizations_service.fetch_interface_list(...)
|
|
267
306
|
end
|
|
268
307
|
|
|
308
|
+
# Fetch detailed interface information for an organization interface.
|
|
269
309
|
# @see Organizations#fetch_interface_details
|
|
310
|
+
# @return [Object] delegated return value from +Organizations#fetch_interface_details+
|
|
270
311
|
def interface_details(...)
|
|
271
|
-
|
|
272
|
-
.new
|
|
273
|
-
.fetch_interface_details(...)
|
|
312
|
+
organizations_service.fetch_interface_details(...)
|
|
274
313
|
end
|
|
275
314
|
|
|
315
|
+
# List users.
|
|
276
316
|
# @see Users#fetch_list
|
|
317
|
+
# @return [Object] delegated return value from +Users#fetch_list+
|
|
277
318
|
def users(...)
|
|
278
|
-
|
|
279
|
-
.new
|
|
280
|
-
.fetch_list(...)
|
|
319
|
+
users_service.fetch_list(...)
|
|
281
320
|
end
|
|
282
321
|
|
|
322
|
+
# Fetch details for a user.
|
|
283
323
|
# @see Users#fetch_user_details
|
|
324
|
+
# @return [Object] delegated return value from +Users#fetch_user_details+
|
|
284
325
|
def user_details(...)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
326
|
+
users_service.fetch_user_details(...)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Force a refresh of the current auth token.
|
|
330
|
+
#
|
|
331
|
+
# @return [Object] return value from +Authenticator.refresh_token!+
|
|
332
|
+
def force_token_refresh!
|
|
333
|
+
Authenticator.refresh_token!
|
|
288
334
|
end
|
|
289
335
|
|
|
336
|
+
# Default HTTP timeout in seconds.
|
|
337
|
+
#
|
|
338
|
+
# @return [Integer]
|
|
290
339
|
def default_timeout
|
|
291
340
|
180
|
|
292
341
|
end
|
|
293
342
|
|
|
343
|
+
# Default user-agent string used for outbound requests.
|
|
344
|
+
#
|
|
345
|
+
# @return [String]
|
|
294
346
|
def default_user_agent
|
|
295
|
-
"folio_client #{
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
def force_token_refresh!
|
|
299
|
-
Authenticator.refresh_token!
|
|
347
|
+
"folio_client #{VERSION}"
|
|
300
348
|
end
|
|
301
349
|
|
|
302
350
|
private
|
|
303
351
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
#
|
|
308
|
-
#
|
|
309
|
-
#
|
|
310
|
-
#
|
|
311
|
-
#
|
|
312
|
-
#
|
|
313
|
-
#
|
|
314
|
-
#
|
|
315
|
-
# expiry to fall in the middle of that related set of HTTP calls).
|
|
352
|
+
STATUSES_REQUIRING_TOKEN_REFRESH = [401, 403].freeze
|
|
353
|
+
private_constant :STATUSES_REQUIRING_TOKEN_REFRESH
|
|
354
|
+
|
|
355
|
+
# Wrap API operations to refresh and retry when auth has expired.
|
|
356
|
+
# @yieldreturn [Faraday::Response] response from a single HTTP request
|
|
357
|
+
# @return [Faraday::Response] original or retried response
|
|
358
|
+
# @note Wrap one HTTP call per block. If auth fails, the entire block is retried.
|
|
359
|
+
# Only the final response yielded by the block is inspected for auth failure.
|
|
360
|
+
# @note Because this class is a Singleton, its token can outlive many client calls.
|
|
361
|
+
# Expiration can occur between any two invocations, even when those calls are
|
|
362
|
+
# logically related from the caller's perspective.
|
|
316
363
|
def with_token_refresh_when_unauthorized
|
|
317
364
|
response = yield
|
|
318
365
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
366
|
+
return response unless STATUSES_REQUIRING_TOKEN_REFRESH.include?(response.status)
|
|
367
|
+
|
|
368
|
+
force_token_refresh!
|
|
369
|
+
|
|
370
|
+
yield
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Build an inventory service object.
|
|
374
|
+
# @return [Inventory]
|
|
375
|
+
def inventory
|
|
376
|
+
Inventory.new
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Build a source-storage service object.
|
|
380
|
+
# @return [SourceStorage]
|
|
381
|
+
def source_storage
|
|
382
|
+
SourceStorage.new
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Build a data-import service object.
|
|
386
|
+
# @return [DataImport]
|
|
387
|
+
def data_import_service
|
|
388
|
+
DataImport.new
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# Build a records-editor service object.
|
|
392
|
+
# @return [RecordsEditor]
|
|
393
|
+
def records_editor
|
|
394
|
+
RecordsEditor.new
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# Build an organizations service object.
|
|
398
|
+
# @return [Organizations]
|
|
399
|
+
def organizations_service
|
|
400
|
+
Organizations.new
|
|
401
|
+
end
|
|
324
402
|
|
|
325
|
-
|
|
403
|
+
# Build a users service object.
|
|
404
|
+
# @return [Users]
|
|
405
|
+
def users_service
|
|
406
|
+
Users.new
|
|
326
407
|
end
|
|
327
408
|
end
|
|
328
|
-
# rubocop:enable Metrics/ClassLength
|