folio_client 0.14.0 → 0.16.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.
data/folio_client.gemspec CHANGED
@@ -1,24 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path("lib", __dir__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require "folio_client/version"
5
+ require 'folio_client/version'
6
6
 
7
7
  Gem::Specification.new do |spec|
8
- spec.name = "folio_client"
8
+ spec.name = 'folio_client'
9
9
  spec.version = FolioClient::VERSION
10
- spec.authors = ["Peter Mangiafico"]
11
- spec.email = ["pmangiafico@stanford.edu"]
10
+ spec.authors = ['Peter Mangiafico']
11
+ spec.email = ['pmangiafico@stanford.edu']
12
12
 
13
- spec.summary = "Interface for interacting with the Folio ILS API."
14
- spec.description = "This provides API interaction with the Folio ILS API"
15
- spec.homepage = "https://github.com/sul-dlss/folio_client"
16
- spec.required_ruby_version = ">= 2.6.0"
13
+ spec.summary = 'Interface for interacting with the Folio ILS API.'
14
+ spec.description = 'This provides API interaction with the Folio ILS API'
15
+ spec.homepage = 'https://github.com/sul-dlss/folio_client'
16
+ spec.required_ruby_version = '>= 3.0.0'
17
17
 
18
- spec.metadata["homepage_uri"] = spec.homepage
19
- spec.metadata["source_code_uri"] = "https://github.com/sul-dlss/folio_client"
20
- spec.metadata["changelog_uri"] = "https://github.com/sul-dlss/folio_client/releases"
21
- spec.metadata["rubygems_mfa_required"] = "true"
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/sul-dlss/folio_client'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/sul-dlss/folio_client/releases'
21
+ spec.metadata['rubygems_mfa_required'] = 'true'
22
22
 
23
23
  # Specify which files should be added to the gem when it is released.
24
24
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -27,20 +27,22 @@ Gem::Specification.new do |spec|
27
27
  (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
28
28
  end
29
29
  end
30
- spec.bindir = "exe"
30
+ spec.bindir = 'exe'
31
31
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
- spec.require_paths = ["lib"]
32
+ spec.require_paths = ['lib']
33
33
 
34
- spec.add_dependency "activesupport", ">= 4.2", "< 8"
35
- spec.add_dependency "faraday"
36
- spec.add_dependency "zeitwerk"
37
- spec.add_dependency "marc"
38
- spec.add_dependency "dry-monads"
34
+ spec.add_dependency 'activesupport', '>= 4.2', '< 8'
35
+ spec.add_dependency 'dry-monads'
36
+ spec.add_dependency 'faraday'
37
+ spec.add_dependency 'faraday-cookie_jar'
38
+ spec.add_dependency 'marc'
39
+ spec.add_dependency 'zeitwerk'
39
40
 
40
- spec.add_development_dependency "rake", "~> 13.0"
41
- spec.add_development_dependency "rspec", "~> 3.0"
42
- spec.add_development_dependency "rubocop-rspec"
43
- spec.add_development_dependency "simplecov"
44
- spec.add_development_dependency "standard"
45
- spec.add_development_dependency "webmock"
41
+ spec.add_development_dependency 'rake', '~> 13.0'
42
+ spec.add_development_dependency 'rspec', '~> 3.0'
43
+ spec.add_development_dependency 'rubocop'
44
+ spec.add_development_dependency 'rubocop-performance'
45
+ spec.add_development_dependency 'rubocop-rspec'
46
+ spec.add_development_dependency 'simplecov'
47
+ spec.add_development_dependency 'webmock'
46
48
  end
@@ -3,24 +3,34 @@
3
3
  class FolioClient
4
4
  # Fetch a token from the Folio API using login_params
5
5
  class Authenticator
6
- def self.token(login_params, connection)
7
- new(login_params, connection).token
8
- end
9
-
10
- def initialize(login_params, connection)
11
- @login_params = login_params
12
- @connection = connection
6
+ def self.token
7
+ new.token
13
8
  end
14
9
 
15
10
  # Request an access_token
16
- def token
17
- response = connection.post("/authn/login", login_params.to_json)
11
+ def token # rubocop:disable Metrics/AbcSize
12
+ response = FolioClient.connection.post(login_endpoint, FolioClient.config.login_params.to_json)
18
13
 
19
14
  UnexpectedResponse.call(response) unless response.success?
20
15
 
21
- JSON.parse(response.body)["okapiToken"]
16
+ # remove legacy_auth once new tokens enabled on Poppy
17
+ if FolioClient.config.legacy_auth
18
+ JSON.parse(response.body)['okapiToken']
19
+ else
20
+ access_cookie = FolioClient.cookie_jar.cookies.find { |cookie| cookie.name == 'folioAccessToken' }
21
+
22
+ raise StandardError, "Problem with folioAccessToken cookie: #{response.headers}, #{response.body}" unless access_cookie
23
+
24
+ access_cookie.value
25
+ end
22
26
  end
23
27
 
24
- attr_reader :login_params, :connection
28
+ private
29
+
30
+ def login_endpoint
31
+ return '/authn/login-with-expiry' unless FolioClient.config.legacy_auth
32
+
33
+ '/authn/login'
34
+ end
25
35
  end
26
36
  end
@@ -1,32 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "date"
4
- require "stringio"
3
+ require 'date'
4
+ require 'stringio'
5
5
 
6
6
  class FolioClient
7
7
  # Imports MARC records into FOLIO
8
8
  class DataImport
9
9
  JOB_PROFILE_ATTRIBUTES = %w[id name description dataType].freeze
10
10
 
11
- # @param client [FolioClient] the configured client
12
- def initialize(client)
13
- @client = client
14
- end
15
-
16
11
  # @param records [Array<MARC::Record>] records to be imported
17
12
  # @param job_profile_id [String] job profile id to use for import
18
13
  # @param job_profile_name [String] job profile name to use for import
19
14
  # @return [JobStatus] a job status instance to get information about the data import job
15
+ # rubocop:disable Metrics/MethodLength
20
16
  def import(records:, job_profile_id:, job_profile_name:)
21
- response_hash = client.post("/data-import/uploadDefinitions", {fileDefinitions: [{name: marc_filename}]})
22
- upload_definition_id = response_hash.dig("fileDefinitions", 0, "uploadDefinitionId")
23
- job_execution_id = response_hash.dig("fileDefinitions", 0, "jobExecutionId")
24
- file_definition_id = response_hash.dig("fileDefinitions", 0, "id")
17
+ response_hash = client.post('/data-import/uploadDefinitions', { fileDefinitions: [{ name: marc_filename }] })
18
+ upload_definition_id = response_hash.dig('fileDefinitions', 0, 'uploadDefinitionId')
19
+ job_execution_id = response_hash.dig('fileDefinitions', 0, 'jobExecutionId')
20
+ file_definition_id = response_hash.dig('fileDefinitions', 0, 'id')
25
21
 
26
22
  upload_file_response_hash = client.post(
27
23
  "/data-import/uploadDefinitions/#{upload_definition_id}/files/#{file_definition_id}",
28
24
  marc_binary(records),
29
- content_type: "application/octet-stream"
25
+ content_type: 'application/octet-stream'
30
26
  )
31
27
 
32
28
  client.post(
@@ -36,25 +32,30 @@ class FolioClient
36
32
  jobProfileInfo: {
37
33
  id: job_profile_id,
38
34
  name: job_profile_name,
39
- dataType: "MARC"
35
+ dataType: 'MARC'
40
36
  }
41
37
  }
42
38
  )
43
39
 
44
- JobStatus.new(client, job_execution_id: job_execution_id)
40
+ JobStatus.new(job_execution_id: job_execution_id)
45
41
  end
42
+ # rubocop:enable Metrics/MethodLength
46
43
 
47
44
  # @return [Array<Hash<String,String>>] a list of job profile hashes
48
45
  def job_profiles
49
46
  client
50
- .get("/data-import-profiles/jobProfiles")
51
- .fetch("jobProfiles", [])
47
+ .get('/data-import-profiles/jobProfiles')
48
+ .fetch('jobProfiles', [])
52
49
  .map { |profile| profile.slice(*JOB_PROFILE_ATTRIBUTES) }
53
50
  end
54
51
 
55
52
  private
56
53
 
57
- attr_reader :client, :job_profile_id, :job_profile_name
54
+ attr_reader :job_profile_id, :job_profile_name
55
+
56
+ def client
57
+ FolioClient.instance
58
+ end
58
59
 
59
60
  def marc_filename
60
61
  @marc_filename ||= "#{DateTime.now.iso8601}.marc"
@@ -3,26 +3,19 @@
3
3
  class FolioClient
4
4
  # Lookup items in the Folio inventory
5
5
  class Inventory
6
- attr_accessor :client
7
-
8
- # @param client [FolioClient] the configured client
9
- def initialize(client)
10
- @client = client
11
- end
12
-
13
6
  # get instance HRID from barcode
14
7
  # @param barcode [String] barcode
15
8
  # @return [String,nil] instance HRID if present, otherwise nil.
16
9
  def fetch_hrid(barcode:)
17
10
  # find the instance UUID for this barcode
18
- instance = client.get("/search/instances", {query: "items.barcode==#{barcode}"})
19
- instance_uuid = instance.dig("instances", 0, "id")
11
+ instance = client.get('/search/instances', { query: "items.barcode==#{barcode}" })
12
+ instance_uuid = instance.dig('instances', 0, 'id')
20
13
 
21
14
  return nil unless instance_uuid
22
15
 
23
16
  # next lookup the instance given the instance_uuid so we can fetch the hrid
24
17
  result = client.get("/inventory/instances/#{instance_uuid}")
25
- result.dig("hrid")
18
+ result['hrid']
26
19
  end
27
20
 
28
21
  # get instance external ID from HRID
@@ -30,12 +23,12 @@ class FolioClient
30
23
  # @return [String,nil] instance external ID if present, otherwise nil.
31
24
  # @raise [ResourceNotFound, MultipleResourcesFound] if search does not return exactly 1 result
32
25
  def fetch_external_id(hrid:)
33
- instance_response = client.get("/search/instances", {query: "hrid==#{hrid}"})
34
- record_count = instance_response["totalRecords"]
35
- raise ResourceNotFound, "No matching instance found for #{hrid}" if instance_response["totalRecords"] == 0
26
+ instance_response = client.get('/search/instances', { query: "hrid==#{hrid}" })
27
+ record_count = instance_response['totalRecords']
28
+ raise ResourceNotFound, "No matching instance found for #{hrid}" if (instance_response['totalRecords']).zero?
36
29
  raise MultipleResourcesFound, "Expected 1 record for #{hrid}, but found #{record_count}" if record_count > 1
37
30
 
38
- instance_response.dig("instances", 0, "id")
31
+ instance_response.dig('instances', 0, 'id')
39
32
  end
40
33
 
41
34
  # Retrieve basic information about a instance record. Example usage: get the external ID and _version for update using
@@ -46,8 +39,8 @@ class FolioClient
46
39
  # @return [Hash] information about the record.
47
40
  # @raise [ArgumentError] if the caller does not provide exactly one of external_id or hrid
48
41
  def fetch_instance_info(external_id: nil, hrid: nil)
49
- raise ArgumentError, "must pass exactly one of external_id or HRID" unless external_id.present? || hrid.present?
50
- raise ArgumentError, "must pass exactly one of external_id or HRID" if external_id.present? && hrid.present?
42
+ raise ArgumentError, 'must pass exactly one of external_id or HRID' unless external_id.present? || hrid.present?
43
+ raise ArgumentError, 'must pass exactly one of external_id or HRID' if external_id.present? && hrid.present?
51
44
 
52
45
  external_id ||= fetch_external_id(hrid: hrid)
53
46
  client.get("/inventory/instances/#{external_id}")
@@ -57,12 +50,12 @@ class FolioClient
57
50
  # @param status_id [String] uuid for an instance status code
58
51
  # @return true if instance status matches the uuid param, false otherwise
59
52
  # @raise [ResourceNotFound] if search by instance HRID returns 0 results
60
- def has_instance_status?(hrid:, status_id:)
53
+ def has_instance_status?(hrid:, status_id:) # rubocop:disable Naming/PredicateName
61
54
  # get the instance record and its statusId
62
- instance = client.get("/inventory/instances", {query: "hrid==#{hrid}"})
63
- raise ResourceNotFound, "No matching instance found for #{hrid}" if instance["totalRecords"] == 0
55
+ instance = client.get('/inventory/instances', { query: "hrid==#{hrid}" })
56
+ raise ResourceNotFound, "No matching instance found for #{hrid}" if (instance['totalRecords']).zero?
64
57
 
65
- instance_status_id = instance.dig("instances", 0, "statusId")
58
+ instance_status_id = instance.dig('instances', 0, 'statusId')
66
59
 
67
60
  return false unless instance_status_id
68
61
 
@@ -70,5 +63,11 @@ class FolioClient
70
63
 
71
64
  false
72
65
  end
66
+
67
+ private
68
+
69
+ def client
70
+ FolioClient.instance
71
+ end
73
72
  end
74
73
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "timeout"
4
- require "dry/monads"
3
+ require 'timeout'
4
+ require 'dry/monads'
5
5
 
6
6
  class FolioClient
7
7
  # Wraps operations waiting for results from jobs
@@ -10,10 +10,8 @@ class FolioClient
10
10
 
11
11
  attr_reader :job_execution_id
12
12
 
13
- # @param client [FolioClient] the configured client
14
13
  # @param job_execution_id [String] ID of the job to be checked on
15
- def initialize(client, job_execution_id:)
16
- @client = client
14
+ def initialize(job_execution_id:)
17
15
  @job_execution_id = job_execution_id
18
16
  end
19
17
 
@@ -27,7 +25,7 @@ class FolioClient
27
25
  def status
28
26
  response_hash = client.get("/change-manager/jobExecutions/#{job_execution_id}")
29
27
 
30
- return Failure(:pending) if !["COMMITTED", "ERROR"].include?(response_hash["status"])
28
+ return Failure(:pending) unless %w[COMMITTED ERROR].include?(response_hash['status'])
31
29
 
32
30
  Success()
33
31
  rescue ResourceNotFound
@@ -35,28 +33,33 @@ class FolioClient
35
33
  Failure(:not_found)
36
34
  end
37
35
 
38
- def wait_until_complete(wait_secs: default_wait_secs, timeout_secs: default_timeout_secs)
39
- wait_with_timeout(wait_secs: wait_secs, timeout_secs: timeout_secs) { status }
36
+ def wait_until_complete(wait_secs: default_wait_secs, timeout_secs: default_timeout_secs,
37
+ max_checks: default_max_checks)
38
+ wait_with_timeout(wait_secs: wait_secs, timeout_secs: timeout_secs, max_checks: max_checks) { status }
40
39
  end
41
40
 
41
+ # rubocop:disable Metrics/AbcSize
42
42
  def instance_hrids
43
43
  current_status = status
44
44
  return current_status unless current_status.success?
45
45
 
46
46
  @instance_hrids ||= wait_with_timeout do
47
47
  response = client
48
- .get("/metadata-provider/journalRecords/#{job_execution_id}")
49
- .fetch("journalRecords", [])
50
- .select { |journal_record| journal_record["entityType"] == "INSTANCE" && journal_record["actionStatus"] == "COMPLETED" }
51
- .filter_map { |instance_record| instance_record["entityHrId"] }
48
+ .get("/metadata-provider/journalRecords/#{job_execution_id}")
49
+ .fetch('journalRecords', [])
50
+ .select { |journal_record| journal_record['entityType'] == 'INSTANCE' && journal_record['actionStatus'] == 'COMPLETED' }
51
+ .filter_map { |instance_record| instance_record['entityHrId'] }
52
52
 
53
53
  response.empty? ? Failure() : Success(response)
54
54
  end
55
55
  end
56
+ # rubocop:enable Metrics/AbcSize
56
57
 
57
58
  private
58
59
 
59
- attr_reader :client
60
+ def client
61
+ FolioClient.instance
62
+ end
60
63
 
61
64
  def default_wait_secs
62
65
  1
@@ -66,13 +69,19 @@ class FolioClient
66
69
  10 * 60
67
70
  end
68
71
 
69
- def wait_with_timeout(wait_secs: default_wait_secs, timeout_secs: default_timeout_secs)
72
+ def default_max_checks
73
+ # arbitrary best guess at number of times to check for job status before erroring
74
+ 10
75
+ end
76
+
77
+ def wait_with_timeout(wait_secs: default_wait_secs, timeout_secs: default_timeout_secs,
78
+ max_checks: default_max_checks)
70
79
  Timeout.timeout(timeout_secs) do
71
80
  loop.with_index do |_, i|
72
81
  result = yield
73
82
 
74
83
  # If a 404, wait a bit longer before raising an error.
75
- check_not_found(result, i)
84
+ check_not_found(result, i, max_checks)
76
85
  return result if done_waiting?(result)
77
86
 
78
87
  sleep(wait_secs)
@@ -86,10 +95,11 @@ class FolioClient
86
95
  result.success? || (result.failure? && result.failure == :error)
87
96
  end
88
97
 
89
- def check_not_found(result, index)
90
- return unless result.failure? && result.failure == :not_found && index > 2
98
+ def check_not_found(result, index, max_checks)
99
+ return unless result.failure? && result.failure == :not_found && index > max_checks
91
100
 
92
- raise ResourceNotFound, "Job #{job_execution_id} not found after #{index} retries"
101
+ raise ResourceNotFound,
102
+ "Job #{job_execution_id} not found after #{index} retries. The data import job may still have completed."
93
103
  end
94
104
  end
95
105
  end
@@ -5,39 +5,38 @@ class FolioClient
5
5
  # https://s3.amazonaws.com/foliodocs/api/mod-organizations/p/organizations.html
6
6
  # https://s3.amazonaws.com/foliodocs/api/mod-organizations-storage/p/interface.html
7
7
  class Organizations
8
- attr_accessor :client
9
-
10
- # @param client [FolioClient] the configured client
11
- def initialize(client)
12
- @client = client
13
- end
14
-
15
8
  # @param query [String] an optional query to limit the number of organizations returned
16
9
  # @param limit [Integer] the number of results to return (defaults to 10,000)
17
10
  # @param offset [Integer] the offset for results returned (defaults to 0)
18
11
  # @param lang [String] language code for returned results (defaults to 'en')
19
- def fetch_list(query: nil, limit: 10000, offset: 0, lang: "en")
20
- params = {limit: limit, offset: offset, lang: lang}
12
+ def fetch_list(query: nil, limit: 10_000, offset: 0, lang: 'en')
13
+ params = { limit: limit, offset: offset, lang: lang }
21
14
  params[:query] = query if query
22
- client.get("/organizations/organizations", params)
15
+ client.get('/organizations/organizations', params)
23
16
  end
24
17
 
25
18
  # @param query [String] an optional query to limit the number of organization interfaces returned
26
19
  # @param limit [Integer] the number of results to return (defaults to 10,000)
27
20
  # @param offset [Integer] the offset for results returned (defaults to 0)
28
21
  # @param lang [String] language code for returned results (defaults to 'en')
29
- def fetch_interface_list(query: nil, limit: 10000, offset: 0, lang: "en")
30
- params = {limit: limit, offset: offset, lang: lang}
22
+ def fetch_interface_list(query: nil, limit: 10_000, offset: 0, lang: 'en')
23
+ params = { limit: limit, offset: offset, lang: lang }
31
24
  params[:query] = query if query
32
- client.get("/organizations-storage/interfaces", params)
25
+ client.get('/organizations-storage/interfaces', params)
33
26
  end
34
27
 
35
28
  # @param id [String] id for requested storage interface
36
29
  # @param lang [String] language code for returned result (defaults to 'en')
37
- def fetch_interface_details(id:, lang: "en")
30
+ def fetch_interface_details(id:, lang: 'en')
38
31
  client.get("/organizations-storage/interfaces/#{id}", {
39
- lang: lang
40
- })
32
+ lang: lang
33
+ })
34
+ end
35
+
36
+ private
37
+
38
+ def client
39
+ FolioClient.instance
41
40
  end
