folio_client 0.19.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/.rubocop.yml +50 -2
- data/Gemfile +1 -1
- data/Gemfile.lock +49 -28
- data/README.md +84 -3
- data/api_test.rb +2 -4
- data/folio_client.gemspec +3 -1
- data/lib/folio_client/authenticator.rb +11 -12
- data/lib/folio_client/inventory.rb +48 -0
- data/lib/folio_client/job_status.rb +4 -0
- data/lib/folio_client/source_storage.rb +2 -6
- data/lib/folio_client/unexpected_response.rb +7 -7
- data/lib/folio_client/version.rb +1 -1
- data/lib/folio_client.rb +86 -58
- metadata +32 -4
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/.rubocop.yml
CHANGED
|
@@ -6,7 +6,7 @@ plugins:
|
|
|
6
6
|
- rubocop-rspec
|
|
7
7
|
|
|
8
8
|
AllCops:
|
|
9
|
-
TargetRubyVersion: 3.
|
|
9
|
+
TargetRubyVersion: 3.4
|
|
10
10
|
DisplayCopNames: true
|
|
11
11
|
SuggestExtensions: false
|
|
12
12
|
Exclude:
|
|
@@ -442,4 +442,52 @@ RSpec/IncludeExamples: # new in 3.6
|
|
|
442
442
|
Capybara/FindAllFirst: # new in 2.22
|
|
443
443
|
Enabled: true
|
|
444
444
|
Capybara/NegationMatcherAfterVisit: # new in 2.22
|
|
445
|
-
Enabled: true
|
|
445
|
+
Enabled: true
|
|
446
|
+
Gemspec/AttributeAssignment: # new in 1.77
|
|
447
|
+
Enabled: true
|
|
448
|
+
Layout/EmptyLinesAfterModuleInclusion: # new in 1.79
|
|
449
|
+
Enabled: true
|
|
450
|
+
Style/ArrayIntersectWithSingleElement: # new in 1.81
|
|
451
|
+
Enabled: true
|
|
452
|
+
Style/CollectionQuerying: # new in 1.77
|
|
453
|
+
Enabled: true
|
|
454
|
+
Style/EmptyClassDefinition: # new in 1.84
|
|
455
|
+
Enabled: true
|
|
456
|
+
Style/ModuleMemberExistenceCheck: # new in 1.82
|
|
457
|
+
Enabled: true
|
|
458
|
+
Style/NegativeArrayIndex: # new in 1.84
|
|
459
|
+
Enabled: true
|
|
460
|
+
Style/ReverseFind: # new in 1.84
|
|
461
|
+
Enabled: true
|
|
462
|
+
RSpecRails/HttpStatusNameConsistency: # new in 2.32
|
|
463
|
+
Enabled: true
|
|
464
|
+
RSpec/LeakyLocalVariable: # new in 3.8
|
|
465
|
+
Enabled: true
|
|
466
|
+
RSpec/Output: # new in 3.9
|
|
467
|
+
Enabled: true
|
|
468
|
+
Lint/DataDefineOverride: # new in 1.85
|
|
469
|
+
Enabled: true
|
|
470
|
+
Lint/UnreachablePatternBranch: # new in 1.85
|
|
471
|
+
Enabled: true
|
|
472
|
+
Style/FileOpen: # new in 1.85
|
|
473
|
+
Enabled: true
|
|
474
|
+
Style/MapJoin: # new in 1.85
|
|
475
|
+
Enabled: true
|
|
476
|
+
Style/OneClassPerFile: # new in 1.85
|
|
477
|
+
Enabled: true
|
|
478
|
+
Style/PartitionInsteadOfDoubleSelect: # new in 1.85
|
|
479
|
+
Enabled: true
|
|
480
|
+
Style/PredicateWithKind: # new in 1.85
|
|
481
|
+
Enabled: true
|
|
482
|
+
Style/ReduceToHash: # new in 1.85
|
|
483
|
+
Enabled: true
|
|
484
|
+
Style/RedundantMinMaxBy: # new in 1.85
|
|
485
|
+
Enabled: true
|
|
486
|
+
Style/RedundantStructKeywordInit: # new in 1.85
|
|
487
|
+
Enabled: true
|
|
488
|
+
Style/SelectByKind: # new in 1.85
|
|
489
|
+
Enabled: true
|
|
490
|
+
Style/SelectByRange: # new in 1.85
|
|
491
|
+
Enabled: true
|
|
492
|
+
Style/TallyMethod: # new in 1.85
|
|
493
|
+
Enabled: true
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
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
|
|
9
10
|
marc
|
|
11
|
+
ostruct
|
|
10
12
|
zeitwerk
|
|
11
13
|
|
|
12
14
|
GEM
|
|
13
15
|
remote: https://rubygems.org/
|
|
14
16
|
specs:
|
|
15
|
-
activesupport (8.1.
|
|
17
|
+
activesupport (8.1.3)
|
|
16
18
|
base64
|
|
17
19
|
bigdecimal
|
|
18
20
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
@@ -25,18 +27,22 @@ GEM
|
|
|
25
27
|
securerandom (>= 0.3)
|
|
26
28
|
tzinfo (~> 2.0, >= 2.0.5)
|
|
27
29
|
uri (>= 0.13.1)
|
|
28
|
-
addressable (2.
|
|
30
|
+
addressable (2.9.0)
|
|
29
31
|
public_suffix (>= 2.0.2, < 8.0)
|
|
30
32
|
ast (2.4.3)
|
|
31
33
|
base64 (0.3.0)
|
|
32
|
-
bigdecimal (4.
|
|
33
|
-
byebug (13.0.0)
|
|
34
|
-
reline (>= 0.6.0)
|
|
34
|
+
bigdecimal (4.1.1)
|
|
35
35
|
concurrent-ruby (1.3.6)
|
|
36
36
|
connection_pool (3.0.2)
|
|
37
37
|
crack (1.0.1)
|
|
38
38
|
bigdecimal
|
|
39
39
|
rexml
|
|
40
|
+
date (3.5.1)
|
|
41
|
+
debug (1.11.1)
|
|
42
|
+
irb (~> 1.10)
|
|
43
|
+
reline (>= 0.3.8)
|
|
44
|
+
deprecation (1.1.0)
|
|
45
|
+
activesupport
|
|
40
46
|
diff-lcs (1.6.2)
|
|
41
47
|
docile (1.4.1)
|
|
42
48
|
domain_name (0.6.20240107)
|
|
@@ -49,7 +55,8 @@ GEM
|
|
|
49
55
|
concurrent-ruby (~> 1.0)
|
|
50
56
|
dry-core (~> 1.1)
|
|
51
57
|
zeitwerk (~> 2.6)
|
|
52
|
-
|
|
58
|
+
erb (6.0.2)
|
|
59
|
+
faraday (2.14.1)
|
|
53
60
|
faraday-net_http (>= 2.0, < 3.5)
|
|
54
61
|
json
|
|
55
62
|
logger
|
|
@@ -64,33 +71,48 @@ GEM
|
|
|
64
71
|
i18n (1.14.8)
|
|
65
72
|
concurrent-ruby (~> 1.0)
|
|
66
73
|
io-console (0.8.2)
|
|
67
|
-
|
|
74
|
+
irb (1.17.0)
|
|
75
|
+
pp (>= 0.6.0)
|
|
76
|
+
prism (>= 1.3.0)
|
|
77
|
+
rdoc (>= 4.0.0)
|
|
78
|
+
reline (>= 0.4.2)
|
|
79
|
+
json (2.19.3)
|
|
68
80
|
language_server-protocol (3.17.0.5)
|
|
69
81
|
lint_roller (1.1.0)
|
|
70
82
|
logger (1.7.0)
|
|
71
83
|
marc (1.4.0)
|
|
72
84
|
nokogiri (~> 1.0)
|
|
73
85
|
rexml
|
|
74
|
-
minitest (6.0.
|
|
86
|
+
minitest (6.0.3)
|
|
87
|
+
drb (~> 2.0)
|
|
75
88
|
prism (~> 1.5)
|
|
76
89
|
net-http (0.9.1)
|
|
77
90
|
uri (>= 0.11.1)
|
|
78
|
-
nokogiri (1.19.
|
|
91
|
+
nokogiri (1.19.2-arm64-darwin)
|
|
79
92
|
racc (~> 1.4)
|
|
80
|
-
nokogiri (1.19.
|
|
93
|
+
nokogiri (1.19.2-x86_64-linux-gnu)
|
|
81
94
|
racc (~> 1.4)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
parser (3.3.10.1)
|
|
95
|
+
ostruct (0.6.3)
|
|
96
|
+
parallel (1.28.0)
|
|
97
|
+
parser (3.3.11.1)
|
|
86
98
|
ast (~> 2.4.1)
|
|
87
99
|
racc
|
|
100
|
+
pp (0.6.3)
|
|
101
|
+
prettyprint
|
|
102
|
+
prettyprint (0.2.0)
|
|
88
103
|
prism (1.9.0)
|
|
89
|
-
|
|
104
|
+
psych (5.3.1)
|
|
105
|
+
date
|
|
106
|
+
stringio
|
|
107
|
+
public_suffix (7.0.5)
|
|
90
108
|
racc (1.8.1)
|
|
91
109
|
rainbow (3.1.1)
|
|
92
110
|
rake (13.3.1)
|
|
93
|
-
|
|
111
|
+
rdoc (7.2.0)
|
|
112
|
+
erb
|
|
113
|
+
psych (>= 4.0.0)
|
|
114
|
+
tsort
|
|
115
|
+
regexp_parser (2.12.0)
|
|
94
116
|
reline (0.6.3)
|
|
95
117
|
io-console (~> 0.5)
|
|
96
118
|
rexml (3.4.4)
|
|
@@ -103,11 +125,11 @@ GEM
|
|
|
103
125
|
rspec-expectations (3.13.5)
|
|
104
126
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
105
127
|
rspec-support (~> 3.13.0)
|
|
106
|
-
rspec-mocks (3.13.
|
|
128
|
+
rspec-mocks (3.13.8)
|
|
107
129
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
108
130
|
rspec-support (~> 3.13.0)
|
|
109
131
|
rspec-support (3.13.7)
|
|
110
|
-
rubocop (1.
|
|
132
|
+
rubocop (1.86.0)
|
|
111
133
|
json (~> 2.3)
|
|
112
134
|
language_server-protocol (~> 3.17.0.2)
|
|
113
135
|
lint_roller (~> 1.1.0)
|
|
@@ -118,7 +140,7 @@ GEM
|
|
|
118
140
|
rubocop-ast (>= 1.49.0, < 2.0)
|
|
119
141
|
ruby-progressbar (~> 1.7)
|
|
120
142
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
121
|
-
rubocop-ast (1.49.
|
|
143
|
+
rubocop-ast (1.49.1)
|
|
122
144
|
parser (>= 3.3.7.2)
|
|
123
145
|
prism (~> 1.7)
|
|
124
146
|
rubocop-capybara (2.22.1)
|
|
@@ -146,29 +168,28 @@ GEM
|
|
|
146
168
|
simplecov_json_formatter (~> 0.1)
|
|
147
169
|
simplecov-html (0.13.2)
|
|
148
170
|
simplecov_json_formatter (0.1.4)
|
|
171
|
+
stringio (3.2.0)
|
|
172
|
+
tsort (0.2.0)
|
|
149
173
|
tzinfo (2.0.6)
|
|
150
174
|
concurrent-ruby (~> 1.0)
|
|
151
175
|
unicode-display_width (3.2.0)
|
|
152
176
|
unicode-emoji (~> 4.1)
|
|
153
177
|
unicode-emoji (4.2.0)
|
|
154
178
|
uri (1.1.1)
|
|
155
|
-
webmock (3.26.
|
|
179
|
+
webmock (3.26.2)
|
|
156
180
|
addressable (>= 2.8.0)
|
|
157
181
|
crack (>= 0.3.2)
|
|
158
182
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
159
|
-
zeitwerk (2.7.
|
|
183
|
+
zeitwerk (2.7.5)
|
|
160
184
|
|
|
161
185
|
PLATFORMS
|
|
162
186
|
arm64-darwin-23
|
|
163
187
|
arm64-darwin-24
|
|
164
|
-
|
|
165
|
-
x86_64-darwin-20
|
|
166
|
-
x86_64-darwin-21
|
|
167
|
-
x86_64-darwin-22
|
|
188
|
+
arm64-darwin-25
|
|
168
189
|
x86_64-linux
|
|
169
190
|
|
|
170
191
|
DEPENDENCIES
|
|
171
|
-
|
|
192
|
+
debug
|
|
172
193
|
folio_client!
|
|
173
194
|
rake (~> 13.0)
|
|
174
195
|
rspec (~> 3.0)
|
|
@@ -182,4 +203,4 @@ DEPENDENCIES
|
|
|
182
203
|
webmock
|
|
183
204
|
|
|
184
205
|
BUNDLED WITH
|
|
185
|
-
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
|
|
|
@@ -163,6 +164,86 @@ client.user_details(id: 'bbbadd51-c2f1-4107-a54d-52b39087725c')
|
|
|
163
164
|
=> {"username"=>"testing",
|
|
164
165
|
"id"=>"bbbadd51-c2f1-4107-a54d-52b39087725c",
|
|
165
166
|
"externalSystemId"=>"00324439", ... # same response as above, but for single user
|
|
167
|
+
|
|
168
|
+
# Get location details by UUID (useful for checking campusId when creating holdings)
|
|
169
|
+
# see https://s3.amazonaws.com/foliodocs/api/mod-inventory-storage/p/location.html#locations__id__get
|
|
170
|
+
client.fetch_location(location_id: 'd9cd0bed-1b49-4b5e-a7bd-064b8d177231')
|
|
171
|
+
=> {"id"=>"d9cd0bed-1b49-4b5e-a7bd-064b8d177231",
|
|
172
|
+
"name"=>"Miller General Stacks",
|
|
173
|
+
"code"=>"UA/CB/LC/GS",
|
|
174
|
+
"isActive"=>true,
|
|
175
|
+
"description"=>"The very general stacks of Miller",
|
|
176
|
+
"discoveryDisplayName"=>"Miller General",
|
|
177
|
+
"institutionId"=>"4b2a3d97-01c3-4ef3-98a5-ae4e853429b4",
|
|
178
|
+
"campusId"=>"b595d838-b1d5-409e-86ac-af3b41bde0be",
|
|
179
|
+
"libraryId"=>"e2889f93-92f2-4937-b944-5452a575367e",
|
|
180
|
+
"details"=>{"a"=>"b", "foo"=>"bar"},
|
|
181
|
+
"primaryServicePoint"=>"79faacf1-4ba4-42c7-8b2a-566b259e4641",
|
|
182
|
+
"servicePointIds"=>["79faacf1-4ba4-42c7-8b2a-566b259e4641"]}
|
|
183
|
+
|
|
184
|
+
# Get holdings records for an instance by HRID (useful for checking permanentLocationId and discoverySuppress)
|
|
185
|
+
# see https://github.com/folio-org/mod-search#search-api
|
|
186
|
+
client.fetch_holdings(hrid: 'in00000000067')
|
|
187
|
+
=> [{"id"=>"7f89e96c-478c-4ca2-bb85-0a1c5b0c6f3e",
|
|
188
|
+
"instanceId"=>"5108040a-65bc-40ed-bd50-265958301ce4",
|
|
189
|
+
"permanentLocationId"=>"d9cd0bed-1b49-4b5e-a7bd-064b8d177231",
|
|
190
|
+
"discoverySuppress"=>false,
|
|
191
|
+
"hrid"=>"ho00000000010",
|
|
192
|
+
"holdingsTypeId"=>"03c9c400-b9e3-4a07-ac0e-05ab470233ed",
|
|
193
|
+
"callNumber"=>"ABC 123"},
|
|
194
|
+
{"id"=>"8a89e96c-478c-4ca2-bb85-0a1c5b0c6f3f",
|
|
195
|
+
"instanceId"=>"5108040a-65bc-40ed-bd50-265958301ce4",
|
|
196
|
+
"permanentLocationId"=>"b595d838-b1d5-409e-86ac-af3b41bde0be",
|
|
197
|
+
"discoverySuppress"=>true,
|
|
198
|
+
"hrid"=>"ho00000000011",
|
|
199
|
+
"holdingsTypeId"=>"03c9c400-b9e3-4a07-ac0e-05ab470233ed",
|
|
200
|
+
"callNumber"=>"DEF 456"}]
|
|
201
|
+
|
|
202
|
+
# Update a holdings record for an instance HRID
|
|
203
|
+
holdings_record =
|
|
204
|
+
{ 'id' => '7f89e96c-478c-4ca2-bb85-0a1c5b0c6f3e',
|
|
205
|
+
'_version' => 1,
|
|
206
|
+
'sourceId' => 'f32d531e-df79-46b3-8932-cdd35f7a2264',
|
|
207
|
+
'hrid' => 'ah1994253_1',
|
|
208
|
+
'holdingsTypeId' => '5684e4a3-9279-4463-b6ee-20ae21bbec07',
|
|
209
|
+
'instanceId' => '54ec1f1a-d039-5a39-95f2-71df00061664',
|
|
210
|
+
'permanentLocationId' => '4573e824-9273-4f13-972f-cff7bf504217',
|
|
211
|
+
'effectiveLocationId' => '4573e824-9273-4f13-972f-cff7bf504217',
|
|
212
|
+
'discoverySuppress' => false }
|
|
213
|
+
client.update_holdings(holdings_id: '7f89e96c-478c-4ca2-bb85-0a1c5b0c6f3e', holdings_record:)
|
|
214
|
+
|
|
215
|
+
#Create a holdings record
|
|
216
|
+
holdings_record =
|
|
217
|
+
{ "instance_id" => "f1b301ce-f5d2-53b5-85eb-e4452bb5a591",
|
|
218
|
+
"permanent_location_id" => '1b14e21c-8d47-45c7-bc49-456a0086422b',
|
|
219
|
+
"source_id" => "f32d531e-df79-46b3-8932-cdd35f7a2264",
|
|
220
|
+
"holdings_type_id" => "996f93e2-5b5e-4cf2-9168-33ced1f95eed",
|
|
221
|
+
"discovery_suppress" => false }
|
|
222
|
+
client.create_holdings(holdings_record:)
|
|
223
|
+
=> {
|
|
224
|
+
"id" => "c65bb9dc-ebca-41fc-9c50-0d39085c1987",
|
|
225
|
+
"_version" => 1,
|
|
226
|
+
"sourceId" => "f32d531e-df79-46b3-8932-cdd35f7a2264",
|
|
227
|
+
"hrid" => "ho00000927052",
|
|
228
|
+
"holdingsTypeId" => "996f93e2-5b5e-4cf2-9168-33ced1f95eed",
|
|
229
|
+
"formerIds" => [],
|
|
230
|
+
"instanceId" => "54ec1f1a-d039-5a39-95f2-71df00061664",
|
|
231
|
+
"permanentLocationId" => "1b14e21c-8d47-45c7-bc49-456a0086422b",
|
|
232
|
+
"effectiveLocationId" => "1b14e21c-8d47-45c7-bc49-456a0086422b",
|
|
233
|
+
"electronicAccess" => [],
|
|
234
|
+
"administrativeNotes" => [],
|
|
235
|
+
"notes" => [],
|
|
236
|
+
"holdingsStatements" => [],
|
|
237
|
+
"holdingsStatementsForIndexes" => [],
|
|
238
|
+
"holdingsStatementsForSupplements" => [],
|
|
239
|
+
"discoverySuppress" => false,
|
|
240
|
+
"statisticalCodeIds" => [],
|
|
241
|
+
"metadata" =>
|
|
242
|
+
{"createdDate" => "2026-03-12T18:58:03.576+00:00",
|
|
243
|
+
"createdByUserId" => "709fdac6-d3f3-5784-8839-fe36ad6ed0b3",
|
|
244
|
+
"updatedDate" => "2026-03-12T18:58:03.576+00:00",
|
|
245
|
+
"updatedByUserId" => "709fdac6-d3f3-5784-8839-fe36ad6ed0b3"}
|
|
246
|
+
}
|
|
166
247
|
```
|
|
167
248
|
|
|
168
249
|
## Development
|
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
|
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
spec.summary = 'Interface for interacting with the Folio ILS API.'
|
|
14
14
|
spec.description = 'This provides API interaction with the Folio ILS API'
|
|
15
15
|
spec.homepage = 'https://github.com/sul-dlss/folio_client'
|
|
16
|
-
spec.required_ruby_version = '>= 3.
|
|
16
|
+
spec.required_ruby_version = '>= 3.4'
|
|
17
17
|
|
|
18
18
|
spec.metadata['homepage_uri'] = spec.homepage
|
|
19
19
|
spec.metadata['source_code_uri'] = 'https://github.com/sul-dlss/folio_client'
|
|
@@ -32,10 +32,12 @@ 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'
|
|
38
39
|
spec.add_dependency 'marc'
|
|
40
|
+
spec.add_dependency 'ostruct'
|
|
39
41
|
spec.add_dependency 'zeitwerk'
|
|
40
42
|
|
|
41
43
|
spec.add_development_dependency 'rake', '~> 13.0'
|
|
@@ -3,27 +3,26 @@
|
|
|
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
|
|
|
16
17
|
access_cookie = FolioClient.cookie_jar.cookies.find { |cookie| cookie.name == 'folioAccessToken' }
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
# NOTE: The client typically delegates raising exceptions (based on HTTP
|
|
20
|
+
# responses) to the UnexpectedResponse class, but this call in
|
|
21
|
+
# Authenticator is a one-off, unlike any other in the app, so we
|
|
22
|
+
# allow it to customize its exception handling.
|
|
23
|
+
raise UnauthorizedError, "Problem with folioAccessToken cookie: #{response.headers}, #{response.body}" unless access_cookie
|
|
19
24
|
|
|
20
25
|
access_cookie.value
|
|
21
26
|
end
|
|
22
|
-
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
def login_endpoint
|
|
26
|
-
'/authn/login-with-expiry'
|
|
27
|
-
end
|
|
28
27
|
end
|
|
29
28
|
end
|
|
@@ -64,6 +64,54 @@ class FolioClient
|
|
|
64
64
|
false
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
# Get location details by UUID
|
|
68
|
+
# @param location_id [String] UUID of the location
|
|
69
|
+
# @return [Hash] location data including campusId and other location information
|
|
70
|
+
# @raise [ResourceNotFound] if location with the given UUID is not found
|
|
71
|
+
def fetch_location(location_id:)
|
|
72
|
+
client.get("/locations/#{location_id}")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Get holdings records for an instance by HRID
|
|
76
|
+
# @see https://s3.amazonaws.com/foliodocs/api/mod-inventory-storage/p/inventory-view.html
|
|
77
|
+
# @param hrid [String] instance HRID
|
|
78
|
+
# @return [Array<Hash>] array of holdings records with permanentLocationId, _version, discoverySuppress, and other fields
|
|
79
|
+
def fetch_holdings(hrid:)
|
|
80
|
+
instance_uuid = fetch_external_id(hrid: hrid)
|
|
81
|
+
instance_response = client.get('/inventory-view/instances', { query: "id==#{instance_uuid}" })
|
|
82
|
+
|
|
83
|
+
instance_response.dig('instances', 0, 'holdingsRecords') || []
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Put an updated holdings record
|
|
87
|
+
# @see https://s3.amazonaws.com/foliodocs/api/mod-inventory/p/inventory.html#inventory_holdings__holdingsid__put
|
|
88
|
+
# @param holdings_id [String] UUID of the holdings record to update
|
|
89
|
+
# @param holdings_record [Hash] holdings record
|
|
90
|
+
# @raise [ResourceNotFound] if holdings record with the given UUID is not found
|
|
91
|
+
# @raise [BadRequestError] may occur if the update includes invalid fields or values
|
|
92
|
+
# @raise [UnexpectedResponse] if the API returns some other error response
|
|
93
|
+
def update_holdings(holdings_id:, holdings_record:)
|
|
94
|
+
client.put("/inventory/holdings/#{holdings_id}", holdings_record, exception_subject: "holdings record with ID #{holdings_id}")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Post a new holdings record
|
|
98
|
+
# @param holdings_record [Hash] holdings record
|
|
99
|
+
# @return [Hash] the created holdings record, including its id and other fields
|
|
100
|
+
# @see https://s3.amazonaws.com/foliodocs/api/mod-inventory-storage/p/holdings-storage.html#holdings_storage_holdings_post
|
|
101
|
+
def create_holdings(holdings_record:)
|
|
102
|
+
required = %w[instance_id permanent_location_id source_id holdings_type_id]
|
|
103
|
+
missing = required.select { |field| !holdings_record.key?(field) || holdings_record[field].blank? }
|
|
104
|
+
raise ArgumentError, "Missing required fields: #{missing.join(', ')}" if missing.any?
|
|
105
|
+
|
|
106
|
+
client.post('/holdings-storage/holdings', {
|
|
107
|
+
instanceId: holdings_record['instance_id'],
|
|
108
|
+
permanentLocationId: holdings_record['permanent_location_id'],
|
|
109
|
+
sourceId: holdings_record['source_id'],
|
|
110
|
+
holdingsTypeId: holdings_record['holdings_type_id'],
|
|
111
|
+
discoverySuppress: false
|
|
112
|
+
})
|
|
113
|
+
end
|
|
114
|
+
|
|
67
115
|
private
|
|
68
116
|
|
|
69
117
|
def client
|
|
@@ -98,6 +98,10 @@ class FolioClient
|
|
|
98
98
|
def check_not_found(result, index, max_checks)
|
|
99
99
|
return unless result.failure? && result.failure == :not_found && index > max_checks
|
|
100
100
|
|
|
101
|
+
# NOTE: The client typically delegates raising exceptions (based on HTTP
|
|
102
|
+
# responses) to the UnexpectedResponse class, but the interaction in
|
|
103
|
+
# JobStatus is more complex due to waits/loops, so we allow this
|
|
104
|
+
# class to do some of its own exception handling.
|
|
101
105
|
raise ResourceNotFound,
|
|
102
106
|
"Job #{job_execution_id} not found after #{index} retries. The data import job may still have completed."
|
|
103
107
|
end
|
|
@@ -21,7 +21,7 @@ class FolioClient
|
|
|
21
21
|
"Expected 1 record for #{instance_hrid}, but found #{record_count}"
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
response_hash
|
|
24
|
+
response_hash.dig('sourceRecords', 0, 'parsedRecord', 'content')
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# get marc bib data as MARCXML from folio given an instance HRID
|
|
@@ -30,9 +30,7 @@ class FolioClient
|
|
|
30
30
|
# @return [String] MARCXML string
|
|
31
31
|
# @raise [ResourceNotFound]
|
|
32
32
|
# @raise [MultipleResourcesFound]
|
|
33
|
-
# rubocop:disable Metrics/MethodLength
|
|
34
|
-
# rubocop:disable Metrics/AbcSize
|
|
35
|
-
def fetch_marc_xml(instance_hrid: nil, barcode: nil)
|
|
33
|
+
def fetch_marc_xml(instance_hrid: nil, barcode: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
36
34
|
if barcode.nil? && instance_hrid.nil?
|
|
37
35
|
raise ArgumentError,
|
|
38
36
|
'Either a barcode or a Folio instance HRID must be provided'
|
|
@@ -62,8 +60,6 @@ class FolioClient
|
|
|
62
60
|
updated_marc.fields << MARC::ControlField.new('003', 'FOLIO')
|
|
63
61
|
updated_marc.to_xml.to_s
|
|
64
62
|
end
|
|
65
|
-
# rubocop:enable Metrics/MethodLength
|
|
66
|
-
# rubocop:enable Metrics/AbcSize
|
|
67
63
|
|
|
68
64
|
private
|
|
69
65
|
|
|
@@ -4,16 +4,18 @@ class FolioClient
|
|
|
4
4
|
# Handles unexpected responses when communicating with Folio
|
|
5
5
|
class UnexpectedResponse
|
|
6
6
|
# @param [Faraday::Response] response
|
|
7
|
-
# rubocop:disable Metrics/MethodLength
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
def self.call(response, **kwargs) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
8
|
+
subject = kwargs.fetch(:exception_subject, 'resource')
|
|
9
|
+
|
|
10
10
|
case response.status
|
|
11
|
+
when 400
|
|
12
|
+
raise BadRequestError, "Bad request for #{subject}: #{response.body}"
|
|
11
13
|
when 401
|
|
12
14
|
raise UnauthorizedError, "There was a problem with the access token: #{response.body}"
|
|
13
15
|
when 403
|
|
14
16
|
raise ForbiddenError, "The operation requires privileges which the client does not have: #{response.body}"
|
|
15
17
|
when 404
|
|
16
|
-
raise ResourceNotFound, "Endpoint not found or
|
|
18
|
+
raise ResourceNotFound, "Endpoint not found or #{subject} does not exist: #{response.body}"
|
|
17
19
|
when 409
|
|
18
20
|
raise ConflictError, "Resource cannot be updated: #{response.body}"
|
|
19
21
|
when 422
|
|
@@ -21,10 +23,8 @@ class FolioClient
|
|
|
21
23
|
when 500
|
|
22
24
|
raise ServiceUnavailable, "The remote server returned an internal server error: #{response.body}"
|
|
23
25
|
else
|
|
24
|
-
raise
|
|
26
|
+
raise Error, "Unexpected response: #{response.status} #{response.body}"
|
|
25
27
|
end
|
|
26
28
|
end
|
|
27
29
|
end
|
|
28
|
-
# rubocop:enable Metrics/MethodLength
|
|
29
|
-
# rubocop:enable Metrics/AbcSize
|
|
30
30
|
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'
|
|
@@ -41,47 +43,56 @@ class FolioClient
|
|
|
41
43
|
# Error raised when the Folio API returns a 409 Conflict
|
|
42
44
|
class ConflictError < Error; end
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
content_type: 'application/json'
|
|
47
|
-
}.freeze
|
|
46
|
+
# Error raised when the Folio API returns a 400 Bad Request
|
|
47
|
+
class BadRequestError < Error; end
|
|
48
48
|
|
|
49
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
|
+
|
|
50
63
|
# @param url [String] the folio API URL
|
|
51
64
|
# @param login_params [Hash] the folio client login params (username:, password:)
|
|
52
|
-
# @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.
|
|
53
73
|
# @return [FolioClient] the configured Singleton class
|
|
54
|
-
def configure(url:, login_params:,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# auto-magic token refreshing. Why not immediately get a valid token? Our apps
|
|
60
|
-
# commonly invoke client `.configure` methods in the initializer in all
|
|
61
|
-
# application environments, even those that are never expected to
|
|
62
|
-
# connect to production APIs, such as local development machines.
|
|
63
|
-
#
|
|
64
|
-
# NOTE: `nil` and blank string cannot be used as dummy values here as
|
|
65
|
-
# they lead to a malformed request to be sent, which triggers an
|
|
66
|
-
# exception not rescued by `with_token_refresh_when_unauthorized`
|
|
67
|
-
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(
|
|
68
79
|
url: url,
|
|
69
80
|
login_params: login_params,
|
|
70
|
-
|
|
71
|
-
|
|
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')
|
|
72
84
|
)
|
|
73
|
-
# rubocop:enable Style/OpenStructUse
|
|
74
85
|
|
|
75
86
|
self
|
|
76
87
|
end
|
|
77
88
|
|
|
78
|
-
delegate :config, :connection, :cookie_jar, :data_import, :default_timeout,
|
|
79
|
-
:edit_marc_json, :fetch_external_id, :fetch_hrid,
|
|
80
|
-
:fetch_instance_info, :fetch_marc_hash, :fetch_marc_xml,
|
|
81
|
-
:force_token_refresh!, :get, :has_instance_status?,
|
|
82
|
-
:
|
|
83
|
-
:
|
|
84
|
-
:
|
|
89
|
+
delegate :config, :connection, :cookie_jar, :create_holdings, :data_import, :default_timeout,
|
|
90
|
+
:default_user_agent, :edit_marc_json, :fetch_external_id, :fetch_holdings, :fetch_hrid,
|
|
91
|
+
:fetch_instance_info, :fetch_location, :fetch_marc_hash, :fetch_marc_xml,
|
|
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
|
|
85
96
|
end
|
|
86
97
|
|
|
87
98
|
attr_accessor :config
|
|
@@ -89,69 +100,54 @@ class FolioClient
|
|
|
89
100
|
# Send an authenticated get request
|
|
90
101
|
# @param path [String] the path to the Folio API request
|
|
91
102
|
# @param params [Hash] params to get to the API
|
|
103
|
+
# @return [Hash, nil] the parsed response body or nil
|
|
92
104
|
def get(path, params = {})
|
|
93
105
|
response = with_token_refresh_when_unauthorized do
|
|
94
|
-
connection.get(path, params
|
|
106
|
+
connection.get(path, params)
|
|
95
107
|
end
|
|
96
108
|
|
|
97
109
|
UnexpectedResponse.call(response) unless response.success?
|
|
98
110
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
JSON.parse(response.body)
|
|
111
|
+
JSON.parse(response.body) if response.body.present?
|
|
102
112
|
end
|
|
103
113
|
|
|
104
114
|
# Send an authenticated post request
|
|
105
115
|
# If the body is JSON, it will be automatically serialized
|
|
106
116
|
# @param path [String] the path to the Folio API request
|
|
107
117
|
# @param body [Object] body to post to the API as JSON
|
|
108
|
-
#
|
|
118
|
+
# @return [Hash, nil] the parsed response body or nil
|
|
109
119
|
def post(path, body = nil, content_type: 'application/json')
|
|
110
120
|
req_body = content_type == 'application/json' ? body&.to_json : body
|
|
111
121
|
response = with_token_refresh_when_unauthorized do
|
|
112
|
-
|
|
113
|
-
'x-okapi-token': config.token,
|
|
114
|
-
'content-type': content_type
|
|
115
|
-
}
|
|
116
|
-
connection.post(path, req_body, req_headers)
|
|
122
|
+
connection.post(path, req_body, { content_type: content_type })
|
|
117
123
|
end
|
|
118
124
|
|
|
119
125
|
UnexpectedResponse.call(response) unless response.success?
|
|
120
126
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
JSON.parse(response.body)
|
|
127
|
+
JSON.parse(response.body) if response.body.present?
|
|
124
128
|
end
|
|
125
|
-
# rubocop:enable Metrics/MethodLength
|
|
126
129
|
|
|
127
130
|
# Send an authenticated put request
|
|
128
131
|
# If the body is JSON, it will be automatically serialized
|
|
129
132
|
# @param path [String] the path to the Folio API request
|
|
130
133
|
# @param body [Object] body to put to the API as JSON
|
|
131
|
-
#
|
|
132
|
-
def put(path, body = nil, content_type: 'application/json')
|
|
134
|
+
# @return [Hash, nil] the parsed response body or nil
|
|
135
|
+
def put(path, body = nil, content_type: 'application/json', **exception_args)
|
|
133
136
|
req_body = content_type == 'application/json' ? body&.to_json : body
|
|
134
137
|
response = with_token_refresh_when_unauthorized do
|
|
135
|
-
|
|
136
|
-
'x-okapi-token': config.token,
|
|
137
|
-
'content-type': content_type
|
|
138
|
-
}
|
|
139
|
-
connection.put(path, req_body, req_headers)
|
|
138
|
+
connection.put(path, req_body, { content_type: content_type })
|
|
140
139
|
end
|
|
141
140
|
|
|
142
|
-
UnexpectedResponse.call(response) unless response.success?
|
|
143
|
-
|
|
144
|
-
return nil if response.body.blank?
|
|
141
|
+
UnexpectedResponse.call(response, **exception_args) unless response.success?
|
|
145
142
|
|
|
146
|
-
JSON.parse(response.body)
|
|
143
|
+
JSON.parse(response.body) if response.body.present?
|
|
147
144
|
end
|
|
148
|
-
# rubocop:enable Metrics/MethodLength
|
|
149
145
|
|
|
150
146
|
# the base connection to the Folio API
|
|
151
147
|
def connection
|
|
152
148
|
@connection ||= Faraday.new(
|
|
153
149
|
url: config.url,
|
|
154
|
-
headers:
|
|
150
|
+
headers: config.headers,
|
|
155
151
|
request: { timeout: config.timeout }
|
|
156
152
|
) do |faraday|
|
|
157
153
|
faraday.use :cookie_jar, jar: cookie_jar
|
|
@@ -186,6 +182,34 @@ class FolioClient
|
|
|
186
182
|
.fetch_instance_info(...)
|
|
187
183
|
end
|
|
188
184
|
|
|
185
|
+
# @see Inventory#fetch_location
|
|
186
|
+
def fetch_location(...)
|
|
187
|
+
Inventory
|
|
188
|
+
.new
|
|
189
|
+
.fetch_location(...)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# @see Inventory#fetch_holdings
|
|
193
|
+
def fetch_holdings(...)
|
|
194
|
+
Inventory
|
|
195
|
+
.new
|
|
196
|
+
.fetch_holdings(...)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# @see Inventory#update_holdings
|
|
200
|
+
def update_holdings(...)
|
|
201
|
+
Inventory
|
|
202
|
+
.new
|
|
203
|
+
.update_holdings(...)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# @see Inventory#create_holdings
|
|
207
|
+
def create_holdings(...)
|
|
208
|
+
Inventory
|
|
209
|
+
.new
|
|
210
|
+
.create_holdings(...)
|
|
211
|
+
end
|
|
212
|
+
|
|
189
213
|
# @see SourceStorage#fetch_marc_hash
|
|
190
214
|
def fetch_marc_hash(...)
|
|
191
215
|
SourceStorage
|
|
@@ -264,11 +288,15 @@ class FolioClient
|
|
|
264
288
|
end
|
|
265
289
|
|
|
266
290
|
def default_timeout
|
|
267
|
-
|
|
291
|
+
180
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def default_user_agent
|
|
295
|
+
"folio_client #{FolioClient::VERSION}"
|
|
268
296
|
end
|
|
269
297
|
|
|
270
298
|
def force_token_refresh!
|
|
271
|
-
|
|
299
|
+
Authenticator.refresh_token!
|
|
272
300
|
end
|
|
273
301
|
|
|
274
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
|
|
@@ -79,6 +93,20 @@ dependencies:
|
|
|
79
93
|
- - ">="
|
|
80
94
|
- !ruby/object:Gem::Version
|
|
81
95
|
version: '0'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: ostruct
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
82
110
|
- !ruby/object:Gem::Dependency
|
|
83
111
|
name: zeitwerk
|
|
84
112
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -274,14 +302,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
274
302
|
requirements:
|
|
275
303
|
- - ">="
|
|
276
304
|
- !ruby/object:Gem::Version
|
|
277
|
-
version: 3.
|
|
305
|
+
version: '3.4'
|
|
278
306
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
279
307
|
requirements:
|
|
280
308
|
- - ">="
|
|
281
309
|
- !ruby/object:Gem::Version
|
|
282
310
|
version: '0'
|
|
283
311
|
requirements: []
|
|
284
|
-
rubygems_version:
|
|
312
|
+
rubygems_version: 4.0.8
|
|
285
313
|
specification_version: 4
|
|
286
314
|
summary: Interface for interacting with the Folio ILS API.
|
|
287
315
|
test_files: []
|