Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DPL-865 handle sprint client response #2130

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5b3b2fe
Merge pull request #2095 from sanger/develop
yoldas Dec 3, 2024
a4837a2
Merge pull request #2107 from sanger/develop
dasunpubudumal Dec 9, 2024
23a1b9e
Merge pull request #2117 from sanger/develop
sdjmchattie Dec 11, 2024
7a88ad6
Bumping release version
dasunpubudumal Dec 17, 2024
9bd1c5d
Merge remote-tracking branch 'origin/master' into 1352-dpl-865-limber…
dasunpubudumal Dec 17, 2024
84fac59
Fixing linting issues
dasunpubudumal Dec 17, 2024
329d73e
Fixing the tests and linting issues
dasunpubudumal Dec 17, 2024
99dccee
Removing the redundant return
dasunpubudumal Dec 17, 2024
43dc23f
Adding docs
dasunpubudumal Dec 17, 2024
7969a0c
Making prettier happy
dasunpubudumal Dec 17, 2024
1b93608
Adding more error handling branches
dasunpubudumal Dec 17, 2024
636feab
Adding more error handling branches
dasunpubudumal Dec 17, 2024
8f88668
[skip ci] Modifying error messages
dasunpubudumal Dec 17, 2024
c6e22af
Adding proper test coverage
dasunpubudumal Dec 17, 2024
c0657de
Adding proper test coverage
dasunpubudumal Dec 17, 2024
3b80962
[skip ci] Adding proper test coverage
dasunpubudumal Dec 17, 2024
ea0f216
Merge remote-tracking branch 'origin/develop' into 1352-dpl-865-limbe…
dasunpubudumal Jan 20, 2025
4ef41ac
Updating the http data to use to_json to be more readable.
dasunpubudumal Jan 20, 2025
4a26c2f
Fixing rubocop issues
dasunpubudumal Jan 20, 2025
24da3f5
Fixing issues with tests
dasunpubudumal Jan 20, 2025
6018158
Prettier fixes
dasunpubudumal Jan 20, 2025
1c13ada
Refactoring error messages
dasunpubudumal Jan 20, 2025
82d94dd
Refactoring error messages
dasunpubudumal Jan 20, 2025
d7f9b7a
Prettier fixes
dasunpubudumal Jan 20, 2025
3a550df
Refactoring pj variable
dasunpubudumal Jan 20, 2025
31b6bb7
Adding better error names
dasunpubudumal Jan 20, 2025
3d7f4eb
Adding better error names
dasunpubudumal Jan 21, 2025
e8002a9
Merge remote-tracking branch 'origin/develop' into 1352-dpl-865-limbe…
dasunpubudumal Jan 21, 2025
5b78de3
Modify handling error messages
dasunpubudumal Jan 21, 2025
499bcf4
Modify handling error messages
dasunpubudumal Jan 21, 2025
75685ff
Merge remote-tracking branch 'origin/develop' into 1352-dpl-865-limbe…
dasunpubudumal Jan 27, 2025
a838885
Merge remote-tracking branch 'origin/develop' into 1352-dpl-865-limbe…
dasunpubudumal Jan 29, 2025
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
96 changes: 70 additions & 26 deletions app/models/print_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,32 +103,9 @@ def print_to_sprint

merge_fields_list = label_array * number_of_copies

# assumes all labels use the same label template
SPrintClient.send_print_request(printer_name, label_template, merge_fields_list)

# TODO: DPL-865 [Limber] Handle sprint client response
#
# This print_to_sprint call fails silently if there is an error. Instead
# of returning success, the result of the send_print_request call should
# be used to check the response status and response body. Examples:
#
# The following response body is returned in a 200 response.
# {"errors":[{"message":"Variable 'printRequest' has an invalid value: Expected
# type 'Int' but was
# 'Double'.","locations":[{"line":1,"column":16}],"extensions":{"classification":"
# ValidationError"}}]}
#
# The following response body is returned in a 500 response.
# {"timestamp": "2023-08-02T14:46:30.160+00:00","status": 500,"error": "Internal
# Server Error","path": "/graphql"}
#
# When sprint cannot log in to printer, the details are available in response body.
#
# A successful response has a job id in response body.
#
# Use errors.add to show proper feedback in the view.