42
41
  end
43
42
  end
@@ -1,14 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FolioClient
4
+ # Edit MARC JSON records in Folio
4
5
  class RecordsEditor
5
- attr_accessor :client
6
-
7
- # @param client [FolioClient] the configured client
8
- def initialize(client)
9
- @client = client
10
- end
11
-
12
6
  # Given an HRID, retrieves the associated MARC JSON, yields it to the caller as a hash,
13
7
  # and attempts to re-save it, using optimistic locking to prevent accidental overwrite,
14
8
  # in case another user or process has updated the record in the time between retrieval and
@@ -18,24 +12,32 @@ class FolioClient
18
12
  # HRID; the updated hash will be saved when control is returned from the block.
19
13
  # @note in limited manual testing, optimistic locking behaved like so when two edit attempts collided:
20
14
  # * One updating client would eventually raise a timeout. This updating client would actually write successfully, and version the record.
21
- # * The other updating client would raise a StandardError, with a message like 'duplicate key value violates unique constraint \"idx_records_matched_id_gen\"'.
15
+ # * The other updating client would raise a StandardError, with a message like 'duplicate key value violates unique
16
+ # constraint \"idx_records_matched_id_gen\"'.
22
17
  # This client would fail to write.
23
18
  # * As opposed to the expected behavior of the "winner" getting a 200 ok response, and the "loser" getting a 409 conflict response.
