folio_client 0.10.1 → 0.12.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/.autoupdate/postupdate +0 -0
- data/.rubocop/custom.yml +17 -3
- data/.rubocop.yml +5 -1
- data/Gemfile.lock +47 -29
- data/README.md +4 -4
- data/lib/folio_client/data_import.rb +21 -6
- data/lib/folio_client/job_status.rb +15 -13
- data/lib/folio_client/version.rb +1 -1
- data/lib/folio_client.rb +63 -10
- metadata +3 -4
- data/lib/folio_client/token_wrapper.rb +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7918be772bc1758840166cbb6f0fb66bfbf4765f636118d971efef2192a947df
|
|
4
|
+
data.tar.gz: 86d3c7e5656e849cf3358ddf7e22463bd65527cedf017a9a31dfdd2606bad88a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9471247fec95c9df775cd359896c788d124173477b74f1124d41641f76e6d61438fe7b5e7c10fb3ee7d6efb9f5dba11c9b0b7b45fb9040520f6668cba626fc1d
|
|
7
|
+
data.tar.gz: 4f0adf00bb277b709a68552e7f2b5119e7b8597723ffc3c4f500d850e2c8470df0249a19e35a98bcfcf2ef6daa3befb8155e57d5f69c8d825fe3b1806cd240a5
|
data/.autoupdate/postupdate
CHANGED
|
File without changes
|
data/.rubocop/custom.yml
CHANGED
|
@@ -34,9 +34,9 @@ RSpec/SubjectDeclaration: # new in 2.5
|
|
|
34
34
|
Enabled: true
|
|
35
35
|
RSpec/VerifiedDoubleReference: # new in 2.10.0
|
|
36
36
|
Enabled: true
|
|
37
|
-
|
|
37
|
+
FactoryBot/ConsistentParenthesesStyle: # new in 2.14
|
|
38
38
|
Enabled: true
|
|
39
|
-
|
|
39
|
+
FactoryBot/SyntaxMethods: # new in 2.7
|
|
40
40
|
Enabled: true
|
|
41
41
|
RSpec/Rails/AvoidSetupHook: # new in 2.4
|
|
42
42
|
Enabled: true
|
|
@@ -58,7 +58,7 @@ RSpec/DuplicatedMetadata: # new in 2.16
|
|
|
58
58
|
Enabled: true
|
|
59
59
|
RSpec/PendingWithoutReason: # new in 2.16
|
|
60
60
|
Enabled: true
|
|
61
|
-
|
|
61
|
+
FactoryBot/FactoryNameStyle: # new in 2.16
|
|
62
62
|
Enabled: true
|
|
63
63
|
RSpec/Rails/MinitestAssertions: # new in 2.17
|
|
64
64
|
Enabled: true
|
|
@@ -68,3 +68,17 @@ RSpec/SkipBlockInsideExample: # new in 2.19
|
|
|
68
68
|
Enabled: true
|
|
69
69
|
RSpec/Rails/TravelAround: # new in 2.19
|
|
70
70
|
Enabled: true
|
|
71
|
+
FactoryBot/AssociationStyle: # new in 2.23
|
|
72
|
+
Enabled: true
|
|
73
|
+
FactoryBot/FactoryAssociationWithStrategy: # new in 2.23
|
|
74
|
+
Enabled: true
|
|
75
|
+
FactoryBot/RedundantFactoryOption: # new in 2.23
|
|
76
|
+
Enabled: true
|
|
77
|
+
RSpec/BeEmpty: # new in 2.20
|
|
78
|
+
Enabled: true
|
|
79
|
+
RSpec/ContainExactly: # new in 2.19
|
|
80
|
+
Enabled: true
|
|
81
|
+
RSpec/IndexedLet: # new in 2.20
|
|
82
|
+
Enabled: true
|
|
83
|
+
RSpec/MatchArray: # new in 2.19
|
|
84
|
+
Enabled: true
|
data/.rubocop.yml
CHANGED
|
@@ -3,12 +3,16 @@ inherit_mode:
|
|
|
3
3
|
- Exclude
|
|
4
4
|
|
|
5
5
|
require:
|
|
6
|
+
- standard
|
|
7
|
+
- standard-custom
|
|
8
|
+
- standard-performance
|
|
6
9
|
- rubocop-performance
|
|
7
10
|
- rubocop-rspec
|
|
8
|
-
- standard
|
|
9
11
|
|
|
10
12
|
inherit_gem:
|
|
11
13
|
standard: config/base.yml
|
|
14
|
+
standard-performance: config/base.yml
|
|
15
|
+
standard-custom: config/base.yml
|
|
12
16
|
|
|
13
17
|
inherit_from:
|
|
14
18
|
- .rubocop/custom.yml
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
folio_client (0.
|
|
4
|
+
folio_client (0.12.0)
|
|
5
5
|
activesupport (>= 4.2, < 8)
|
|
6
6
|
dry-monads
|
|
7
7
|
faraday
|
|
@@ -11,82 +11,92 @@ PATH
|
|
|
11
11
|
GEM
|
|
12
12
|
remote: https://rubygems.org/
|
|
13
13
|
specs:
|
|
14
|
-
activesupport (7.0.
|
|
14
|
+
activesupport (7.0.8)
|
|
15
15
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
16
16
|
i18n (>= 1.6, < 2)
|
|
17
17
|
minitest (>= 5.1)
|
|
18
18
|
tzinfo (~> 2.0)
|
|
19
|
-
addressable (2.8.
|
|
19
|
+
addressable (2.8.5)
|
|
20
20
|
public_suffix (>= 2.0.2, < 6.0)
|
|
21
21
|
ast (2.4.2)
|
|
22
|
+
base64 (0.1.1)
|
|
22
23
|
byebug (11.1.3)
|
|
23
24
|
concurrent-ruby (1.2.2)
|
|
24
25
|
crack (0.4.5)
|
|
25
26
|
rexml
|
|
26
27
|
diff-lcs (1.5.0)
|
|
27
28
|
docile (1.4.0)
|
|
28
|
-
dry-core (1.0.
|
|
29
|
+
dry-core (1.0.1)
|
|
29
30
|
concurrent-ruby (~> 1.0)
|
|
30
31
|
zeitwerk (~> 2.6)
|
|
31
32
|
dry-monads (1.6.0)
|
|
32
33
|
concurrent-ruby (~> 1.0)
|
|
33
34
|
dry-core (~> 1.0, < 2)
|
|
34
35
|
zeitwerk (~> 2.6)
|
|
35
|
-
faraday (2.7.
|
|
36
|
+
faraday (2.7.11)
|
|
37
|
+
base64
|
|
36
38
|
faraday-net_http (>= 2.0, < 3.1)
|
|
37
39
|
ruby2_keywords (>= 0.0.4)
|
|
38
40
|
faraday-net_http (3.0.2)
|
|
39
41
|
hashdiff (1.0.1)
|
|
40
|
-
i18n (1.
|
|
42
|
+
i18n (1.14.1)
|
|
41
43
|
concurrent-ruby (~> 1.0)
|
|
42
44
|
json (2.6.3)
|
|
43
45
|
language_server-protocol (3.17.0.3)
|
|
46
|
+
lint_roller (1.1.0)
|
|
44
47
|
marc (1.2.0)
|
|
45
48
|
rexml
|
|
46
49
|
scrub_rb (>= 1.0.1, < 2)
|
|
47
50
|
unf
|
|
48
|
-
minitest (5.
|
|
49
|
-
parallel (1.
|
|
50
|
-
parser (3.2.
|
|
51
|
+
minitest (5.20.0)
|
|
52
|
+
parallel (1.23.0)
|
|
53
|
+
parser (3.2.2.3)
|
|
51
54
|
ast (~> 2.4.1)
|
|
52
|
-
|
|
55
|
+
racc
|
|
56
|
+
public_suffix (5.0.3)
|
|
57
|
+
racc (1.7.1)
|
|
53
58
|
rainbow (3.1.1)
|
|
54
59
|
rake (13.0.6)
|
|
55
|
-
regexp_parser (2.
|
|
56
|
-
rexml (3.2.
|
|
60
|
+
regexp_parser (2.8.1)
|
|
61
|
+
rexml (3.2.6)
|
|
57
62
|
rspec (3.12.0)
|
|
58
63
|
rspec-core (~> 3.12.0)
|
|
59
64
|
rspec-expectations (~> 3.12.0)
|
|
60
65
|
rspec-mocks (~> 3.12.0)
|
|
61
|
-
rspec-core (3.12.
|
|
66
|
+
rspec-core (3.12.2)
|
|
62
67
|
rspec-support (~> 3.12.0)
|
|
63
|
-
rspec-expectations (3.12.
|
|
68
|
+
rspec-expectations (3.12.3)
|
|
64
69
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
65
70
|
rspec-support (~> 3.12.0)
|
|
66
|
-
rspec-mocks (3.12.
|
|
71
|
+
rspec-mocks (3.12.6)
|
|
67
72
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
68
73
|
rspec-support (~> 3.12.0)
|
|
69
|
-
rspec-support (3.12.
|
|
70
|
-
rubocop (1.
|
|
74
|
+
rspec-support (3.12.1)
|
|
75
|
+
rubocop (1.56.3)
|
|
76
|
+
base64 (~> 0.1.1)
|
|
71
77
|
json (~> 2.3)
|
|
78
|
+
language_server-protocol (>= 3.17.0)
|
|
72
79
|
parallel (~> 1.10)
|
|
73
|
-
parser (>= 3.2.
|
|
80
|
+
parser (>= 3.2.2.3)
|
|
74
81
|
rainbow (>= 2.2.2, < 4.0)
|
|
75
82
|
regexp_parser (>= 1.8, < 3.0)
|
|
76
83
|
rexml (>= 3.2.5, < 4.0)
|
|
77
|
-
rubocop-ast (>= 1.
|
|
84
|
+
rubocop-ast (>= 1.28.1, < 2.0)
|
|
78
85
|
ruby-progressbar (~> 1.7)
|
|
79
86
|
unicode-display_width (>= 2.4.0, < 3.0)
|
|
80
|
-
rubocop-ast (1.
|
|
87
|
+
rubocop-ast (1.29.0)
|
|
81
88
|
parser (>= 3.2.1.0)
|
|
82
|
-
rubocop-capybara (2.
|
|
89
|
+
rubocop-capybara (2.18.0)
|
|
83
90
|
rubocop (~> 1.41)
|
|
84
|
-
rubocop-
|
|
91
|
+
rubocop-factory_bot (2.24.0)
|
|
92
|
+
rubocop (~> 1.33)
|
|
93
|
+
rubocop-performance (1.19.1)
|
|
85
94
|
rubocop (>= 1.7.0, < 2.0)
|
|
86
95
|
rubocop-ast (>= 0.4.0)
|
|
87
|
-
rubocop-rspec (2.
|
|
96
|
+
rubocop-rspec (2.24.0)
|
|
88
97
|
rubocop (~> 1.33)
|
|
89
98
|
rubocop-capybara (~> 2.17)
|
|
99
|
+
rubocop-factory_bot (~> 2.22)
|
|
90
100
|
ruby-progressbar (1.13.0)
|
|
91
101
|
ruby2_keywords (0.0.5)
|
|
92
102
|
scrub_rb (1.0.1)
|
|
@@ -96,21 +106,29 @@ GEM
|
|
|
96
106
|
simplecov_json_formatter (~> 0.1)
|
|
97
107
|
simplecov-html (0.12.3)
|
|
98
108
|
simplecov_json_formatter (0.1.4)
|
|
99
|
-
standard (1.
|
|
109
|
+
standard (1.31.1)
|
|
100
110
|
language_server-protocol (~> 3.17.0.2)
|
|
101
|
-
|
|
102
|
-
rubocop
|
|
111
|
+
lint_roller (~> 1.0)
|
|
112
|
+
rubocop (~> 1.56.2)
|
|
113
|
+
standard-custom (~> 1.0.0)
|
|
114
|
+
standard-performance (~> 1.2)
|
|
115
|
+
standard-custom (1.0.2)
|
|
116
|
+
lint_roller (~> 1.0)
|
|
117
|
+
rubocop (~> 1.50)
|
|
118
|
+
standard-performance (1.2.0)
|
|
119
|
+
lint_roller (~> 1.1)
|
|
120
|
+
rubocop-performance (~> 1.19.0)
|
|
103
121
|
tzinfo (2.0.6)
|
|
104
122
|
concurrent-ruby (~> 1.0)
|
|
105
123
|
unf (0.1.4)
|
|
106
124
|
unf_ext
|
|
107
125
|
unf_ext (0.0.8.2)
|
|
108
126
|
unicode-display_width (2.4.2)
|
|
109
|
-
webmock (3.
|
|
127
|
+
webmock (3.19.1)
|
|
110
128
|
addressable (>= 2.8.0)
|
|
111
129
|
crack (>= 0.3.2)
|
|
112
130
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
113
|
-
zeitwerk (2.6.
|
|
131
|
+
zeitwerk (2.6.11)
|
|
114
132
|
|
|
115
133
|
PLATFORMS
|
|
116
134
|
x86_64-darwin-19
|
|
@@ -130,4 +148,4 @@ DEPENDENCIES
|
|
|
130
148
|
webmock
|
|
131
149
|
|
|
132
150
|
BUNDLED WITH
|
|
133
|
-
2.4.
|
|
151
|
+
2.4.13
|
data/README.md
CHANGED
|
@@ -71,15 +71,15 @@ client.fetch_marc_hash(instance_hrid: "a7927874")
|
|
|
71
71
|
[{"003"=>"FOLIO"}....]
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
# Import
|
|
75
|
-
data_importer = client.data_import(
|
|
74
|
+
# Import MARC records into FOLIO
|
|
75
|
+
data_importer = client.data_import(records: [marc_record1, marc_record2], job_profile_id: '4ba4f4ab', job_profile_name: 'ETDs')
|
|
76
76
|
# If called too quickly, might get Failure(:not_found)
|
|
77
77
|
data_importer.status
|
|
78
78
|
=> Failure(:pending)
|
|
79
79
|
data_importer.wait_until_complete
|
|
80
80
|
=> Success()
|
|
81
|
-
data_importer.
|
|
82
|
-
=> Success("in00000000010")
|
|
81
|
+
data_importer.instance_hrids
|
|
82
|
+
=> Success(["in00000000010", "in00000000011"])
|
|
83
83
|
|
|
84
84
|
# Get list of organizations (filtered with an optional query)
|
|
85
85
|
# see https://s3.amazonaws.com/foliodocs/api/mod-organizations/p/organizations.html#organizations_organizations_get
|
|
@@ -7,21 +7,28 @@ require "stringio"
|
|
|
7
7
|
class FolioClient
|
|
8
8
|
# Imports MARC records into FOLIO
|
|
9
9
|
class DataImport
|
|
10
|
+
JOB_PROFILE_ATTRIBUTES = %w[id name description dataType].freeze
|
|
11
|
+
|
|
10
12
|
# @param client [FolioClient] the configured client
|
|
11
13
|
def initialize(client)
|
|
12
14
|
@client = client
|
|
13
15
|
end
|
|
14
16
|
|
|
15
|
-
# @param
|
|
17
|
+
# @param records [Array<MARC::Record>] records to be imported
|
|
16
18
|
# @param job_profile_id [String] job profile id to use for import
|
|
17
19
|
# @param job_profile_name [String] job profile name to use for import
|
|
18
|
-
|
|
20
|
+
# @return [JobStatus] a job status instance to get information about the data import job
|
|
21
|
+
def import(records:, job_profile_id:, job_profile_name:)
|
|
19
22
|
response_hash = client.post("/data-import/uploadDefinitions", {fileDefinitions: [{name: marc_filename}]})
|
|
20
23
|
upload_definition_id = response_hash.dig("fileDefinitions", 0, "uploadDefinitionId")
|
|
21
24
|
job_execution_id = response_hash.dig("fileDefinitions", 0, "jobExecutionId")
|
|
22
25
|
file_definition_id = response_hash.dig("fileDefinitions", 0, "id")
|
|
23
26
|
|
|
24
|
-
upload_file_response_hash = client.post(
|
|
27
|
+
upload_file_response_hash = client.post(
|
|
28
|
+
"/data-import/uploadDefinitions/#{upload_definition_id}/files/#{file_definition_id}",
|
|
29
|
+
marc_binary(records),
|
|
30
|
+
content_type: "application/octet-stream"
|
|
31
|
+
)
|
|
25
32
|
|
|
26
33
|
client.post(
|
|
27
34
|
"/data-import/uploadDefinitions/#{upload_definition_id}/processFiles",
|
|
@@ -38,18 +45,26 @@ class FolioClient
|
|
|
38
45
|
JobStatus.new(client, job_execution_id: job_execution_id)
|
|
39
46
|
end
|
|
40
47
|
|
|
48
|
+
# @return [Array<Hash<String,String>>] a list of job profile hashes
|
|
49
|
+
def job_profiles
|
|
50
|
+
client
|
|
51
|
+
.get("/data-import-profiles/jobProfiles")
|
|
52
|
+
.fetch("jobProfiles", [])
|
|
53
|
+
.map { |profile| profile.slice(*JOB_PROFILE_ATTRIBUTES) }
|
|
54
|
+
end
|
|
55
|
+
|
|
41
56
|
private
|
|
42
57
|
|
|
43
|
-
attr_reader :client, :
|
|
58
|
+
attr_reader :client, :job_profile_id, :job_profile_name
|
|
44
59
|
|
|
45
60
|
def marc_filename
|
|
46
61
|
@marc_filename ||= "#{DateTime.now.iso8601}.marc"
|
|
47
62
|
end
|
|
48
63
|
|
|
49
|
-
def marc_binary(
|
|
64
|
+
def marc_binary(records)
|
|
50
65
|
StringIO.open do |io|
|
|
51
66
|
MARC::Writer.new(io) do |writer|
|
|
52
|
-
writer.write(
|
|
67
|
+
records.each { |record| writer.write(record) }
|
|
53
68
|
end
|
|
54
69
|
io.string
|
|
55
70
|
end
|
|
@@ -17,15 +17,17 @@ class FolioClient
|
|
|
17
17
|
@job_execution_id = job_execution_id
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
# @
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
20
|
+
# @todo An "ERROR" approach means one or more records failed, but it does
|
|
21
|
+
# not mean they all fail. We will likely need a more nuanced way to
|
|
22
|
+
# handle this eventually.
|
|
23
|
+
#
|
|
24
|
+
# @return [Dry::Monads::Result] Success() if job is complete,
|
|
25
|
+
# Failure(:pending) if job is still running,
|
|
26
|
+
# Failure(:not_found) if job is not found
|
|
24
27
|
def status
|
|
25
|
-
response_hash = client.get("/
|
|
28
|
+
response_hash = client.get("/change-manager/jobExecutions/#{job_execution_id}")
|
|
26
29
|
|
|
27
|
-
return Failure(:
|
|
28
|
-
return Failure(:pending) if response_hash.dig("sourceRecordSummary", "totalCreatedEntities").zero? && response_hash.dig("sourceRecordSummary", "totalUpdatedEntities").zero?
|
|
30
|
+
return Failure(:pending) if !["COMMITTED", "ERROR"].include?(response_hash["status"])
|
|
29
31
|
|
|
30
32
|
Success()
|
|
31
33
|
rescue ResourceNotFound
|
|
@@ -37,18 +39,18 @@ class FolioClient
|
|
|
37
39
|
wait_with_timeout(wait_secs: wait_secs, timeout_secs: timeout_secs) { status }
|
|
38
40
|
end
|
|
39
41
|
|
|
40
|
-
def
|
|
42
|
+
def instance_hrids
|
|
41
43
|
current_status = status
|
|
42
44
|
return current_status unless current_status.success?
|
|
43
45
|
|
|
44
|
-
@
|
|
46
|
+
@instance_hrids ||= wait_with_timeout do
|
|
45
47
|
response = client
|
|
46
48
|
.get("/metadata-provider/journalRecords/#{job_execution_id}")
|
|
47
49
|
.fetch("journalRecords", [])
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
+
.select { |journal_record| journal_record["entityType"] == "INSTANCE" && journal_record["actionStatus"] == "COMPLETED" }
|
|
51
|
+
.filter_map { |instance_record| instance_record["entityHrId"] }
|
|
50
52
|
|
|
51
|
-
response.
|
|
53
|
+
response.empty? ? Failure() : Success(response)
|
|
52
54
|
end
|
|
53
55
|
end
|
|
54
56
|
|
|
@@ -61,7 +63,7 @@ class FolioClient
|
|
|
61
63
|
end
|
|
62
64
|
|
|
63
65
|
def default_timeout_secs
|
|
64
|
-
|
|
66
|
+
10 * 60
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
def wait_with_timeout(wait_secs: default_wait_secs, timeout_secs: default_timeout_secs)
|
data/lib/folio_client/version.rb
CHANGED
data/lib/folio_client.rb
CHANGED
|
@@ -47,17 +47,34 @@ class FolioClient
|
|
|
47
47
|
# @param url [String] the folio API URL
|
|
48
48
|
# @param login_params [Hash] the folio client login params (username:, password:)
|
|
49
49
|
# @param okapi_headers [Hash] the okapi specific headers to add (X-Okapi-Tenant:, User-Agent:)
|
|
50
|
+
# @return [FolioClient] the configured Singleton class
|
|
50
51
|
def configure(url:, login_params:, okapi_headers:, timeout: default_timeout)
|
|
51
|
-
instance.config = OpenStruct.new(
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
instance.config = OpenStruct.new(
|
|
53
|
+
# For the initial token, use a dummy value to avoid hitting any APIs
|
|
54
|
+
# during configuration, allowing `with_token_refresh_when_unauthorized` to handle
|
|
55
|
+
# auto-magic token refreshing. Why not immediately get a valid token? Our apps
|
|
56
|
+
# commonly invoke client `.configure` methods in the initializer in all
|
|
57
|
+
# application environments, even those that are never expected to
|
|
58
|
+
# connect to production APIs, such as local development machines.
|
|
59
|
+
#
|
|
60
|
+
# NOTE: `nil` and blank string cannot be used as dummy values here as
|
|
61
|
+
# they lead to a malformed request to be sent, which triggers an
|
|
62
|
+
# exception not rescued by `with_token_refresh_when_unauthorized`
|
|
63
|
+
token: "a temporary dummy token to avoid hitting the API before it is needed",
|
|
64
|
+
url: url,
|
|
65
|
+
login_params: login_params,
|
|
66
|
+
okapi_headers: okapi_headers,
|
|
67
|
+
timeout: timeout
|
|
68
|
+
)
|
|
54
69
|
|
|
55
70
|
self
|
|
56
71
|
end
|
|
57
72
|
|
|
58
|
-
delegate :config, :connection, :
|
|
59
|
-
|
|
60
|
-
:
|
|
73
|
+
delegate :config, :connection, :data_import, :default_timeout,
|
|
74
|
+
:edit_marc_json, :fetch_external_id, :fetch_hrid, :fetch_instance_info,
|
|
75
|
+
:fetch_marc_hash, :get, :has_instance_status?, :http_get_headers,
|
|
76
|
+
:http_post_and_put_headers, :interface_details, :job_profiles,
|
|
77
|
+
:organization_interfaces, :organizations, :post, :put, to: :instance
|
|
61
78
|
end
|
|
62
79
|
|
|
63
80
|
attr_accessor :config
|
|
@@ -66,7 +83,7 @@ class FolioClient
|
|
|
66
83
|
# @param path [String] the path to the Folio API request
|
|
67
84
|
# @param params [Hash] params to get to the API
|
|
68
85
|
def get(path, params = {})
|
|
69
|
-
response =
|
|
86
|
+
response = with_token_refresh_when_unauthorized do
|
|
70
87
|
connection.get(path, params, {"x-okapi-token": config.token})
|
|
71
88
|
end
|
|
72
89
|
|
|
@@ -83,7 +100,7 @@ class FolioClient
|
|
|
83
100
|
# @param body [Object] body to post to the API as JSON
|
|
84
101
|
def post(path, body = nil, content_type: "application/json")
|
|
85
102
|
req_body = (content_type == "application/json") ? body&.to_json : body
|
|
86
|
-
response =
|
|
103
|
+
response = with_token_refresh_when_unauthorized do
|
|
87
104
|
req_headers = {
|
|
88
105
|
"x-okapi-token": config.token,
|
|
89
106
|
"content-type": content_type
|
|
@@ -104,7 +121,7 @@ class FolioClient
|
|
|
104
121
|
# @param body [Object] body to put to the API as JSON
|
|
105
122
|
def put(path, body = nil, content_type: "application/json")
|
|
106
123
|
req_body = (content_type == "application/json") ? body&.to_json : body
|
|
107
|
-
response =
|
|
124
|
+
response = with_token_refresh_when_unauthorized do
|
|
108
125
|
req_headers = {
|
|
109
126
|
"x-okapi-token": config.token,
|
|
110
127
|
"content-type": content_type
|
|
@@ -172,25 +189,35 @@ class FolioClient
|
|
|
172
189
|
.import(...)
|
|
173
190
|
end
|
|
174
191
|
|
|
175
|
-
# @see
|
|
192
|
+
# @ see DataImport#job_profiles
|
|
193
|
+
def job_profiles(...)
|
|
194
|
+
DataImport
|
|
195
|
+
.new(self)
|
|
196
|
+
.job_profiles(...)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# @see RecordsEditor#edit_marc_json
|
|
176
200
|
def edit_marc_json(...)
|
|
177
201
|
RecordsEditor
|
|
178
202
|
.new(self)
|
|
179
203
|
.edit_marc_json(...)
|
|
180
204
|
end
|
|
181
205
|
|
|
206
|
+
# @see Organizations#fetch_list
|
|
182
207
|
def organizations(...)
|
|
183
208
|
Organizations
|
|
184
209
|
.new(self)
|
|
185
210
|
.fetch_list(...)
|
|
186
211
|
end
|
|
187
212
|
|
|
213
|
+
# @see Organizations#fetch_interface_list
|
|
188
214
|
def organization_interfaces(...)
|
|
189
215
|
Organizations
|
|
190
216
|
.new(self)
|
|
191
217
|
.fetch_interface_list(...)
|
|
192
218
|
end
|
|
193
219
|
|
|
220
|
+
# @see Organizations#fetch_interface_details
|
|
194
221
|
def interface_details(...)
|
|
195
222
|
Organizations
|
|
196
223
|
.new(self)
|
|
@@ -200,4 +227,30 @@ class FolioClient
|
|
|
200
227
|
def default_timeout
|
|
201
228
|
120
|
|
202
229
|
end
|
|
230
|
+
|
|
231
|
+
private
|
|
232
|
+
|
|
233
|
+
# Wraps API operations to request new access token if expired.
|
|
234
|
+
# @yieldreturn response [Faraday::Response] the response to inspect
|
|
235
|
+
#
|
|
236
|
+
# @note You likely want to make sure you're wrapping a _single_ HTTP request in this
|
|
237
|
+
# method, because 1) all calls in the block will be retried from the top if there's
|
|
238
|
+
# an authN failure detected, and 2) only the response returned by the block will be
|
|
239
|
+
# inspected for authN failure.
|
|
240
|
+
# Related: consider that the client instance and its token will live across many
|
|
241
|
+
# invocations of the FolioClient methods once the client is configured by a consuming application,
|
|
242
|
+
# since this class is a Singleton. Thus, a token may expire between any two calls (i.e. it
|
|
243
|
+
# isn't necessary for a set of operations to collectively take longer than the token lifetime for
|
|
244
|
+
# expiry to fall in the middle of that related set of HTTP calls).
|
|
245
|
+
def with_token_refresh_when_unauthorized
|
|
246
|
+
response = yield
|
|
247
|
+
|
|
248
|
+
# if unauthorized, token has likely expired. try to get a new token and then retry the same request(s).
|
|
249
|
+
if response.status == 401
|
|
250
|
+
config.token = Authenticator.token(config.login_params, connection)
|
|
251
|
+
response = yield
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
response
|
|
255
|
+
end
|
|
203
256
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: folio_client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter Mangiafico
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-09-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -196,7 +196,6 @@ files:
|
|
|
196
196
|
- lib/folio_client/organizations.rb
|
|
197
197
|
- lib/folio_client/records_editor.rb
|
|
198
198
|
- lib/folio_client/source_storage.rb
|
|
199
|
-
- lib/folio_client/token_wrapper.rb
|
|
200
199
|
- lib/folio_client/unexpected_response.rb
|
|
201
200
|
- lib/folio_client/version.rb
|
|
202
201
|
homepage: https://github.com/sul-dlss/folio_client
|
|
@@ -221,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
221
220
|
- !ruby/object:Gem::Version
|
|
222
221
|
version: '0'
|
|
223
222
|
requirements: []
|
|
224
|
-
rubygems_version: 3.3.
|
|
223
|
+
rubygems_version: 3.3.7
|
|
225
224
|
signing_key:
|
|
226
225
|
specification_version: 4
|
|
227
226
|
summary: Interface for interacting with the Folio ILS API.
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class FolioClient
|
|
4
|
-
# Wraps API operations to request new access token if expired
|
|
5
|
-
class TokenWrapper
|
|
6
|
-
def self.refresh(config, connection)
|
|
7
|
-
yield.tap { |response| UnexpectedResponse.call(response) unless response.success? }
|
|
8
|
-
rescue UnauthorizedError
|
|
9
|
-
config.token = Authenticator.token(config.login_params, connection)
|
|
10
|
-
yield
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|