folio_api_client 0.4.3 → 0.5.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/README.md +3 -0
- data/lib/folio_api_client/finders.rb +76 -1
- data/lib/folio_api_client/version.rb +1 -1
- data/lib/folio_api_client.rb +6 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f56685abde3488956608b9a829b88079a22ed2dac7608ed792b1c6eafc2e662
|
|
4
|
+
data.tar.gz: 1fafd404369f0a3061c079e7922da6caea6735cb8b4a2e943512af815aaf3621
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '09241496ff463b9c8f01a9b2a801698908d426cc30080ca72a81a2b09480a0ba8b116a7b1f2f6cad1ad13f53ae00872b202f3faff30d306ab18620ceaee40266'
|
|
7
|
+
data.tar.gz: 2a33854f99b12cbf278573a8e4ec3eed28474bb9b14a4ca9af8089905718952d2b804daf810006c8ecea6c9cb7d4620fec3ec5d9b7d00a9502bf5f1a87efda29
|
data/README.md
CHANGED
|
@@ -54,6 +54,9 @@ client.find_instance_record(instance_record_id: 'some-instance-record-id')
|
|
|
54
54
|
client.find_instance_record(instance_record_hrid: 'some-instance-record-hrid')
|
|
55
55
|
client.find_source_record(instance_record_id: 'some-instance-record-id')
|
|
56
56
|
client.find_source_record(instance_record_hrid: 'some-instance-record-hrid')
|
|
57
|
+
client.find_source_marc_records(modified_since: '2025-01-01T00:00:00Z') { |marc_record_hash| }
|
|
58
|
+
client.find_source_marc_records(with_965_value: '965abc') { |marc_record_hash| }
|
|
59
|
+
client.find_source_marc_records(modified_since: '2025-01-01T00:00:00Z', with_965_value: '965abc') { |marc_record_hash| }
|
|
57
60
|
|
|
58
61
|
# Convert a FOLIO MARC source record to a marc gem MARC::Record object:
|
|
59
62
|
source_record = client.find_source_record(instance_record_id: 'some-instance-record-id')
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class FolioApiClient
|
|
4
|
-
module Finders
|
|
4
|
+
module Finders # rubocop:disable Metrics/ModuleLength
|
|
5
5
|
def find_item_record(barcode:)
|
|
6
6
|
item_search_results = self.get('/item-storage/items', { query: "barcode==#{barcode}", limit: 2 })['items']
|
|
7
7
|
return nil if item_search_results.empty?
|
|
@@ -90,6 +90,52 @@ class FolioApiClient
|
|
|
90
90
|
source_record_search_results['sourceRecords'].first
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
+
# Retrieve and yield marc records, filtered by the given modified_since and with_965_value parameters.
|
|
94
|
+
# NOTE: This method skips staff-suppressed FOLIO records.
|
|
95
|
+
def find_source_marc_records(modified_since: nil, with_965_value: nil, &block) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
96
|
+
# FOLIO does not allow an offset value higher than 9999, but this method needs to be able to retrieve
|
|
97
|
+
# result sets that have more than 9999 results, so we break big queries up into a bunch of smaller
|
|
98
|
+
# queries that split up the results based on the first character of the instance UUIDs. The first character
|
|
99
|
+
# of a UUID is a hex character (0-9 or a-f), which means that we will perform 16 different searches
|
|
100
|
+
# (i.e. "all of the results that have instance ids that start with a", then "all of the results that have
|
|
101
|
+
# instance ids that start with b", and so on).
|
|
102
|
+
|
|
103
|
+
# Since we're splitting up the query into a bunch of different sub-queries, we need to do a
|
|
104
|
+
# non-prefix-filtered query first just to get the total number of results.
|
|
105
|
+
total_query = response = self.get(
|
|
106
|
+
'search/instances',
|
|
107
|
+
marc_records_query(modified_since: modified_since, with_965_value: with_965_value).merge({ limit: 0 })
|
|
108
|
+
)
|
|
109
|
+
total_records = total_query['totalRecords']
|
|
110
|
+
|
|
111
|
+
with_uuid_prefixes do |uuid_prefix|
|
|
112
|
+
query = marc_records_query(modified_since: modified_since, with_965_value: with_965_value,
|
|
113
|
+
uuid_prefix: uuid_prefix)
|
|
114
|
+
loop do
|
|
115
|
+
response = self.get('search/instances', query)
|
|
116
|
+
process_marc_for_instance(response['instances'], total_records, &block) if block
|
|
117
|
+
break if (query[:offset] + query[:limit]) >= response['totalRecords']
|
|
118
|
+
|
|
119
|
+
query[:offset] += query[:limit]
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# UUIDs can start with
|
|
125
|
+
def with_uuid_prefixes(&block)
|
|
126
|
+
(('0'..'9').to_a + ('a'..'f').to_a).each(&block)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def process_marc_for_instance(instances, total_records, &block)
|
|
130
|
+
instances.each do |instance|
|
|
131
|
+
source_record = find_source_record(instance_record_id: instance['id'])
|
|
132
|
+
next if source_record.nil? # Occasionally, we find an instance record without a source record. Skip these.
|
|
133
|
+
|
|
134
|
+
marc_content = source_record.dig('parsedRecord', 'content')
|
|
135
|
+
yield(marc_content, total_records) if marc_content && block
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
93
139
|
def source_record_query(instance_record_id: nil, instance_record_hrid: nil)
|
|
94
140
|
return { instanceId: instance_record_id } if instance_record_id
|
|
95
141
|
return { instanceHrid: instance_record_hrid } if instance_record_hrid
|
|
@@ -112,5 +158,34 @@ class FolioApiClient
|
|
|
112
158
|
raise FolioApiClient::Exceptions::MissingQueryFieldError,
|
|
113
159
|
'Missing query field. Must supply a code.'
|
|
114
160
|
end
|
|
161
|
+
|
|
162
|
+
def marc_records_query(modified_since: nil, with_965_value: nil, uuid_prefix: nil) # rubocop:disable Metrics/MethodLength,Metrics/CyclomaticComplexity
|
|
163
|
+
params = { limit: 100, offset: 0 }
|
|
164
|
+
|
|
165
|
+
if modified_since.nil? && with_965_value.nil?
|
|
166
|
+
raise FolioApiClient::Exceptions::MissingQueryFieldError,
|
|
167
|
+
'Missing query field. Must supply either modified_since or with_965_value.'
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
if modified_since && !modified_since.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/)
|
|
171
|
+
raise ArgumentError,
|
|
172
|
+
%(Invalid format for modified_since argument. Must be a date string like "2025-10-03T16:49:00Z".)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
query_parts = []
|
|
176
|
+
query_parts << "metadata.updatedDate>=\"#{modified_since}\"" if modified_since
|
|
177
|
+
query_parts << %(identifiers.value="#{with_965_value}") if with_965_value
|
|
178
|
+
|
|
179
|
+
# Only include non-staff-suppressed records because staff-suppressed records represent deleted records in FOLIO.
|
|
180
|
+
# Reminder: staff-suppressed records are NOT the same as discovery-suppressed records. Discovery-suppressed
|
|
181
|
+
# records are ones that aren't displayed to the public, and we DO want to include discovery-suppressed records
|
|
182
|
+
# in the results returned by this query.
|
|
183
|
+
query_parts << 'staffSuppress==false'
|
|
184
|
+
|
|
185
|
+
query_parts << %(id="#{uuid_prefix}*") if uuid_prefix
|
|
186
|
+
|
|
187
|
+
params[:query] = query_parts.join(' and ')
|
|
188
|
+
params
|
|
189
|
+
end
|
|
115
190
|
end
|
|
116
191
|
end
|
data/lib/folio_api_client.rb
CHANGED
|
@@ -55,6 +55,12 @@ class FolioApiClient
|
|
|
55
55
|
# one time in responde to a 401 or 403.
|
|
56
56
|
refresh_auth_token!
|
|
57
57
|
|
|
58
|
+
# Re-run block
|
|
59
|
+
yield
|
|
60
|
+
rescue Faraday::ConnectionFailed
|
|
61
|
+
# If we make too many rapid requests in a row, FOLIO sometimes disconnects.
|
|
62
|
+
# If this happens, we'll sleep for a little while and retry the request.
|
|
63
|
+
sleep 5
|
|
58
64
|
# Re-run block
|
|
59
65
|
yield
|
|
60
66
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: folio_api_client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eric O'Hanlon
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-10-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|