24
19
  # @todo If this is a problem in practice, see if it's possible to have Folio respond in a more standard way; or, workaround with error handling.
25
20
  def edit_marc_json(hrid:)
26
21
  instance_info = client.fetch_instance_info(hrid: hrid)
27
22
 
28
- version = instance_info["_version"]
29
- external_id = instance_info["id"]
23
+ version = instance_info['_version']
24
+ external_id = instance_info['id']
30
25
 
31
- record_json = client.get("/records-editor/records", {externalId: external_id})
26
+ record_json = client.get('/records-editor/records', { externalId: external_id })
32
27
 
33
- parsed_record_id = record_json["parsedRecordId"]
34
- record_json["relatedRecordVersion"] = version # setting this field on the JSON we send back is what will allow optimistic locking to catch stale updates
28
+ parsed_record_id = record_json['parsedRecordId']
29
+ # setting this field on the JSON we send back is what will allow optimistic locking to catch stale updates
30
+ record_json['relatedRecordVersion'] = version
35
31
 
36
32
  yield record_json
37
33
 
38
34
  client.put("/records-editor/records/#{parsed_record_id}", record_json)
39
35
  end
36
+
37
+ private
38
+
39
+ def client
40
+ FolioClient.instance
41
+ end
40
42
  end
41
43
  end
