folio_client 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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, max_checks: default_max_checks)
36
+ def wait_until_complete(wait_secs: default_wait_secs, timeout_secs: default_timeout_secs,
37
+ max_checks: default_max_checks)
39
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
@@ -71,7 +74,8 @@ class FolioClient
71
74
  10
72
75
  end
73
76
 
74
- def wait_with_timeout(wait_secs: default_wait_secs, timeout_secs: default_timeout_secs, max_checks: default_max_checks)
77
+ def wait_with_timeout(wait_secs: default_wait_secs, timeout_secs: default_timeout_secs,
78
+ max_checks: default_max_checks)
75
79
  Timeout.timeout(timeout_secs) do
76
80
  loop.with_index do |_, i|
77
81
  result = yield
@@ -94,7 +98,8 @@ class FolioClient
94
98
  def check_not_found(result, index, max_checks)
95
99
  return unless result.failure? && result.failure == :not_found && index > max_checks
96
100
 
97
- raise ResourceNotFound, "Job #{job_execution_id} not found after #{index} retries. The data import job may still have completed."
101
+ raise ResourceNotFound,
102
+ "Job #{job_execution_id} not found after #{index} retries. The data import job may still have completed."
98
103
  end
99
104
  end
100
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.15.0"
4
+ VERSION = '0.16.0'
5
5
  end