diff --git a/README.md b/README.md index 3229451..5dd1fd7 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,9 @@ client.find_instance_record(instance_record_id: 'some-instance-record-id') client.find_instance_record(instance_record_hrid: 'some-instance-record-hrid') client.find_source_record(instance_record_id: 'some-instance-record-id') client.find_source_record(instance_record_hrid: 'some-instance-record-hrid') +client.find_source_marc_records(modified_since: '2025-01-01T00:00:00Z') { |marc_record_hash| } +client.find_source_marc_records(with_965_value: '965abc') { |marc_record_hash| } +client.find_source_marc_records(modified_since: '2025-01-01T00:00:00Z', with_965_value: '965abc') { |marc_record_hash| } # Convert a FOLIO MARC source record to a marc gem MARC::Record object: source_record = client.find_source_record(instance_record_id: 'some-instance-record-id') diff --git a/lib/folio_api_client/finders.rb b/lib/folio_api_client/finders.rb index 463e6ab..c1fc4f8 100644 --- a/lib/folio_api_client/finders.rb +++ b/lib/folio_api_client/finders.rb @@ -90,8 +90,8 @@ def find_source_record(instance_record_id: nil, instance_record_hrid: nil) source_record_search_results['sourceRecords'].first end - def find_source_marc_records(modified_since, has_965hyacinth: false, &block) - query = marc_records_query(modified_since: modified_since, has_965hyacinth: has_965hyacinth) + def find_source_marc_records(modified_since: nil, with_965_value: nil, &block) + query = marc_records_query(modified_since: modified_since, with_965_value: with_965_value) loop do response = self.get('search/instances', query) @@ -105,6 +105,8 @@ def find_source_marc_records(modified_since, has_965hyacinth: false, &block) def process_marc_for_instance(instances, &block) instances.each do |instance| source_record = find_source_record(instance_record_id: instance['id']) + next if source_record.nil? # Occasionally, we find an instance record without a source record. Skip these. + marc_content = source_record.dig('parsedRecord', 'content') yield(marc_content) if marc_content && block end @@ -133,17 +135,22 @@ def location_record_query(code: nil) 'Missing query field. Must supply a code.' end - def marc_records_query(modified_since: nil, has_965hyacinth: false) + def marc_records_query(modified_since: nil, with_965_value: nil) # rubocop:disable Metrics/MethodLength params = { limit: 100, offset: 0 } - if modified_since.nil? && !has_965hyacinth + if modified_since.nil? && with_965_value.nil? raise FolioApiClient::Exceptions::MissingQueryFieldError, - 'Missing query field. Must supply either modified_since or has_965hyacinth=true.' + 'Missing query field. Must supply either modified_since or with_965_value.' + end + + if modified_since && !modified_since.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/) + raise ArgumentError, + %(Invalid format for modified_since argument. Must be a date string like "2025-10-03T16:49:00Z".) end query_parts = [] query_parts << "metadata.updatedDate>=\"#{modified_since}\"" if modified_since - query_parts << 'identifiers.value="965hyacinth"' if has_965hyacinth + query_parts << %(identifiers.value="#{with_965_value}") if with_965_value params[:query] = query_parts.join(' and ') params diff --git a/spec/folio_api_client/finders_spec.rb b/spec/folio_api_client/finders_spec.rb index 452db47..1103c07 100644 --- a/spec/folio_api_client/finders_spec.rb +++ b/spec/folio_api_client/finders_spec.rb @@ -333,6 +333,7 @@ describe '#find_source_marc_records' do let(:modified_since) { '2025-01-01T00:00:00Z' } + let(:value_965) { '965hyacinth' } let(:marc_content) { { 'fields' => [{ '001' => '12345' }] } } let(:instance_record) { { 'id' => 'instance-123' } } let(:instances_response) { { 'totalRecords' => 1, 'instances' => [instance_record] } } @@ -350,50 +351,56 @@ it 'yields MARC content for each source record' do yielded_records = [] - instance.find_source_marc_records(modified_since) { |record| yielded_records << record } + instance.find_source_marc_records(modified_since: modified_since) { |record| yielded_records << record } expect(yielded_records).to eq([marc_content]) end + + it 'raises an exception if the given modified_since parameter is an invalid format' do + expect { + instance.find_source_marc_records(modified_since: 'banana') { |_| } + }.to raise_error(ArgumentError) + end end - context 'with has_965hyacinth parameter' do + context 'with with_965_value parameter' do before do allow(instance).to receive(:get).with( - 'search/instances', { query: 'identifiers.value="965hyacinth"', limit: 100, offset: 0 } + 'search/instances', { query: %(identifiers.value="#{value_965}"), limit: 100, offset: 0 } ).and_return(instances_response) end it 'uses the correct query' do - instance.find_source_marc_records(nil, has_965hyacinth: true) { |_| } + instance.find_source_marc_records(with_965_value: value_965) { |_| } expect(instance).to have_received(:get).with( 'search/instances', { query: 'identifiers.value="965hyacinth"', limit: 100, offset: 0 } ) end end - context 'with both parameters' do + context 'with modified_since and with_965_value parameters' do before do allow(instance).to receive(:get).with( 'search/instances', { - query: "metadata.updatedDate>=\"#{modified_since}\" and identifiers.value=\"965hyacinth\"", + query: %(metadata.updatedDate>="#{modified_since}" and identifiers.value="#{value_965}"), limit: 100, offset: 0 } ).and_return(instances_response) end it 'combines both filters' do - instance.find_source_marc_records(modified_since, has_965hyacinth: true) { |_| } + instance.find_source_marc_records(modified_since: modified_since, with_965_value: value_965) { |_| } expect(instance).to have_received(:get).with( 'search/instances', { - query: "metadata.updatedDate>=\"#{modified_since}\" and identifiers.value=\"965hyacinth\"", + query: %(metadata.updatedDate>="#{modified_since}" and identifiers.value="#{value_965}"), limit: 100, offset: 0 } ) end end - it 'raises error when no parameters provided' do + it 'raises an error when no parameters provided' do expect { - instance.find_source_marc_records(nil) { |_| } + instance.find_source_marc_records { |_| } }.to raise_error(FolioApiClient::Exceptions::MissingQueryFieldError) end end