@@ -5,26 +5,23 @@ class FolioClient
5
5
  class SourceStorage
6
6
  FIELDS_TO_REMOVE = %w[001 003].freeze
7
7
 
8
- attr_accessor :client
9
-
10
- # @param client [FolioClient] the configured client
11
- def initialize(client)
12
- @client = client
13
- end
14
-
15
8
  # get marc bib data from folio given an instance HRID
16
9
  # @param instance_hrid [String] the key to use for MARC lookup
17
10
  # @return [Hash] hash representation of the MARC. should be usable by MARC::Record.new_from_hash (from ruby-marc gem)
18
11
  # @raise [ResourceNotFound]
19
12
  # @raise [MultipleResourcesFound]
20
13
  def fetch_marc_hash(instance_hrid:)
21
- response_hash = client.get("/source-storage/source-records", {instanceHrid: instance_hrid})
14
+ response_hash = client.get('/source-storage/source-records', { instanceHrid: instance_hrid })
22
15
 
23
- record_count = response_hash["totalRecords"]
16
+ record_count = response_hash['totalRecords']
24
17
  raise ResourceNotFound, "No records found for #{instance_hrid}" if record_count.zero?
25
- raise MultipleResourcesFound, "Expected 1 record for #{instance_hrid}, but found #{record_count}" if record_count > 1
26
18
 
