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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef1f093f7eb2cfcd38d85fc547d702a9768235b9a4e24f061653225e207978a9
4
- data.tar.gz: ffe4c65db99fda6d3cece47f00bb84d490794382342ec2b4be6d4d2ff97a4f1e
3
+ metadata.gz: d8c16843f867a9647afa4265ac90ce5d853e3e4ed4bd543dd209913302e8fd1e
4
+ data.tar.gz: e9e6f447bd7a23b75e2b38ccfbb641d26880ab9c6db815a13f47bae0a6ef6788
5
5
  SHA512:
6
- metadata.gz: 514d18193471517981408f5e1193cc13efa0640e3ad3176030aee72629a3b27b0f6ef9b5f9b36ca5197a9280180276d609a7ca0abddd56d5e9885bc6215f81dd
7
- data.tar.gz: 885859bc1542aa0ceb2d7cf6c939851e2f9774db332f39c2d66b90773d8a6fea6278cf4267a969ece4e492e277deb51ca94337bf02d4395fb00c5a8da3692aa9
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.20.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.2)
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.8.9)
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.0.1)
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.1)
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
- mcp (0.8.0)
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.1-arm64-darwin)
91
+ nokogiri (1.19.2-arm64-darwin)
94
92
  racc (~> 1.4)
95
- nokogiri (1.19.1-x86_64-linux-gnu)
93
+ nokogiri (1.19.2-x86_64-linux-gnu)
96
94
  racc (~> 1.4)
97
95
  ostruct (0.6.3)
98
- parallel (1.27.0)
99
- parser (3.3.10.2)
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.11.3)
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.85.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.0)
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.1)
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.7
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://okapi-dev.stanford.edu',
28
+ url: 'https://folio-dev.stanford.edu',
29
29
  login_params: { username: 'xxx', password: 'yyy' },
30
- okapi_headers: { 'X-Okapi-Tenant': 'sul', 'User-Agent': 'FolioApiClient' }
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
- okapi_headers: Settings.okapi.headers,
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
- okapi_headers: {
17
- 'X-Okapi-Tenant': ENV.fetch('OKAPI_TENANT', nil),
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
- def self.token
7
- new.token
8
- end
6
+ LOGIN_ENDPOINT = '/authn/login-with-expiry'
9
7
 
10
8
  # Request an access_token
11
- def token
12
- response = FolioClient.connection.post(login_endpoint, FolioClient.config.login_params.to_json)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FolioClient
4
- VERSION = '0.20.0'
4
+ VERSION = '0.21.0'
5
5
  end
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 okapi_headers [Hash] the okapi specific headers to add (X-Okapi-Tenant:, User-Agent:)
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:, okapi_headers:, timeout: default_timeout, **)
58
- # rubocop:disable Style/OpenStructUse
59
- instance.config = OpenStruct.new(
60
- # For the initial token, use a dummy value to avoid hitting any APIs
61
- # during configuration, allowing `with_token_refresh_when_unauthorized` to handle
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
- okapi_headers: okapi_headers,
74
- timeout: timeout
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
- :http_get_headers, :http_post_and_put_headers, :interface_details,
86
- :job_profiles, :organization_interfaces, :organizations, :update_holdings, :users,
87
- :user_details, :post, :put, to: :instance
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, { 'x-okapi-token': config.token })
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
- req_headers = {
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
- req_headers = {
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: DEFAULT_HEADERS.merge(config.okapi_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
- 120
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
- config.token = Authenticator.token
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.20.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: 2026-03-13 00:00:00.000000000 Z
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: 3.6.2
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: []