true
response = SPrintClient.send_print_request(printer_name, label_template, merge_fields_list)

handle_sprint_response(response)
end

def number_of_copies=(number)
Expand All @@ -155,4 +132,71 @@ def get_label_template_by_service(print_service)
templates_by_service = JSON.parse(label_templates_by_service)
templates_by_service[print_service]
end

# Handles the response from the SPrintClient and checks for success.
# If the response is successful and contains a job ID, it returns true.
# Otherwise, it adds an error message to the errors object and returns false.
#
# The print_to_sprint call fails silently if there is an error. Instead
# of returning success, the result of the send_print_request call should
# be used to check the response status and response body. Examples:
#
# The following response body is returned in a 200 response.
# {"errors":[{"message":"Variable 'printRequest' has an invalid value: Expected
# type 'Int' but was
# 'Double'.","locations":[{"line":1,"column":16}],"extensions":{"classification":"
# ValidationError"}}]}
#
# The following response body is returned in a 500 response.
# {"timestamp": "2023-08-02T14:46:30.160+00:00","status": 500,"error": "Internal
# Server Error","path": "/graphql"}
#
# When sprint cannot log in to printer, the details are available in response body.
#
# A successful response has a job id in response body.
#
# Use errors.add to show proper feedback in the view.
#
# @param response [Net::HTTPResponse] The response object from the SPrintClient.
# @return [Boolean] True if the response is successful and contains a job ID, false otherwise.
def handle_sprint_response(response)
# Non-200 errors are treated as failures
# Ref: https://ruby-doc.org/stdlib-2.7.0/libdoc/net/http/rdoc/Net/HTTP.html#class-Net::HTTP-label-GET+with+Dynamic+Parameters
unless response.is_a?(Net::HTTPSuccess)
case response
when Net::HTTPUnprocessableEntity
errors.add(:sprint, 'Sprint could not understand the request. Please check the label data.')
when Net::HTTPInternalServerError
errors.add(:sprint, 'Internal server error at SPrint. Please try again later.')
else
errors.add(:sprint, 'Trouble connecting to SPrint. Please try again later.')
end
return false
end
if response.body.present? && response.body['jobId'].present?
true
else
errors.add(:sprint, extract_error_message(response))
false
end
end

def extract_error_message(response)
if response.body.present?
begin
response_body = JSON.parse(response.body)
if response_body['errors'].present?
messages =
response_body['errors'].map do |error|
extension = error['extensions'] ? " (#{error['extensions']['classification']})" : ''
"#{error['message']}#{extension}"
end
return messages.join(' - ')
end
rescue JSON::ParserError
return 'Failed to parse JSON response from SprintClient'
end
end
'Unknown error'
end
end
154 changes: 143 additions & 11 deletions spec/models/print_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -199,25 +199,157 @@
}
end

it 'will send a print request to SPrintClient' do
pj =
PrintJob.new(
printer_name: printer_sprint.name,
printer: printer_sprint,
label_templates_by_service: label_templates_by_service,
labels: [{ label: { barcode: '12345', test_attr: 'test' } }],
labels_sprint: labels_sprint,
number_of_copies: 1
)
let(:pj) do
PrintJob.new(
printer_name: printer_sprint.name,
printer: printer_sprint,
label_templates_by_service: label_templates_by_service,
labels: [{ label: { barcode: '12345', test_attr: 'test' } }],
labels_sprint: labels_sprint,
number_of_copies: 1
)
end

it 'will send a print request to SPrintClient' do
allow(pj).to receive(:get_label_template_by_service).and_return(label_template_name_sprint)
allow(SPrintClient).to receive(:send_print_request).and_return('a response')
response = Net::HTTPSuccess.new(1.0, '200', 'OK')
response.instance_variable_set(:@read, true)
response.instance_variable_set(
:@body,
{ data: { print: { jobId: 'psd-2:68b27056-11cf-41ff-9b22-bdf6121a95be' } } }.to_json
)
allow(SPrintClient).to receive(:send_print_request).and_return(response)
expect(SPrintClient).to receive(:send_print_request).with(
printer_sprint.name,
label_template_name_sprint,
labels_sprint.values
)
expect(pj.print_to_sprint).to eq(true)
end