27
- response_hash["sourceRecords"].first["parsedRecord"]["content"]
19
+ if record_count > 1
20
+ raise MultipleResourcesFound,
21
+ "Expected 1 record for #{instance_hrid}, but found #{record_count}"
22
+ end
23
+
24
+ response_hash['sourceRecords'].first['parsedRecord']['content']
28
25
  end
29
26
 
30
27
  # get marc bib data as MARCXML from folio given an instance HRID
@@ -33,12 +30,20 @@ class FolioClient
33
30
  # @return [String] MARCXML string
34
31
  # @raise [ResourceNotFound]
35
32
  # @raise [MultipleResourcesFound]
33
+ # rubocop:disable Metrics/MethodLength
34
+ # rubocop:disable Metrics/AbcSize
36
35
  def fetch_marc_xml(instance_hrid: nil, barcode: nil)
37
- raise ArgumentError, "Either a barcode or a Folio instance HRID must be provided" if barcode.nil? && instance_hrid.nil?
36
+ if barcode.nil? && instance_hrid.nil?
37
+ raise ArgumentError,
38
+ 'Either a barcode or a Folio instance HRID must be provided'
39
+ end
38
40
 
39
41
  instance_hrid ||= client.fetch_hrid(barcode: barcode)
40
42
 
41
- raise ResourceNotFound, "Catalog record not found. HRID: #{instance_hrid} | Barcode: #{barcode}" if instance_hrid.blank?
43
+ if instance_hrid.blank?
44
+ raise ResourceNotFound,
45
+ "Catalog record not found. HRID: #{instance_hrid} | Barcode: #{barcode}"
46
+ end
42
47
 
