folio_client 0.20.0 → 0.21.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/Gemfile.lock +18 -21
- data/README.md +4 -3
- data/api_test.rb +2 -4
- data/folio_client.gemspec +1 -0
- data/lib/folio_client/authenticator.rb +6 -11
- data/lib/folio_client/version.rb +1 -1
- data/lib/folio_client.rb +46 -42
- metadata +17 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d8c16843f867a9647afa4265ac90ce5d853e3e4ed4bd543dd209913302e8fd1e
|
|
4
|
+
data.tar.gz: e9e6f447bd7a23b75e2b38ccfbb641d26880ab9c6db815a13f47bae0a6ef6788
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: caa83ad6b9dd3d56312791ca3d5917b86895c456067b8eabacc41e27a283bb6db5dfde2fb7137eb9eb4bf599851e875bdaaf29f32f0157682c27759d1df23845
|
|
7
|
+
data.tar.gz: d5ce53af19a71d1c32d89d3cfe566e0eec8f103655c1514644e639c3ff0200316ea559d2ecc2685309e4a393bf0889b8e3953358e8aaea5748681aec213a4990
|
data/Gemfile.lock
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
folio_client (0.
|
|
4
|
+
folio_client (0.21.0)
|
|
5
5
|
activesupport (>= 4.2)
|
|
6
|
+
deprecation
|
|
6
7
|
dry-monads
|
|
7
8
|
faraday
|
|
8
9
|
faraday-cookie_jar
|
|
@@ -13,7 +14,7 @@ PATH
|
|
|
13
14
|
GEM
|
|
14
15
|
remote: https://rubygems.org/
|
|
15
16
|
specs:
|
|
16
|
-
activesupport (8.1.
|
|
17
|
+
activesupport (8.1.3)
|
|
17
18
|
base64
|
|
18
19
|
bigdecimal
|
|
19
20
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
@@ -26,11 +27,11 @@ GEM
|
|
|
26
27
|
securerandom (>= 0.3)
|
|
27
28
|
tzinfo (~> 2.0, >= 2.0.5)
|
|
28
29
|
uri (>= 0.13.1)
|
|
29
|
-
addressable (2.
|
|
30
|
+
addressable (2.9.0)
|
|
30
31
|
public_suffix (>= 2.0.2, < 8.0)
|
|
31
32
|
ast (2.4.3)
|
|
32
33
|
base64 (0.3.0)
|
|
33
|
-
bigdecimal (4.
|
|
34
|
+
bigdecimal (4.1.1)
|
|
34
35
|
concurrent-ruby (1.3.6)
|
|
35
36
|
connection_pool (3.0.2)
|
|
36
37
|
crack (1.0.1)
|
|
@@ -40,6 +41,8 @@ GEM
|
|
|
40
41
|
debug (1.11.1)
|
|
41
42
|
irb (~> 1.10)
|
|
42
43
|
reline (>= 0.3.8)
|
|
44
|
+
deprecation (1.1.0)
|
|
45
|
+
activesupport
|
|
43
46
|
diff-lcs (1.6.2)
|
|
44
47
|
docile (1.4.1)
|
|
45
48
|
domain_name (0.6.20240107)
|
|
@@ -73,30 +76,25 @@ GEM
|
|
|
73
76
|
prism (>= 1.3.0)
|
|
74
77
|
rdoc (>= 4.0.0)
|
|
75
78
|
reline (>= 0.4.2)
|
|
76
|
-
json (2.19.
|
|
77
|
-
json-schema (6.2.0)
|
|
78
|
-
addressable (~> 2.8)
|
|
79
|
-
bigdecimal (>= 3.1, < 5)
|
|
79
|
+
json (2.19.3)
|
|
80
80
|
language_server-protocol (3.17.0.5)
|
|
81
81
|
lint_roller (1.1.0)
|
|
82
82
|
logger (1.7.0)
|
|
83
83
|
marc (1.4.0)
|
|
84
84
|
nokogiri (~> 1.0)
|
|
85
85
|
rexml
|
|
86
|
-
|
|
87
|
-
json-schema (>= 4.1)
|
|
88
|
-
minitest (6.0.2)
|
|
86
|
+
minitest (6.0.3)
|
|
89
87
|
drb (~> 2.0)
|
|
90
88
|
prism (~> 1.5)
|
|
91
89
|
net-http (0.9.1)
|
|
92
90
|
uri (>= 0.11.1)
|
|
93
|
-
nokogiri (1.19.
|
|
91
|
+
nokogiri (1.19.2-arm64-darwin)
|
|
94
92
|
racc (~> 1.4)
|
|
95
|
-
nokogiri (1.19.
|
|
93
|
+
nokogiri (1.19.2-x86_64-linux-gnu)
|
|
96
94
|
racc (~> 1.4)
|
|
97
95
|
ostruct (0.6.3)
|
|
98
|
-
parallel (1.
|
|
99
|
-
parser (3.3.
|
|
96
|
+
parallel (1.28.0)
|
|
97
|
+
parser (3.3.11.1)
|
|
100
98
|
ast (~> 2.4.1)
|
|
101
99
|
racc
|
|
102
100
|
pp (0.6.3)
|
|
@@ -114,7 +112,7 @@ GEM
|
|
|
114
112
|
erb
|
|
115
113
|
psych (>= 4.0.0)
|
|
116
114
|
tsort
|
|
117
|
-
regexp_parser (2.
|
|
115
|
+
regexp_parser (2.12.0)
|
|
118
116
|
reline (0.6.3)
|
|
119
117
|
io-console (~> 0.5)
|
|
120
118
|
rexml (3.4.4)
|
|
@@ -131,11 +129,10 @@ GEM
|
|
|
131
129
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
132
130
|
rspec-support (~> 3.13.0)
|
|
133
131
|
rspec-support (3.13.7)
|
|
134
|
-
rubocop (1.
|
|
132
|
+
rubocop (1.86.0)
|
|
135
133
|
json (~> 2.3)
|
|
136
134
|
language_server-protocol (~> 3.17.0.2)
|
|
137
135
|
lint_roller (~> 1.1.0)
|
|
138
|
-
mcp (~> 0.6)
|
|
139
136
|
parallel (~> 1.10)
|
|
140
137
|
parser (>= 3.3.0.2)
|
|
141
138
|
rainbow (>= 2.2.2, < 4.0)
|
|
@@ -143,7 +140,7 @@ GEM
|
|
|
143
140
|
rubocop-ast (>= 1.49.0, < 2.0)
|
|
144
141
|
ruby-progressbar (~> 1.7)
|
|
145
142
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
146
|
-
rubocop-ast (1.49.
|
|
143
|
+
rubocop-ast (1.49.1)
|
|
147
144
|
parser (>= 3.3.7.2)
|
|
148
145
|
prism (~> 1.7)
|
|
149
146
|
rubocop-capybara (2.22.1)
|
|
@@ -179,7 +176,7 @@ GEM
|
|
|
179
176
|
unicode-emoji (~> 4.1)
|
|
180
177
|
unicode-emoji (4.2.0)
|
|
181
178
|
uri (1.1.1)
|
|
182
|
-
webmock (3.26.
|
|
179
|
+
webmock (3.26.2)
|
|
183
180
|
addressable (>= 2.8.0)
|
|
184
181
|
crack (>= 0.3.2)
|
|
185
182
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
@@ -206,4 +203,4 @@ DEPENDENCIES
|
|
|
206
203
|
webmock
|
|
207
204
|
|
|
208
205
|
BUNDLED WITH
|
|
209
|
-
4.0.
|
|
206
|
+
4.0.9
|
data/README.md
CHANGED
|
@@ -25,9 +25,10 @@ require 'folio_client'
|
|
|
25
25
|
|
|
26
26
|
# this will configure the client and request an access token
|
|
27
27
|
client = FolioClient.configure(
|
|
28
|
-
url: 'https://
|
|
28
|
+
url: 'https://folio-dev.stanford.edu',
|
|
29
29
|
login_params: { username: 'xxx', password: 'yyy' },
|
|
30
|
-
|
|
30
|
+
tenant_id: 'sul',
|
|
31
|
+
user_agent: 'FolioApiClient'
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
response = client.get('/organizations/organizations', {query_string_param: 'abcdef'})
|
|
@@ -43,7 +44,7 @@ require 'folio_client'
|
|
|
43
44
|
client = FolioClient.configure(
|
|
44
45
|
url: Settings.okapi.url,
|
|
45
46
|
login_params: Settings.okapi.login_params,
|
|
46
|
-
|
|
47
|
+
...
|
|
47
48
|
)
|
|
48
49
|
```
|
|
49
50
|
|
data/api_test.rb
CHANGED
|
@@ -13,10 +13,8 @@ client =
|
|
|
13
13
|
username: ENV.fetch('OKAPI_USER', nil),
|
|
14
14
|
password: ENV.fetch('OKAPI_PASSWORD', nil)
|
|
15
15
|
},
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
'User-Agent': 'folio_client gem (testing)'
|
|
19
|
-
}
|
|
16
|
+
tenant_id: ENV.fetch('OKAPI_TENANT', nil),
|
|
17
|
+
user_agent: 'folio_client gem (testing)'
|
|
20
18
|
)
|
|
21
19
|
|
|
22
20
|
pp(client.fetch_marc_hash(instance_hrid: 'a666'))
|
data/folio_client.gemspec
CHANGED
|
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
|
|
|
32
32
|
spec.require_paths = ['lib']
|
|
33
33
|
|
|
34
34
|
spec.add_dependency 'activesupport', '>= 4.2'
|
|
35
|
+
spec.add_dependency 'deprecation', '>= 0'
|
|
35
36
|
spec.add_dependency 'dry-monads'
|
|
36
37
|
spec.add_dependency 'faraday'
|
|
37
38
|
spec.add_dependency 'faraday-cookie_jar'
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
class FolioClient
|
|
4
4
|
# Fetch a token from the Folio API using login_params
|
|
5
5
|
class Authenticator
|
|
6
|
-
|
|
7
|
-
new.token
|
|
8
|
-
end
|
|
6
|
+
LOGIN_ENDPOINT = '/authn/login-with-expiry'
|
|
9
7
|
|
|
10
8
|
# Request an access_token
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
#
|
|
10
|
+
# @raise [UnauthorizedError] if the response is not successful or if the
|
|
11
|
+
# @return [String] the access token
|
|
12
|
+
def self.refresh_token!
|
|
13
|
+
response = FolioClient.connection.post(LOGIN_ENDPOINT, FolioClient.config.login_params.to_json)
|
|
13
14
|
|
|
14
15
|
UnexpectedResponse.call(response) unless response.success?
|
|
15
16
|
|
|
@@ -23,11 +24,5 @@ class FolioClient
|
|
|
23
24
|
|
|
24
25
|
access_cookie.value
|
|
25
26
|
end
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
def login_endpoint
|
|
30
|
-
'/authn/login-with-expiry'
|
|
31
|
-
end
|
|
32
27
|
end
|
|
33
28
|
end
|
data/lib/folio_client/version.rb
CHANGED
data/lib/folio_client.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'http/cookie' # Workaround for https://github.com/sparklemotion/http-cookie/issues/62
|
|
3
4
|
require 'active_support/core_ext/module/delegation'
|
|
4
5
|
require 'active_support/core_ext/object/blank'
|
|
6
|
+
require 'deprecation'
|
|
5
7
|
require 'faraday'
|
|
6
8
|
require 'faraday-cookie_jar'
|
|
7
9
|
require 'marc'
|
|
@@ -44,47 +46,53 @@ class FolioClient
|
|
|
44
46
|
# Error raised when the Folio API returns a 400 Bad Request
|
|
45
47
|
class BadRequestError < Error; end
|
|
46
48
|
|
|
47
|
-
DEFAULT_HEADERS = {
|
|
48
|
-
accept: 'application/json, text/plain',
|
|
49
|
-
content_type: 'application/json'
|
|
50
|
-
}.freeze
|
|
51
|
-
|
|
52
49
|
class << self
|
|
50
|
+
extend Deprecation
|
|
51
|
+
|
|
52
|
+
Config = Struct.new('Config', :url, :login_params, :timeout, :tenant_id, :user_agent) do
|
|
53
|
+
def headers
|
|
54
|
+
{
|
|
55
|
+
accept: 'application/json, text/plain',
|
|
56
|
+
content_type: 'application/json',
|
|
57
|
+
user_agent: user_agent,
|
|
58
|
+
'X-Okapi-Tenant': tenant_id
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
53
63
|
# @param url [String] the folio API URL
|
|
54
64
|
# @param login_params [Hash] the folio client login params (username:, password:)
|
|
55
|
-
# @param
|
|
65
|
+
# @param tenant_id [String] the ID of the Folio tenant
|
|
66
|
+
# @param user_agent [String] the user agent string to send in API requests
|
|
67
|
+
# @param timeout [Integer] the timeout in seconds for API requests
|
|
68
|
+
# @param unsupported_kwargs [Hash] any additional keyword arguments that are not explicitly supported.
|
|
69
|
+
# This is to allow for backward compatibility with previous versions of the client that accepted
|
|
70
|
+
# additional configuration options, such as `okapi_headers`, without raising an error. The values
|
|
71
|
+
# of any recognized keys in this hash will be used to set the corresponding configuration options,
|
|
72
|
+
# and a deprecation warning will be issued for any keys present in this hash.
|
|
56
73
|
# @return [FolioClient] the configured Singleton class
|
|
57
|
-
def configure(url:, login_params:,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# auto-magic token refreshing. Why not immediately get a valid token? Our apps
|
|
63
|
-
# commonly invoke client `.configure` methods in the initializer in all
|
|
64
|
-
# application environments, even those that are never expected to
|
|
65
|
-
# connect to production APIs, such as local development machines.
|
|
66
|
-
#
|
|
67
|
-
# NOTE: `nil` and blank string cannot be used as dummy values here as
|
|
68
|
-
# they lead to a malformed request to be sent, which triggers an
|
|
69
|
-
# exception not rescued by `with_token_refresh_when_unauthorized`
|
|
70
|
-
token: 'a temporary dummy token to avoid hitting the API before it is needed',
|
|
74
|
+
def configure(url:, login_params:, tenant_id: nil, user_agent: default_user_agent, # rubocop:disable Metrics/ParameterLists
|
|
75
|
+
timeout: default_timeout, **unsupported_kwargs)
|
|
76
|
+
Deprecation.warn(FolioClient, "Deprecated keywords: #{unsupported_kwargs.keys.sort.join(', ')}") if unsupported_kwargs.any?
|
|
77
|
+
|
|
78
|
+
instance.config = Config.new(
|
|
71
79
|
url: url,
|
|
72
80
|
login_params: login_params,
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
timeout: timeout,
|
|
82
|
+
tenant_id: tenant_id.presence || unsupported_kwargs.dig(:okapi_headers, :'X-Okapi-Tenant'),
|
|
83
|
+
user_agent: user_agent.presence || unsupported_kwargs.dig(:okapi_headers, :'User-Agent')
|
|
75
84
|
)
|
|
76
|
-
# rubocop:enable Style/OpenStructUse
|
|
77
85
|
|
|
78
86
|
self
|
|
79
87
|
end
|
|
80
88
|
|
|
81
89
|
delegate :config, :connection, :cookie_jar, :create_holdings, :data_import, :default_timeout,
|
|
82
|
-
:edit_marc_json, :fetch_external_id, :fetch_holdings, :fetch_hrid,
|
|
90
|
+
:default_user_agent, :edit_marc_json, :fetch_external_id, :fetch_holdings, :fetch_hrid,
|
|
83
91
|
:fetch_instance_info, :fetch_location, :fetch_marc_hash, :fetch_marc_xml,
|
|
84
|
-
:force_token_refresh!, :get, :has_instance_status?,
|
|
85
|
-
:
|
|
86
|
-
:
|
|
87
|
-
:
|
|
92
|
+
:force_token_refresh!, :get, :has_instance_status?, :http_get_headers,
|
|
93
|
+
:http_post_and_put_headers, :interface_details, :job_profiles,
|
|
94
|
+
:organization_interfaces, :organizations, :update_holdings, :users, :user_details,
|
|
95
|
+
:post, :put, to: :instance
|
|
88
96
|
end
|
|
89
97
|
|
|
90
98
|
attr_accessor :config
|
|
@@ -95,7 +103,7 @@ class FolioClient
|
|
|
95
103
|
# @return [Hash, nil] the parsed response body or nil
|
|
96
104
|
def get(path, params = {})
|
|
97
105
|
response = with_token_refresh_when_unauthorized do
|
|
98
|
-
connection.get(path, params
|
|
106
|
+
connection.get(path, params)
|
|
99
107
|
end
|
|
100
108
|
|
|
101
109
|
UnexpectedResponse.call(response) unless response.success?
|
|
@@ -111,11 +119,7 @@ class FolioClient
|
|
|
111
119
|
def post(path, body = nil, content_type: 'application/json')
|
|
112
120
|
req_body = content_type == 'application/json' ? body&.to_json : body
|
|
113
121
|
response = with_token_refresh_when_unauthorized do
|
|
114
|
-
|
|
115
|
-
'x-okapi-token': config.token,
|
|
116
|
-
'content-type': content_type
|
|
117
|
-
}
|
|
118
|
-
connection.post(path, req_body, req_headers)
|
|
122
|
+
connection.post(path, req_body, { content_type: content_type })
|
|
119
123
|
end
|
|
120
124
|
|
|
121
125
|
UnexpectedResponse.call(response) unless response.success?
|
|
@@ -131,11 +135,7 @@ class FolioClient
|
|
|
131
135
|
def put(path, body = nil, content_type: 'application/json', **exception_args)
|
|
132
136
|
req_body = content_type == 'application/json' ? body&.to_json : body
|
|
133
137
|
response = with_token_refresh_when_unauthorized do
|
|
134
|
-
|
|
135
|
-
'x-okapi-token': config.token,
|
|
136
|
-
'content-type': content_type
|
|
137
|
-
}
|
|
138
|
-
connection.put(path, req_body, req_headers)
|
|
138
|
+
connection.put(path, req_body, { content_type: content_type })
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
UnexpectedResponse.call(response, **exception_args) unless response.success?
|
|
@@ -147,7 +147,7 @@ class FolioClient
|
|
|
147
147
|
def connection
|
|
148
148
|
@connection ||= Faraday.new(
|
|
149
149
|
url: config.url,
|
|
150
|
-
headers:
|
|
150
|
+
headers: config.headers,
|
|
151
151
|
request: { timeout: config.timeout }
|
|
152
152
|
) do |faraday|
|
|
153
153
|
faraday.use :cookie_jar, jar: cookie_jar
|
|
@@ -288,11 +288,15 @@ class FolioClient
|
|
|
288
288
|
end
|
|
289
289
|
|
|
290
290
|
def default_timeout
|
|
291
|
-
|
|
291
|
+
180
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def default_user_agent
|
|
295
|
+
"folio_client #{FolioClient::VERSION}"
|
|
292
296
|
end
|
|
293
297
|
|
|
294
298
|
def force_token_refresh!
|
|
295
|
-
|
|
299
|
+
Authenticator.refresh_token!
|
|
296
300
|
end
|
|
297
301
|
|
|
298
302
|
private
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: folio_client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.21.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter Mangiafico
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activesupport
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '4.2'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: deprecation
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: dry-monads
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -295,7 +309,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
295
309
|
- !ruby/object:Gem::Version
|
|
296
310
|
version: '0'
|
|
297
311
|
requirements: []
|
|
298
|
-
rubygems_version:
|
|
312
|
+
rubygems_version: 4.0.8
|
|
299
313
|
specification_version: 4
|
|
300
314
|
summary: Interface for interacting with the Folio ILS API.
|
|
301
315
|
test_files: []
|