Skip to content

Commit 8c06e25

Browse files
authored
Do not override message errors and use magic message or default instead (#316)
1 parent 70b4af9 commit 8c06e25

10 files changed

Lines changed: 46 additions & 85 deletions

File tree

gems/smithy-client/lib/smithy-client/rpc_v2_cbor/error_handler.rb

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ def call(context)
1010
# 200 range for error handling only for this case.
1111
@handler.call(context).on_done(200..599) do |response|
1212
if !valid_response?(context)
13-
code, message, data = http_status_error(context)
14-
response.error = build_error(context, code, message, data)
15-
elsif (300..599).cover?(context.http_response.status_code)
13+
code, data = http_status_error(context)
14+
response.error = build_error(context, code, data)
15+
elsif (400..599).cover?(context.http_response.status_code)
1616
response.error = error(context)
1717
end
1818
end
@@ -26,60 +26,52 @@ def valid_response?(context)
2626
req_header == resp_header
2727
end
2828

29-
# TODO: Fix this
30-
# This is not correct per protocol tests. Some headers will determine the error code.
31-
# If the body is empty, there is still potentially an error code from the header, but
32-
# we are making a generic http status error instead. In a new major version, we should
33-
# always try to extract header, and during extraction, check headers and body.
3429
def error(context)
3530
body = context.http_response.body.read
3631
if body.empty?
37-
code, message, data = http_status_error(context)
32+
code, data = http_status_error(context)
3833
else
39-
code, message, data = extract_error(body, context)
34+
code, data = extract_error(body, context)
4035
end
41-
build_error(context, code, message, data)
36+
build_error(context, code, data)
4237
end
4338

4439
def extract_error(body, context)
4540
data = CBOR.decode(body)
46-
type = data['__type']
47-
code = error_code(type, context)
48-
message = data['message']
49-
data = parse_error_data(context, body, type)
50-
[code, message, data]
41+
code = error_code(context, data)
42+
data = parse_error_data(context, body, code)
43+
[code, data]
5144
rescue CBOR::Error
52-
[http_status_error_code(context), '', Schema::EmptyStructure.new]
45+
[http_status_error_code(context), Schema::EmptyStructure.new]
5346
end
5447

5548
def parse_error_data(context, body, code)
5649
data = Schema::EmptyStructure.new
5750
context.operation.errors.each do |ref|
58-
next unless ref.shape.id == code
51+
next unless ref.shape.name == code
5952

6053
data = context.config.cbor_codec.deserialize(ref, body, ref.shape.type.new)
6154
end
6255
data
6356
end
6457

65-
def error_code(type, context)
66-
return type.split('#').last if type
67-
68-
http_status_error_code(context)
58+
def error_code(context, data)
59+
code = data['__type']
60+
code ||= http_status_error_code(context)
61+
code.split('#').last.split('$').first
6962
end
7063

71-
def build_error(context, code, message, data)
64+
def build_error(context, code, data)
7265
errors_module = context.client.class.errors_module
73-
errors_module.error_class(code).new(context, message, data)
66+
errors_module.error_class(code).new(context, data)
7467
end
7568

7669
def http_status_error(context)
77-
[http_status_error_code(context), '', Schema::EmptyStructure.new]
70+
[http_status_error_code(context), Schema::EmptyStructure.new]
7871
end
7972

8073
def http_status_error_code(context)
81-
status_code = context.http_response.status_code
82-
"HTTP#{status_code}Error"
74+
"HTTP#{context.http_response.status_code}Error"
8375
end
8476
end
8577
end

gems/smithy-client/lib/smithy-client/service_error.rb

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@ module Client
88
# service and not one generated by the client.
99
class ServiceError < RuntimeError
1010
# @param [Smithy::Client::HandlerContext] context
11-
# @param [String] message
1211
# @param [Structure] data
13-
def initialize(context, message, data)
12+
def initialize(context, data)
1413
@code = self.class.code
1514
@context = context
16-
@message = extract_message(message, data)
1715
@data = data
18-
super(@message)
16+
message = data.message if data.respond_to?(:message)
17+
super(message)
1918
end
2019

2120
# @return [String, nil] The error code returned by the service.
@@ -25,10 +24,6 @@ def initialize(context, message, data)
2524
# that triggered the remote service to return this error.
2625
attr_reader :context
2726

28-
# @return [String] The error message returned by the service.
29-
# Defaults to the class name if no message is provided or can be parsed.
30-
attr_reader :message
31-
3227
# @return [Structure] Additional data returned by the service.
3328
attr_accessor :data
3429

@@ -46,12 +41,6 @@ def retryable?
4641
def throttling?
4742
false
4843
end
49-
50-
private
51-
52-
def extract_message(message, data)
53-
message || (data.message if data.respond_to?(:message)) || self.class.name.to_s
54-
end
5544
end
5645
end
5746
end

gems/smithy-client/lib/smithy-client/stubbing/rpc_v2_cbor.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def stub_error(_config, error_code)
1919
response.status_code = 400
2020
response.headers['Smithy-Protocol'] = 'rpc-v2-cbor'
2121
response.headers['Content-Type'] = 'application/cbor'
22-
data = { '__type' => error_code, 'message' => 'stubbed-error-message' }
22+
data = { '__type' => "smithy.ruby.tests##{error_code}", 'message' => 'stubbed-error-message' }
2323
response.body = CBOR.encode(data)
2424
response
2525
end

gems/smithy-client/sig/smithy-client/service_error.rbs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
module Smithy
22
module Client
33
class ServiceError < RuntimeError
4-
def initialize: (HandlerContext?, String?, untyped?) -> void
4+
def initialize: (HandlerContext, Schema::Structure) -> void
55

66
attr_reader code: String?
7-
attr_reader context: HandlerContext?
8-
attr_reader message: String?
7+
attr_reader context: HandlerContext
98
attr_accessor data: untyped?
109

1110
def self.code: () -> String?

gems/smithy-client/spec/smithy-client/http/error_inspector_spec.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@ module Smithy
44
module Client
55
module HTTP
66
describe ErrorInspector do
7-
let(:error) do
8-
ServiceError.new(HandlerContext.new, 'error', {})
9-
end
10-
let(:status_code) { 404 }
7+
let(:error) { ServiceError.new(HandlerContext.new, Schema::EmptyStructure.new) }
118
let(:http_response) { HTTP::Response.new(status_code: status_code) }
9+
let(:status_code) { 404 }
1210

1311
subject { ErrorInspector.new(error, http_response) }
1412

gems/smithy-client/spec/smithy-client/plugins/retry_errors_spec.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ module Plugins
8989
config.build!
9090
end
9191

92-
let(:response) { Response.new(context: HandlerContext.new(config: config)) }
93-
let(:service_error) { ServiceError.new(nil, nil, nil) }
92+
let(:context) { HandlerContext.new(config: config) }
93+
let(:response) { Response.new(context: context) }
94+
let(:service_error) { ServiceError.new(context, Schema::EmptyStructure.new) }
9495

9596
let(:retry_strategy) { config.retry_strategy }
9697
let(:quota) { retry_strategy.instance_variable_get(:@quota) }

gems/smithy-client/spec/smithy-client/service_error_spec.rb

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,38 @@ module Smithy
66
module Client
77
describe ServiceError do
88
let(:context) { HandlerContext.new }
9-
let(:message) { 'message' }
109
let(:data) { double('Structure') }
1110
let(:code) { 'ServiceErrorCode' }
1211

13-
subject { ServiceError.new(context, message, data) }
12+
subject { ServiceError.new(context, data) }
1413

15-
before do
16-
ServiceError.code = code
17-
end
14+
before { ServiceError.code = code }
1815

1916
it 'is a subclass of RuntimeError' do
2017
expect(ServiceError.superclass).to be(RuntimeError)
2118
end
2219

2320
describe '#initialize' do
24-
it 'sets the code' do
21+
it 'sets the code using the class accessor' do
2522
expect(subject.code).to be(ServiceError.code)
2623
end
2724

2825
it 'sets the context' do
2926
expect(subject.context).to be(context)
3027
end
3128

32-
it 'sets the message' do
33-
expect(subject.message).to be(message)
34-
end
35-
36-
it 'defaults the message to the class name' do
37-
error = ServiceError.new(context, nil, data)
38-
expect(error.message).to include('ServiceError')
39-
end
40-
4129
it 'sets the data' do
4230
expect(subject.data).to be(data)
4331
end
4432

45-
it 'calls super with the message' do
46-
expect { raise subject }.to raise_error(RuntimeError, message)
33+
it 'parses a message from data' do
34+
data = double('Structure', message: 'message')
35+
subject = ServiceError.new(context, data)
36+
expect(subject.message).to be('message')
37+
end
38+
39+
it 'defaults the message to the class name' do
40+
expect(subject.message).to include('ServiceError')
4741
end
4842
end
4943

gems/smithy/lib/smithy/templates/client/errors.erb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,17 @@ module <%= module_name %>
3737
<% end -%>
3838
class <%= error.name %> < Smithy::Client::ServiceError
3939
# @param [Smithy::Client::HandlerContext] context
40-
# @param [String] message
4140
# @param [<%= module_name %>::Types::<%= error.name %>] data
42-
def initialize(context, message, data = Smithy::Schema::EmptyStructure.new)
43-
super(context, message, data)
41+
def initialize(context, data = Smithy::Schema::EmptyStructure.new)
42+
super(context, data)
4443
end
4544
<% error.members.each do |member| -%>
4645

4746
<% member.docstrings.each do |docstring| -%>
4847
# <%= docstring %>
4948
<% end -%>
5049
def <%= member.name %>
51-
<%= '@message || ' if member.message? %>@data[:<%= member.name %>]
50+
@data[:<%= member.name %>]
5251
end
5352
<% end -%>
5453
<% if error.retryable? -%>

gems/smithy/lib/smithy/views/client/errors.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,6 @@ def initialize(name, shape)
6666

6767
attr_reader :shape
6868

69-
def message?
70-
@name == 'message'
71-
end
72-
7369
def docstrings
7470
@shape
7571
.fetch('traits', {})

gems/smithy/spec/interfaces/client/errors_spec.rb

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,15 @@
4949

5050
it 'generates errors with messages from data' do
5151
data = Errors::Types::ServiceError.new(message: 'message')
52-
error = Errors::Errors::ServiceError.new(nil, nil, data)
52+
error = Errors::Errors::ServiceError.new(nil, data)
5353
expect(error.message).to eq('message')
5454
expect { raise error }.to raise_error(Errors::Errors::ServiceError, 'message')
5555
end
5656

57-
it 'allows overriding the message' do
58-
data = Errors::Types::ServiceError.new(message: 'message')
59-
error = Errors::Errors::ServiceError.new(nil, 'new message', data)
60-
expect(error.message).to eq('new message')
61-
expect { raise error }.to raise_error(Errors::Errors::ServiceError, 'new message')
62-
end
63-
6457
it 'can return data members' do
6558
structure = Errors::Types::Structure.new(value: 'foo')
6659
data = Errors::Types::ServiceError.new(structure: structure)
67-
error = Errors::Errors::ServiceError.new(nil, nil, data)
60+
error = Errors::Errors::ServiceError.new(nil, data)
6861
expect(error.data.structure.value).to eq('foo')
6962
end
7063

0 commit comments

Comments
 (0)