it 'will not execute if the SPrintClient is down' do
response = Net::HTTPBadGateway.new(1.0, '502', nil)
dasunpubudumal marked this conversation as resolved.
Show resolved Hide resolved
response.instance_variable_set(:@read, true)
allow(SPrintClient).to receive(:send_print_request).and_return(response)
expect(pj.execute).to be false
expect(pj.errors.full_messages[0]).to eq('Sprint Trouble connecting to SPrint. Please try again later.')
end

it 'will not execute if the SPrintClient returns unprocessable entity' do
response = Net::HTTPUnprocessableEntity.new(1.0, '422', nil)
response.instance_variable_set(:@read, true)
allow(SPrintClient).to receive(:send_print_request).and_return(response)
expect(pj.execute).to be false
expect(pj.errors.full_messages[0]).to eq(
'Sprint Sprint could not understand the request. Please check the label data.'
)
end

it 'will not execute if the SPrintClient returns unprocessable entity' do
response = Net::HTTPInternalServerError.new(1.0, '500', nil)
response.instance_variable_set(:@read, true)
allow(SPrintClient).to receive(:send_print_request).and_return(response)
expect(pj.execute).to be false
expect(pj.errors.full_messages[0]).to eq('Sprint Internal server error at SPrint. Please try again later.')
end

it 'will not execute if the SPrintClient sends a valid error with code 200' do
response = Net::HTTPSuccess.new(1.0, '200', nil)
response.instance_variable_set(:@read, true)
response.instance_variable_set(
:@body,
{
errors: [
{
message: "Variable 'printRequest' has an invalid value: Expected type 'Int' but was 'Double'.",
locations: [{ line: 1, column: 16 }],
extensions: {
classification: 'ValidationError'
}
}
]
}.to_json
)
allow(SPrintClient).to receive(:send_print_request).and_return(response)
expect(pj.execute).to be false
expect(pj.errors.full_messages[0]).to eq(
"Sprint Variable 'printRequest' has an invalid value: Expected type 'Int' but was 'Double'. (ValidationError)"
)
end

it 'will not execute if the SPrintClient sends an invalid error with code 200' do
response = Net::HTTPSuccess.new(1.0, '200', nil)
response.instance_variable_set(:@read, true)
response.instance_variable_set(
:@body,
{
errors: [
{
message: 'Failed to parse JSON response from SprintClient',
locations: [{ line: 1, column: 16 }],
extensions: {
classification: 'ValidationError'
}
}
]
}.to_json
)
allow(SPrintClient).to receive(:send_print_request).and_return(response)
expect(pj.execute).to be false
expect(pj.errors.full_messages[0]).to eq(
'Sprint Failed to parse JSON response from SprintClient (ValidationError)'
)
end
end

describe '#extract_error_message' do
let(:print_job) { PrintJob.new }

it 'returns the error message with extensions' do
response =
instance_double(
'Net::HTTPResponse',
body: {
errors: [
{
message: "Variable 'printRequest' has an invalid value: Expected type 'Int' but was 'Double'.",
extensions: {
classification: 'ValidationError'
}
}
]
}.to_json
)

expect(print_job.send(:extract_error_message, response)).to eq(
"Variable 'printRequest' has an invalid value: Expected type 'Int' but was 'Double'. (ValidationError)"
)
end

it 'returns the error message without extensions' do
response =
instance_double(
'Net::HTTPResponse',
body: {
errors: [{ message: "Variable 'printRequest' has an invalid value: Expected type 'Int' but was 'Double'." }]
}.to_json
)

expect(print_job.send(:extract_error_message, response)).to eq(
"Variable 'printRequest' has an invalid value: Expected type 'Int' but was 'Double'."
)
end

it 'returns unknown error if response body is empty' do
response = instance_double('Net::HTTPResponse', body: nil)

expect(print_job.send(:extract_error_message, response)).to eq('Unknown error')
end

it 'returns failed to parse JSON response if JSON parsing fails' do
response = instance_double('Net::HTTPResponse', body: 'invalid_json')

expect(print_job.send(:extract_error_message, response)).to eq('Failed to parse JSON response from SprintClient')
end
end
end
Loading