Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion app/jobs/dspace_publication_results_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class DspacePublicationResultsJob < ActiveJob::Base
MAX_MESSAGES = ENV.fetch('SQS_RESULT_MAX_MESSAGES', 10)
WAIT_TIME_SECONDS = ENV.fetch('SQS_RESULT_WAIT_TIME_SECONDS', 10)
IDLE_TIMEOUT = ENV.fetch('SQS_RESULT_IDLE_TIMEOUT', 0)
MAX_ERROR_FIELD_LENGTH = 500
MAX_TRACEBACK_LENGTH = 1000

queue_as :default

Expand Down Expand Up @@ -124,7 +126,7 @@ def update_publication_status(thesis, body, results, status)
when 'success'
update_handle(thesis, body, results)
when 'error'
error = body['DSpaceResponse']
error = format_dss_error(body)
thesis.publication_status = 'Publication error'
thesis.save
Rails.logger.info("Thesis #{thesis.id} updated to status #{thesis.publication_status}. Error from DSS: #{error}")
Expand All @@ -138,6 +140,42 @@ def update_publication_status(thesis, body, results, status)
end
end

def format_dss_error(body)
details = []
details << format_error_detail('ErrorInfo', body['ErrorInfo']) if body['ErrorInfo'].present?
details << format_error_detail('DSpaceResponse', body['DSpaceResponse']) if body['DSpaceResponse'].present?
details << format_error_detail('ExceptionMessage', body['ExceptionMessage']) if body['ExceptionMessage'].present?

traceback = format_traceback(body['ExceptionTraceback'])
details << format_error_detail('ExceptionTraceback', traceback, max_length: MAX_TRACEBACK_LENGTH) if traceback.present?

return 'No error details provided by DSS' if details.empty?

details.join(' | ')
Comment thread
jazairi marked this conversation as resolved.
end

def format_error_detail(label, value, max_length: MAX_ERROR_FIELD_LENGTH)
normalized = value.to_s.gsub(/\s+/, ' ').strip
if normalized.length > max_length
normalized = "#{normalized[0...max_length]}...(truncated)"
end

"#{label}: #{normalized}"
end

def format_traceback(traceback)
lines = if traceback.is_a?(Array)
traceback
elsif traceback.is_a?(String)
traceback.split(/\r?\n/)
else
[]
end

lines = lines.map { |line| line.to_s.strip }
lines.reject(&:blank?).first(5).join(' || ')
end

def poll_messages(queue_url, results)
# https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/sqs-example-poll-messages.html
# Poller retrieves messages until there are none left and deletes them as it goes
Expand Down
28 changes: 28 additions & 0 deletions test/jobs/dspace_publication_results_job_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,34 @@ def teardown
'validate checksums as no local files were attached to the record. This ' \
'requires staff to manually check the ETD record and DSpace record and take ' \
'appropriate action.'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire test, not just the new assertions, is very confusing. It looks like we have stubbed an entire run and in this test are checking all the possible error states that the stubs have provided us.

If that's the case, since you didn't update any stubs are these new assertions testing things that were already there or are they testing the new formatting you put in place?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! Previously, this stub would produce an empty/nil error message because we were only reading DSpaceResponse. So yes, the new assertions are testing new behavior, but it's really hard to discern that in the current test state.

Let me know if the change I just pushed helps clarify the intended test behavior.

end

test 'DSS error fields are surfaced in results errors' do
Aws.config[:sqs] = {
stub_responses: {
receive_message: [
{
messages: [
{ message_id: 'id1', receipt_handle: 'handle1',
body: '{"ResultType": "error", "ErrorInfo": "Stuff broke", "DSpaceResponse": "N/A", "ExceptionMessage": "500 Server Error: Internal Server Error", "ExceptionTraceback": "Traceback (most recent call last):\nFile submission.py, line 84, in submit\nrequests.exceptions.HTTPError: 500 Server Error"}',
message_attributes: { 'PackageID' => { string_value: "etd_#{@bad_thesis.id}", data_type: 'String' },
'SubmissionSource' => { string_value: 'ETD', data_type: 'String' } } }
]
},
{ messages: [] }
]
}
}

results = DspacePublicationResultsJob.perform_now

dss_error = results[:errors].find { |e| e.include?('ErrorInfo:') }
assert_not_nil dss_error, 'Expected DSS error details to be surfaced in results[:errors]'
assert_includes dss_error, 'ErrorInfo: Stuff broke'
assert_includes dss_error, 'DSpaceResponse: N/A'
assert_includes dss_error, 'ExceptionMessage: 500 Server Error: Internal Server Error'
assert_includes dss_error, 'ExceptionTraceback: Traceback (most recent call last):'
end

test 'enqueues preservation submission prep job' do
Expand Down
Loading