43
48
  marc_record = MARC::Record.new_from_hash(
44
49
  fetch_marc_hash(instance_hrid: instance_hrid)
@@ -52,10 +57,18 @@ class FolioClient
52
57
  updated_marc.fields << field
53
58
  end
54
59
  # explicitly inject the instance_hrid into the 001 field
55
- updated_marc.fields << MARC::ControlField.new("001", instance_hrid)
60
+ updated_marc.fields << MARC::ControlField.new('001', instance_hrid)
56
61
  # explicitly inject FOLIO into the 003 field
57
- updated_marc.fields << MARC::ControlField.new("003", "FOLIO")
62
+ updated_marc.fields << MARC::ControlField.new('003', 'FOLIO')
58
63
  updated_marc.to_xml.to_s
59
64
  end
65
+ # rubocop:enable Metrics/MethodLength
66
+ # rubocop:enable Metrics/AbcSize
67
+
68
+ private
69
+
70
+ def client
71
+ FolioClient.instance
72
+ end
60
73
  end
61
74
  end
@@ -4,6 +4,8 @@ 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
+ # rubocop:disable Metrics/AbcSize
7
9
  def self.call(response)
8
10
  case response.status
9
11
  when 401
@@ -23,4 +25,6 @@ class FolioClient
23
25
  end
24
26
  end
25
27
  end
28
+ # rubocop:enable Metrics/MethodLength
29
+ # rubocop:enable Metrics/AbcSize
26
30
  end
@@ -4,29 +4,28 @@ class FolioClient
4
4
  # Query user records in Folio; see
5
5
  # https://s3.amazonaws.com/foliodocs/api/mod-users/r/users.html
6
6
  class Users
7
- attr_accessor :client
8
-
9
- # @param client [FolioClient] the configured client
10
- def initialize(client)
11
- @client = client
12
- end
13
-
14
7
  # @param query [String] an optional query to limit the number of users returned
15
8
  # @param limit [Integer] the number of results to return (defaults to 10,000)
16
9
  # @param offset [Integer] the offset for results returned (defaults to 0)
17
10
  # @param lang [String] language code for returned results (defaults to 'en')
18
- def fetch_list(query: nil, limit: 10000, offset: 0, lang: "en")
19
- params = {limit: limit, offset: offset, lang: lang}
11
+ def fetch_list(query: nil, limit: 10_000, offset: 0, lang: 'en')
12
+ params = { limit: limit, offset: offset, lang: lang }
20
13
  params[:query] = query if query
21
- client.get("/users", params)
14
+ client.get('/users', params)
22
15
  end
23
16
 
24
17
  # @param id [String] id for requested user
25
18
  # @param lang [String] language code for returned results (defaults to 'en')
26
- def fetch_user_details(id:, lang: "en")
19
+ def fetch_user_details(id:, lang: 'en')
27
20
  client.get("/users/#{id}", {
28
- lang: lang
29
- })
21
+ lang: lang
22
+ })
23
+ end
24
+
25
+ private
26
+
27
+ def client
28
+ FolioClient.instance
30
29
  end
31
30
  end
32
31
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FolioClient
4
- VERSION = "0.14.0"
4
+ VERSION = '0.16.0'
5
5
  end