From ee676627a54332c8842a93e906a6925a7b9a5781 Mon Sep 17 00:00:00 2001 From: Alex Woods Date: Wed, 21 Aug 2024 10:50:20 -0700 Subject: [PATCH] Make all errors transport agnostic. Push error parsing into parsers intead of error classes. --- .../rails_json/lib/rails_json/errors.rb | 18 +-- .../rails_json/lib/rails_json/middleware.rb | 130 ++++++++-------- .../rails_json/sig/rails_json/errors.rbs | 8 +- .../rpcv2_cbor/lib/rpcv2_cbor/errors.rb | 24 ++- .../rpcv2_cbor/lib/rpcv2_cbor/middleware.rb | 26 ++-- .../white_label/lib/white_label/errors.rb | 48 ++---- .../lib/white_label/event_stream.rb | 29 +++- .../white_label/lib/white_label/middleware.rb | 50 +++--- .../white_label/lib/white_label/parsers.rb | 33 ++-- .../white_label/sig/white_label/errors.rbs | 30 +--- .../white_label/spec/errors_spec.rb | 51 +----- .../spec/event_stream_stubs_spec.rb | 4 +- .../protocol/generators/ParserGenerator.java | 13 +- .../integration-specs/errors_spec.rb | 51 +----- .../event_stream_stubs_spec.rb | 4 +- .../amazon/smithy/ruby/codegen/Hearth.java | 5 - .../generators/ErrorsGeneratorBase.java | 147 +++--------------- .../generators/EventStreamGenerator.java | 53 ++++++- .../generators/ParserGeneratorBase.java | 4 +- .../factories/ParseMiddlewareFactory.java | 4 +- .../lib/hearth/event_stream/handler_base.rb | 4 +- hearth/lib/hearth/http.rb | 1 - hearth/lib/hearth/http/api_error.rb | 25 --- hearth/lib/hearth/http/error_parser.rb | 26 ++-- hearth/spec/hearth/http/api_error_spec.rb | 50 ------ 25 files changed, 296 insertions(+), 542 deletions(-) delete mode 100644 hearth/lib/hearth/http/api_error.rb delete mode 100644 hearth/spec/hearth/http/api_error_spec.rb diff --git a/codegen/projections/rails_json/lib/rails_json/errors.rb b/codegen/projections/rails_json/lib/rails_json/errors.rb index 8d898a33d..5b65874f4 100644 --- a/codegen/projections/rails_json/lib/rails_json/errors.rb +++ b/codegen/projections/rails_json/lib/rails_json/errors.rb @@ -14,18 +14,16 @@ def self.error_code(resp) end # Base class for all errors returned by this service - class ApiError < Hearth::HTTP::ApiError; end + class ApiError < Hearth::ApiError; end # Base class for all errors returned where the client is at fault. - # These are generally errors with 4XX HTTP status codes. class ApiClientError < ApiError; end # Base class for all errors returned where the server is at fault. - # These are generally errors with 5XX HTTP status codes. class ApiServerError < ApiError; end # Base class for all errors returned where the service returned - # a 3XX redirection. + # a redirection. class ApiRedirectError < ApiError def initialize(location:, **kwargs) @location = location @@ -37,11 +35,11 @@ def initialize(location:, **kwargs) end class ComplexError < ApiClientError - def initialize(http_resp:, **kwargs) - @data = Parsers::ComplexError.parse(http_resp) + def initialize(data:, **kwargs) + @data = data kwargs[:message] = @data.message if @data.respond_to?(:message) - super(http_resp: http_resp, **kwargs) + super(**kwargs) end # @return [Types::ComplexError] @@ -49,11 +47,11 @@ def initialize(http_resp:, **kwargs) end class InvalidGreeting < ApiClientError - def initialize(http_resp:, **kwargs) - @data = Parsers::InvalidGreeting.parse(http_resp) + def initialize(data:, **kwargs) + @data = data kwargs[:message] = @data.message if @data.respond_to?(:message) - super(http_resp: http_resp, **kwargs) + super(**kwargs) end # @return [Types::InvalidGreeting] diff --git a/codegen/projections/rails_json/lib/rails_json/middleware.rb b/codegen/projections/rails_json/lib/rails_json/middleware.rb index 64faeb114..0b6cf9007 100644 --- a/codegen/projections/rails_json/lib/rails_json/middleware.rb +++ b/codegen/projections/rails_json/lib/rails_json/middleware.rb @@ -47,7 +47,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -98,7 +98,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -149,7 +149,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -200,7 +200,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -251,7 +251,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -302,7 +302,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -353,7 +353,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -404,7 +404,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -459,7 +459,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -514,7 +514,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -565,7 +565,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -616,7 +616,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [Errors::InvalidGreeting, Errors::ComplexError] + error_parsers: [Parsers::InvalidGreeting, Parsers::ComplexError] ) ) stack.use(Middleware::RequestId) @@ -667,7 +667,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -719,7 +719,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -770,7 +770,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -821,7 +821,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -872,7 +872,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -923,7 +923,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -974,7 +974,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1025,7 +1025,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1076,7 +1076,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1127,7 +1127,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1178,7 +1178,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1229,7 +1229,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1280,7 +1280,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1331,7 +1331,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1382,7 +1382,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1433,7 +1433,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1484,7 +1484,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1535,7 +1535,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1586,7 +1586,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1637,7 +1637,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1688,7 +1688,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1739,7 +1739,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1790,7 +1790,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1841,7 +1841,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1892,7 +1892,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1943,7 +1943,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -1994,7 +1994,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2045,7 +2045,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2096,7 +2096,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2147,7 +2147,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2198,7 +2198,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2249,7 +2249,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2300,7 +2300,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2351,7 +2351,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2402,7 +2402,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2453,7 +2453,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2510,7 +2510,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2561,7 +2561,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2612,7 +2612,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2663,7 +2663,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2714,7 +2714,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2765,7 +2765,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2816,7 +2816,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2867,7 +2867,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2917,7 +2917,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -2968,7 +2968,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -3018,7 +3018,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -3069,7 +3069,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -3120,7 +3120,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -3171,7 +3171,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -3222,7 +3222,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -3273,7 +3273,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) @@ -3324,7 +3324,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Middleware::RequestId) diff --git a/codegen/projections/rails_json/sig/rails_json/errors.rbs b/codegen/projections/rails_json/sig/rails_json/errors.rbs index bb504b138..271833001 100644 --- a/codegen/projections/rails_json/sig/rails_json/errors.rbs +++ b/codegen/projections/rails_json/sig/rails_json/errors.rbs @@ -10,9 +10,9 @@ module RailsJson module Errors - def self.error_code: (Hearth::HTTP::Response) -> ::String? + def self.error_code: (Hearth::Response) -> ::String? - class ApiError < Hearth::HTTP::ApiError + class ApiError < Hearth::ApiError end class ApiClientError < ApiError @@ -28,13 +28,13 @@ module RailsJson end class ComplexError < ApiClientError - def initialize: (http_resp: Hearth::HTTP::Response, **untyped kwargs) -> void + def initialize: (data: Types::ComplexError, **untyped kwargs) -> void attr_reader data: Types::ComplexError end class InvalidGreeting < ApiClientError - def initialize: (http_resp: Hearth::HTTP::Response, **untyped kwargs) -> void + def initialize: (data: Types::InvalidGreeting, **untyped kwargs) -> void attr_reader data: Types::InvalidGreeting end diff --git a/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/errors.rb b/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/errors.rb index b4a2e2a69..5f4739c69 100644 --- a/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/errors.rb +++ b/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/errors.rb @@ -24,18 +24,16 @@ def self.error_code(resp) end # Base class for all errors returned by this service - class ApiError < Hearth::HTTP::ApiError; end + class ApiError < Hearth::ApiError; end # Base class for all errors returned where the client is at fault. - # These are generally errors with 4XX HTTP status codes. class ApiClientError < ApiError; end # Base class for all errors returned where the server is at fault. - # These are generally errors with 5XX HTTP status codes. class ApiServerError < ApiError; end # Base class for all errors returned where the service returned - # a 3XX redirection. + # a redirection. class ApiRedirectError < ApiError def initialize(location:, **kwargs) @location = location @@ -47,11 +45,11 @@ def initialize(location:, **kwargs) end class ComplexError < ApiClientError - def initialize(http_resp:, **kwargs) - @data = Parsers::ComplexError.parse(http_resp) + def initialize(data:, **kwargs) + @data = data kwargs[:message] = @data.message if @data.respond_to?(:message) - super(http_resp: http_resp, **kwargs) + super(**kwargs) end # @return [Types::ComplexError] @@ -59,11 +57,11 @@ def initialize(http_resp:, **kwargs) end class InvalidGreeting < ApiClientError - def initialize(http_resp:, **kwargs) - @data = Parsers::InvalidGreeting.parse(http_resp) + def initialize(data:, **kwargs) + @data = data kwargs[:message] = @data.message if @data.respond_to?(:message) - super(http_resp: http_resp, **kwargs) + super(**kwargs) end # @return [Types::InvalidGreeting] @@ -71,11 +69,11 @@ def initialize(http_resp:, **kwargs) end class ValidationException < ApiClientError - def initialize(http_resp:, **kwargs) - @data = Parsers::ValidationException.parse(http_resp) + def initialize(data:, **kwargs) + @data = data kwargs[:message] = @data.message if @data.respond_to?(:message) - super(http_resp: http_resp, **kwargs) + super(**kwargs) end # @return [Types::ValidationException] diff --git a/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/middleware.rb b/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/middleware.rb index 84b09d0eb..796d1da82 100644 --- a/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/middleware.rb +++ b/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/middleware.rb @@ -45,7 +45,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -95,7 +95,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -145,7 +145,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -195,7 +195,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [Errors::InvalidGreeting, Errors::ComplexError] + error_parsers: [Parsers::InvalidGreeting, Parsers::ComplexError] ) ) stack.use(Hearth::Middleware::Send, @@ -245,7 +245,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -295,7 +295,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [Errors::ValidationException] + error_parsers: [Parsers::ValidationException] ) ) stack.use(Hearth::Middleware::Send, @@ -345,7 +345,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -395,7 +395,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -445,7 +445,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [Errors::ValidationException] + error_parsers: [Parsers::ValidationException] ) ) stack.use(Hearth::Middleware::Send, @@ -495,7 +495,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [Errors::ValidationException] + error_parsers: [Parsers::ValidationException] ) ) stack.use(Hearth::Middleware::Send, @@ -545,7 +545,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [Errors::ValidationException] + error_parsers: [Parsers::ValidationException] ) ) stack.use(Hearth::Middleware::Send, @@ -595,7 +595,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -645,7 +645,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, diff --git a/codegen/projections/white_label/lib/white_label/errors.rb b/codegen/projections/white_label/lib/white_label/errors.rb index 558329793..36bb96d0e 100644 --- a/codegen/projections/white_label/lib/white_label/errors.rb +++ b/codegen/projections/white_label/lib/white_label/errors.rb @@ -14,18 +14,16 @@ def self.error_code(resp) end # Base class for all errors returned by this service - class ApiError < Hearth::HTTP::ApiError; end + class ApiError < Hearth::ApiError; end # Base class for all errors returned where the client is at fault. - # These are generally errors with 4XX HTTP status codes. class ApiClientError < ApiError; end # Base class for all errors returned where the server is at fault. - # These are generally errors with 5XX HTTP status codes. class ApiServerError < ApiError; end # Base class for all errors returned where the service returned - # a 3XX redirection. + # a redirection. class ApiRedirectError < ApiError def initialize(location:, **kwargs) @location = location @@ -37,11 +35,11 @@ def initialize(location:, **kwargs) end class ClientError < ApiClientError - def initialize(http_resp:, **kwargs) - @data = Parsers::ClientError.parse(http_resp) + def initialize(data:, **kwargs) + @data = data kwargs[:message] = @data.message if @data.respond_to?(:message) - super(http_resp: http_resp, **kwargs) + super(**kwargs) end # @return [Types::ClientError] @@ -53,11 +51,11 @@ def retryable? end class ServerError < ApiServerError - def initialize(http_resp:, **kwargs) - @data = Parsers::ServerError.parse(http_resp) + def initialize(data:, **kwargs) + @data = data kwargs[:message] = @data.message if @data.respond_to?(:message) - super(http_resp: http_resp, **kwargs) + super(**kwargs) end # @return [Types::ServerError] @@ -73,40 +71,16 @@ def throttling? end class ServerErrorEvent < ApiServerError - def initialize(http_resp:, **kwargs) - @data = Parsers::ServerErrorEvent.parse(http_resp) + def initialize(data:, **kwargs) + @data = data kwargs[:message] = @data.message if @data.respond_to?(:message) - super(http_resp: http_resp, **kwargs) + super(**kwargs) end # @return [Types::ServerErrorEvent] attr_reader :data end - module EventStream - - # Base class for all event errors returned by this service - class Error < Hearth::ApiError; end - - # Base class for event errors where the client is at fault. - class ClientError < Error; end - - # Base class for event errors where the server is at fault. - class ServerError < Error; end - - class ServerErrorEvent < ServerError - def initialize(event:, **kwargs) - @data = event - kwargs[:message] = @data.message if @data.respond_to?(:message) - - super(error_code: 'ServerErrorEvent', **kwargs) - end - - # @return [Types::ServerErrorEvent] - attr_reader :data - end - end - end end diff --git a/codegen/projections/white_label/lib/white_label/event_stream.rb b/codegen/projections/white_label/lib/white_label/event_stream.rb index 6cc0e2e5c..490edd7bb 100644 --- a/codegen/projections/white_label/lib/white_label/event_stream.rb +++ b/codegen/projections/white_label/lib/white_label/event_stream.rb @@ -82,20 +82,37 @@ def on_unknown_event(&block) def parse_event(type, message) case type - when 'initial-response' then Parsers::EventStream::StartEventStreamInitialResponse.parse(message) - when 'SimpleEvent' then Types::Events::SimpleEvent.new(Parsers::EventStream::SimpleEvent.parse(message)) - when 'NestedEvent' then Types::Events::NestedEvent.new(Parsers::EventStream::NestedEvent.parse(message)) - when 'ExplicitPayloadEvent' then Types::Events::ExplicitPayloadEvent.new(Parsers::EventStream::ExplicitPayloadEvent.parse(message)) - when 'ServerErrorEvent' then Types::Events::ServerErrorEvent.new(Parsers::EventStream::ServerErrorEvent.parse(message)) + when 'initial-response' + Parsers::EventStream::StartEventStreamInitialResponse.parse(message) + when 'SimpleEvent' + Types::Events::SimpleEvent.new(Parsers::EventStream::SimpleEvent.parse(message)) + when 'NestedEvent' + Types::Events::NestedEvent.new(Parsers::EventStream::NestedEvent.parse(message)) + when 'ExplicitPayloadEvent' + Types::Events::ExplicitPayloadEvent.new(Parsers::EventStream::ExplicitPayloadEvent.parse(message)) + when 'ServerErrorEvent' + Types::Events::ServerErrorEvent.new(Parsers::EventStream::ServerErrorEvent.parse(message)) else Types::Events::Unknown.new(name: type || 'unknown', value: message) end end + def parse_exception_event(type, message) + case type + when 'ServerErrorEvent' + data = Parsers::EventStream::ServerErrorEvent.parse(message) + Errors::ServerErrorEvent.new(data: data, error_code: 'WhiteLabel::Types::Events::ServerErrorEvent', metadata: {message: message}) + else + data = Types::Events::Unknown.new(name: type || 'unknown', value: message) + Errors::ApiError.new(error_code: type || 'unknown', metadata: {data: data, message: message}) + end + end + def parse_error_event(message) error_code = message.headers.delete(':error-code')&.value error_message = message.headers.delete(':error-message')&.value - Errors::EventStream::Error.new(error_code: error_code, message: error_message) + metadata = {message: message} + Errors::ApiError.new(error_code: error_code, metadata: metadata, message: error_message) end end diff --git a/codegen/projections/white_label/lib/white_label/middleware.rb b/codegen/projections/white_label/lib/white_label/middleware.rb index 2665f8058..7069e7de5 100644 --- a/codegen/projections/white_label/lib/white_label/middleware.rb +++ b/codegen/projections/white_label/lib/white_label/middleware.rb @@ -56,7 +56,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -114,7 +114,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -172,7 +172,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -234,7 +234,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -296,7 +296,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -354,7 +354,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -412,7 +412,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -470,7 +470,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -528,7 +528,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -586,7 +586,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [Errors::ClientError, Errors::ServerError] + error_parsers: [Parsers::ClientError, Parsers::ServerError] ) ) stack.use(Hearth::Middleware::Send, @@ -644,7 +644,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -702,7 +702,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -760,7 +760,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -818,7 +818,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -876,7 +876,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -934,7 +934,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -995,7 +995,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -1060,7 +1060,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -1123,7 +1123,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -1181,7 +1181,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -1294,7 +1294,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -1352,7 +1352,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -1410,7 +1410,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -1468,7 +1468,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, @@ -1526,7 +1526,7 @@ def self.build(config, options = {}) error_parser: Hearth::HTTP::ErrorParser.new( error_module: Errors, success_status: 200, - errors: [] + error_parsers: [] ) ) stack.use(Hearth::Middleware::Send, diff --git a/codegen/projections/white_label/lib/white_label/parsers.rb b/codegen/projections/white_label/lib/white_label/parsers.rb index 6d243ec97..c8e21f955 100644 --- a/codegen/projections/white_label/lib/white_label/parsers.rb +++ b/codegen/projections/white_label/lib/white_label/parsers.rb @@ -13,13 +13,14 @@ module Parsers # Error Parser for ClientError class ClientError - def self.parse(http_resp) + def self.parse(http_resp, **kwargs) data = Types::ClientError.new body = http_resp.body.read - return data if body.empty? - map = Hearth::JSON.parse(body) - data.message = map['Message'] - data + unless body.empty? + map = Hearth::JSON.parse(body) + data.message = map['Message'] + end + Errors::ClientError.new(data: data, **kwargs) end end @@ -347,25 +348,27 @@ def self.parse(map) # Error Parser for ServerError class ServerError - def self.parse(http_resp) + def self.parse(http_resp, **kwargs) data = Types::ServerError.new body = http_resp.body.read - return data if body.empty? - map = Hearth::JSON.parse(body) - data + unless body.empty? + map = Hearth::JSON.parse(body) + end + Errors::ServerError.new(data: data, **kwargs) end end # Error Parser for ServerErrorEvent class ServerErrorEvent - def self.parse(http_resp) + def self.parse(http_resp, **kwargs) data = Types::ServerErrorEvent.new body = http_resp.body.read - return data if body.empty? - map = Hearth::JSON.parse(body) - data.nested = (NestedStructure.parse(map['nested']) unless map['nested'].nil?) - data.message = map['message'] - data + unless body.empty? + map = Hearth::JSON.parse(body) + data.nested = (NestedStructure.parse(map['nested']) unless map['nested'].nil?) + data.message = map['message'] + end + Errors::ServerErrorEvent.new(data: data, **kwargs) end end diff --git a/codegen/projections/white_label/sig/white_label/errors.rbs b/codegen/projections/white_label/sig/white_label/errors.rbs index 808955796..6f00efac6 100644 --- a/codegen/projections/white_label/sig/white_label/errors.rbs +++ b/codegen/projections/white_label/sig/white_label/errors.rbs @@ -10,9 +10,9 @@ module WhiteLabel module Errors - def self.error_code: (Hearth::HTTP::Response) -> ::String? + def self.error_code: (Hearth::Response) -> ::String? - class ApiError < Hearth::HTTP::ApiError + class ApiError < Hearth::ApiError end class ApiClientError < ApiError @@ -28,14 +28,14 @@ module WhiteLabel end class ClientError < ApiClientError - def initialize: (http_resp: Hearth::HTTP::Response, **untyped kwargs) -> void + def initialize: (data: Types::ClientError, **untyped kwargs) -> void attr_reader data: Types::ClientError def retryable?: () -> true end class ServerError < ApiServerError - def initialize: (http_resp: Hearth::HTTP::Response, **untyped kwargs) -> void + def initialize: (data: Types::ServerError, **untyped kwargs) -> void attr_reader data: Types::ServerError def retryable?: () -> true @@ -43,28 +43,10 @@ module WhiteLabel end class ServerErrorEvent < ApiServerError - def initialize: (http_resp: Hearth::HTTP::Response, **untyped kwargs) -> void + def initialize: (data: Types::ServerErrorEvent, **untyped kwargs) -> void attr_reader data: Types::ServerErrorEvent end - module EventStream - class Error < Hearth::ApiError - end - - class ClientError < Error - end - - class ServerError < Error - end - - class ServerErrorEvent < ServerError - def initialize: (event: Types::ServerErrorEvent, **untyped kwargs) -> void - - # @return [Types::ServerErrorEvent] - attr_reader data: Types::ServerErrorEvent - end - end - - end end +end diff --git a/codegen/projections/white_label/spec/errors_spec.rb b/codegen/projections/white_label/spec/errors_spec.rb index 42b81ed79..884a4bab4 100644 --- a/codegen/projections/white_label/spec/errors_spec.rb +++ b/codegen/projections/white_label/spec/errors_spec.rb @@ -6,17 +6,15 @@ module WhiteLabel module Errors describe ApiError do it 'inherits the base protocol api error' do - http_resp = Hearth::HTTP::Response.new - error = ApiError.new(http_resp: http_resp, metadata: {}, + error = ApiError.new(metadata: {}, error_code: 'error') - expect(error).to be_a(Hearth::HTTP::ApiError) + expect(error).to be_a(Hearth::ApiError) end end describe ApiClientError do it 'inherits the base client api error' do - http_resp = Hearth::HTTP::Response.new - error = ApiClientError.new(http_resp: http_resp, metadata: {}, + error = ApiClientError.new(metadata: {}, error_code: 'error') expect(error).to be_a(ApiError) end @@ -24,8 +22,7 @@ module Errors describe ApiServerError do it 'inherits the base client api error' do - http_resp = Hearth::HTTP::Response.new - error = ApiServerError.new(http_resp: http_resp, metadata: {}, + error = ApiServerError.new(metadata: {}, error_code: 'error') expect(error).to be_a(ApiError) end @@ -33,10 +30,8 @@ module Errors describe ApiRedirectError do it 'inherits the base client api error' do - http_resp = Hearth::HTTP::Response.new error = ApiRedirectError.new( location: 'location', - http_resp: http_resp, metadata: {}, error_code: 'error' ) @@ -44,9 +39,7 @@ module Errors end it 'stores a location' do - http_resp = Hearth::HTTP::Response.new error = ApiRedirectError.new( - http_resp: http_resp, location: 'location', error_code: 'error', metadata: {}, @@ -57,24 +50,8 @@ module Errors end describe ClientError do - it 'parses the data with a modeled message' do - http_resp = Hearth::HTTP::Response.new( - body: StringIO.new('error message') - ) - data = Types::ClientError.new(message: 'error message') - # don't test fakeProtocol parsers - expect(Parsers::ClientError).to receive(:parse) - .with(http_resp).and_return(data) - error = ClientError.new(http_resp: http_resp, metadata: {}, - error_code: 'error') - expect(error.data).to eq(data) - expect(error).to be_a(ApiClientError) - expect(error.message).to eq('error message') - end - it 'is retryable without throttling' do - http_resp = Hearth::HTTP::Response.new - error = ClientError.new(http_resp: http_resp, metadata: {}, + error = ClientError.new(data: nil, metadata: {}, error_code: 'error') expect(error.retryable?).to be true expect(error.throttling?).to be false @@ -82,24 +59,8 @@ module Errors end describe ServerError do - it 'parses the data without a modeled message' do - http_resp = Hearth::HTTP::Response.new( - body: StringIO.new('hidden error message') - ) - data = Types::ServerError.new - # don't test fakeProtocol parsers - expect(Parsers::ServerError).to receive(:parse) - .with(http_resp).and_return(data) - error = ServerError.new(http_resp: http_resp, metadata: {}, - error_code: 'error') - expect(error.data).to eq(data) - expect(error).to be_a(ApiServerError) - expect(error.message).to eq('WhiteLabel::Errors::ServerError') - end - it 'is retryable with throttling' do - http_resp = Hearth::HTTP::Response.new - error = ServerError.new(http_resp: http_resp, metadata: {}, + error = ServerError.new(data: nil, metadata: {}, error_code: 'error') expect(error.retryable?).to be true expect(error.throttling?).to be true diff --git a/codegen/projections/white_label/spec/event_stream_stubs_spec.rb b/codegen/projections/white_label/spec/event_stream_stubs_spec.rb index 811b9909e..3f227e0ad 100644 --- a/codegen/projections/white_label/spec/event_stream_stubs_spec.rb +++ b/codegen/projections/white_label/spec/event_stream_stubs_spec.rb @@ -75,7 +75,7 @@ error_events = 0 event_handler.on_error do |error| error_events += 1 - expect(error).to be_a(WhiteLabel::Errors::EventStream::Error) + expect(error).to be_a(WhiteLabel::Errors::ApiError) expect(error.error_code).to eq(error_code) expect(error.message).to eq(error_message) end @@ -287,7 +287,7 @@ subject.stub_responses( :start_event_stream, WhiteLabel::Errors::ClientError.new( - http_resp: Hearth::HTTP2::Response.new, + data: nil, error_code: 'ClientError' ) ) diff --git a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ParserGenerator.java b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ParserGenerator.java index 3ad682942..87e862c98 100644 --- a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ParserGenerator.java +++ b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ParserGenerator.java @@ -184,14 +184,15 @@ protected void renderOperationParseMethod(OperationShape operation, Shape output @Override protected void renderErrorParseMethod(Shape shape) { writer - .openBlock("def self.parse(http_resp)") + .openBlock("def self.parse(http_resp, **kwargs)") .write("data = $T.new", context.symbolProvider().toSymbol(shape)) .write("body = http_resp.body.read") - .write("return data if body.empty?") - .write("map = $T.parse(body)", Hearth.JSON); - renderMemberParsers(shape); - writer - .write("data") + .openBlock("unless body.empty?") + .write("map = $T.parse(body)", Hearth.JSON) + .call(() -> renderMemberParsers(shape)) + .closeBlock("end") + .write("Errors::$L.new(data: data, **kwargs)", + context.symbolProvider().toSymbol(shape).getName()) .closeBlock("end"); } diff --git a/codegen/smithy-ruby-codegen-test/integration-specs/errors_spec.rb b/codegen/smithy-ruby-codegen-test/integration-specs/errors_spec.rb index 42b81ed79..884a4bab4 100644 --- a/codegen/smithy-ruby-codegen-test/integration-specs/errors_spec.rb +++ b/codegen/smithy-ruby-codegen-test/integration-specs/errors_spec.rb @@ -6,17 +6,15 @@ module WhiteLabel module Errors describe ApiError do it 'inherits the base protocol api error' do - http_resp = Hearth::HTTP::Response.new - error = ApiError.new(http_resp: http_resp, metadata: {}, + error = ApiError.new(metadata: {}, error_code: 'error') - expect(error).to be_a(Hearth::HTTP::ApiError) + expect(error).to be_a(Hearth::ApiError) end end describe ApiClientError do it 'inherits the base client api error' do - http_resp = Hearth::HTTP::Response.new - error = ApiClientError.new(http_resp: http_resp, metadata: {}, + error = ApiClientError.new(metadata: {}, error_code: 'error') expect(error).to be_a(ApiError) end @@ -24,8 +22,7 @@ module Errors describe ApiServerError do it 'inherits the base client api error' do - http_resp = Hearth::HTTP::Response.new - error = ApiServerError.new(http_resp: http_resp, metadata: {}, + error = ApiServerError.new(metadata: {}, error_code: 'error') expect(error).to be_a(ApiError) end @@ -33,10 +30,8 @@ module Errors describe ApiRedirectError do it 'inherits the base client api error' do - http_resp = Hearth::HTTP::Response.new error = ApiRedirectError.new( location: 'location', - http_resp: http_resp, metadata: {}, error_code: 'error' ) @@ -44,9 +39,7 @@ module Errors end it 'stores a location' do - http_resp = Hearth::HTTP::Response.new error = ApiRedirectError.new( - http_resp: http_resp, location: 'location', error_code: 'error', metadata: {}, @@ -57,24 +50,8 @@ module Errors end describe ClientError do - it 'parses the data with a modeled message' do - http_resp = Hearth::HTTP::Response.new( - body: StringIO.new('error message') - ) - data = Types::ClientError.new(message: 'error message') - # don't test fakeProtocol parsers - expect(Parsers::ClientError).to receive(:parse) - .with(http_resp).and_return(data) - error = ClientError.new(http_resp: http_resp, metadata: {}, - error_code: 'error') - expect(error.data).to eq(data) - expect(error).to be_a(ApiClientError) - expect(error.message).to eq('error message') - end - it 'is retryable without throttling' do - http_resp = Hearth::HTTP::Response.new - error = ClientError.new(http_resp: http_resp, metadata: {}, + error = ClientError.new(data: nil, metadata: {}, error_code: 'error') expect(error.retryable?).to be true expect(error.throttling?).to be false @@ -82,24 +59,8 @@ module Errors end describe ServerError do - it 'parses the data without a modeled message' do - http_resp = Hearth::HTTP::Response.new( - body: StringIO.new('hidden error message') - ) - data = Types::ServerError.new - # don't test fakeProtocol parsers - expect(Parsers::ServerError).to receive(:parse) - .with(http_resp).and_return(data) - error = ServerError.new(http_resp: http_resp, metadata: {}, - error_code: 'error') - expect(error.data).to eq(data) - expect(error).to be_a(ApiServerError) - expect(error.message).to eq('WhiteLabel::Errors::ServerError') - end - it 'is retryable with throttling' do - http_resp = Hearth::HTTP::Response.new - error = ServerError.new(http_resp: http_resp, metadata: {}, + error = ServerError.new(data: nil, metadata: {}, error_code: 'error') expect(error.retryable?).to be true expect(error.throttling?).to be true diff --git a/codegen/smithy-ruby-codegen-test/integration-specs/event_stream_stubs_spec.rb b/codegen/smithy-ruby-codegen-test/integration-specs/event_stream_stubs_spec.rb index 811b9909e..3f227e0ad 100644 --- a/codegen/smithy-ruby-codegen-test/integration-specs/event_stream_stubs_spec.rb +++ b/codegen/smithy-ruby-codegen-test/integration-specs/event_stream_stubs_spec.rb @@ -75,7 +75,7 @@ error_events = 0 event_handler.on_error do |error| error_events += 1 - expect(error).to be_a(WhiteLabel::Errors::EventStream::Error) + expect(error).to be_a(WhiteLabel::Errors::ApiError) expect(error.error_code).to eq(error_code) expect(error.message).to eq(error_message) end @@ -287,7 +287,7 @@ subject.stub_responses( :start_event_stream, WhiteLabel::Errors::ClientError.new( - http_resp: Hearth::HTTP2::Response.new, + data: nil, error_code: 'ClientError' ) ) diff --git a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/Hearth.java b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/Hearth.java index e8be7cfc3..949b47499 100644 --- a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/Hearth.java +++ b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/Hearth.java @@ -66,11 +66,6 @@ public final class Hearth { .name("ApiError") .build(); - public static final Symbol HTTP_API_ERROR = Symbol.builder() - .namespace("Hearth::HTTP", "::") - .name("ApiError") - .build(); - public static final Symbol HTTP_ERROR_INSPECTOR = Symbol.builder() .namespace("Hearth::HTTP", "::") .name("ErrorInspector") diff --git a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/ErrorsGeneratorBase.java b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/ErrorsGeneratorBase.java index 6576f2083..94497456a 100644 --- a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/ErrorsGeneratorBase.java +++ b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/ErrorsGeneratorBase.java @@ -21,6 +21,7 @@ import java.util.Optional; import java.util.logging.Logger; import java.util.stream.Collectors; +import java.util.stream.Stream; import software.amazon.smithy.build.FileManifest; import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.model.Model; @@ -80,10 +81,6 @@ public abstract class ErrorsGeneratorBase { */ protected final List errorShapes; - protected final boolean outputEventStreams; - - protected final List eventErrorShapes; - /** * @param context generation context */ @@ -95,44 +92,34 @@ public ErrorsGeneratorBase(GenerationContext context) { this.rbsWriter = new RubyCodeWriter(context.settings().getModule() + "::Errors"); this.symbolProvider = context.symbolProvider(); this.errorShapes = getErrorShapes(); - this.eventErrorShapes = getEventErrorShapes(); - this.outputEventStreams = TopDownIndex.of(model).getContainedOperations(context.service()).stream() - .anyMatch(o -> Streaming.isEventStreaming(model, model.expectShape(o.getOutputShape()))); } /** - * @return list of all applicable (connected) error shapes. + * @return list of all applicable (connected) error shapes from operations and from event streams. */ protected List getErrorShapes() { TopDownIndex topDownIndex = TopDownIndex.of(model); - return topDownIndex.getContainedOperations(context.service()).stream() + Stream operationErrors = topDownIndex.getContainedOperations(context.service()).stream() .map(OperationShape::getErrors) .flatMap(Collection::stream) - .map(shapeId -> model.expectShape(shapeId, StructureShape.class)) - .collect(Collectors.toSet()).stream() // for uniqueness - .sorted(Comparator.comparing((o) -> o.getId().getName())) - .toList(); - } + .map(s -> model.expectShape(s, StructureShape.class)); - /** - * @return list of all error shapes from connected operations w/ output event streams. - */ - protected List getEventErrorShapes() { - TopDownIndex topDownIndex = TopDownIndex.of(model); - - return topDownIndex.getContainedOperations(context.service()).stream() + Stream eventErrors = topDownIndex.getContainedOperations(context.service()).stream() .map(o -> Streaming.getEventStreamMember(model, model.expectShape(o.getOutputShape()))) .filter(Optional::isPresent) .map(Optional::get) .map(m -> model.expectShape(m.getTarget())) .map(eventUnion -> Streaming.getEventStreamErrors(model, eventUnion).values()) - .flatMap(Collection::stream) + .flatMap(Collection::stream); + + return Stream.concat(operationErrors, eventErrors) .collect(Collectors.toSet()).stream() // for uniqueness .sorted(Comparator.comparing((o) -> o.getId().getName())) .toList(); } + /** * @param fileManifest fileManifest to use for writing. */ @@ -147,11 +134,6 @@ public void render(FileManifest fileManifest) { .closeBlock("end") .call(() -> renderBaseErrors()) .call(() -> renderModeledErrors()) - .call(() -> { - if (outputEventStreams) { - renderEventErrors(); - } - }) .write("") .closeBlock("end") .closeBlock("end"); @@ -172,12 +154,7 @@ public void renderRbs(FileManifest fileManifest) { .write("") .call(() -> renderRbsErrorCode()) .call(() -> renderRbsBaseErrors()) - .call(() -> renderRbsServiceErrors()) - .call(() -> { - if (outputEventStreams) { - renderRbsEventErrors(); - } - }) + .call(() -> renderRbsModeledErrors()) .write("") .closeBlock("end") .closeBlock("end"); @@ -188,19 +165,16 @@ public void renderRbs(FileManifest fileManifest) { LOGGER.fine("Wrote errors rbs to " + fileName); } - // TODO: the error module is currently protocol (http) specific private void renderBaseErrors() { writer .write("\n# Base class for all errors returned by this service") - .write("class ApiError < $T; end", Hearth.HTTP_API_ERROR) + .write("class ApiError < $T; end", Hearth.API_ERROR) .write("\n# Base class for all errors returned where the client is at fault.") - .write("# These are generally errors with 4XX HTTP status codes.") .write("class ApiClientError < ApiError; end") .write("\n# Base class for all errors returned where the server is at fault.") - .write("# These are generally errors with 5XX HTTP status codes.") .write("class ApiServerError < ApiError; end") .write("\n# Base class for all errors returned where the service returned") - .write("# a 3XX redirection.") + .write("# a redirection.") .openBlock("class ApiRedirectError < ApiError") .openBlock("def initialize(location:, **kwargs)") .write("@location = location") @@ -214,7 +188,7 @@ private void renderBaseErrors() { private void renderRbsBaseErrors() { rbsWriter - .write("\nclass ApiError < $T", Hearth.HTTP_API_ERROR) + .write("\nclass ApiError < $T", Hearth.API_ERROR) .write("end") .write("\nclass ApiClientError < ApiError") .write("end") @@ -243,7 +217,7 @@ private void renderRbsBaseErrors() { * Render RBS signature for error code. */ public void renderRbsErrorCode() { - rbsWriter.write("def self.error_code: (Hearth::HTTP::Response) -> ::String?"); + rbsWriter.write("def self.error_code: (Hearth::Response) -> ::String?"); } private void renderModeledErrors() { @@ -261,12 +235,13 @@ private void renderModeledError(StructureShape errorShape) { writer .write("") .openBlock("class $L < $L", symbolProvider.toSymbol(errorShape).getName(), apiErrorType) - .openBlock("def initialize(http_resp:, **kwargs)") - .write("@data = Parsers::$L.parse(http_resp)", symbolProvider.toSymbol(errorShape).getName()) + .openBlock("def initialize(data:, **kwargs)") + .write("@data = data") .write("kwargs[:message] = @data.message if @data.respond_to?(:message)\n") - .write("super(http_resp: http_resp, **kwargs)") + .write("super(**kwargs)") .closeBlock("end") .write("") + .writeYardReturn("Types::" + errorName, "") .write("attr_reader :data") .call(() -> { @@ -290,89 +265,13 @@ private void renderModeledError(StructureShape errorShape) { .closeBlock("end"); } - private void renderEventErrors() { - // event errors may be also be used as operation errors so must be in their own namespace. - writer - .write("") - .openBlock("module EventStream") - .write("\n# Base class for all event errors returned by this service") - .write("class Error < $T; end", Hearth.API_ERROR) - .write("\n# Base class for event errors where the client is at fault.") - .write("class ClientError < Error; end") - .write("\n# Base class for event errors where the server is at fault.") - .write("class ServerError < Error; end") - .write("") - .call(() -> { - for (StructureShape errorShape : eventErrorShapes) { - renderEventError(errorShape); - } - }) - .closeBlock("end"); - } - - private void renderEventError(StructureShape errorShape) { - String errorName = symbolProvider.toSymbol(errorShape).getName(); - String errorType = getEventErrorType(errorShape.expectTrait(ErrorTrait.class)); - - writer - .write("") - .openBlock("class $L < $L", errorName, errorType) - .openBlock("def initialize(event:, **kwargs)") - .write("@data = event") - .write("kwargs[:message] = @data.message if @data.respond_to?(:message)\n") - .write("super(error_code: '$L', **kwargs)", errorName) - .closeBlock("end") - .write("") - .writeYardReturn("Types::" + errorName, "") - .write("attr_reader :data") - .closeBlock("end"); - } - - private void renderRbsEventErrors() { - rbsWriter - .openBlock("module EventStream") - .write("\nclass Error < $T", Hearth.API_ERROR) - .write("end") - .write("\nclass ClientError < Error") - .write("end") - .write("\nclass ServerError < Error") - .write("end") - .call(() -> { - for (StructureShape errorShape : eventErrorShapes) { - renderRbsEventError(errorShape); - } - }) - .closeBlock("end"); - } - - private void renderRbsEventError(StructureShape errorShape) { - String errorName = symbolProvider.toSymbol(errorShape).getName(); - String errorType = getEventErrorType(errorShape.expectTrait(ErrorTrait.class)); - - rbsWriter - .write("") - .openBlock("class $L < $L", errorName, errorType) - .openBlock("def initialize: (event: Types::$L, **untyped kwargs) -> void\n", errorName) - .writeYardReturn("Types::" + errorName, "") - .write("attr_reader data: Types::$L", errorName) - .closeBlock("end"); - } - - private String getEventErrorType(ErrorTrait errorTrait) { - if (errorTrait.isClientError()) { - return "ClientError"; - } else { - return "ServerError"; - } - } - - private void renderRbsServiceErrors() { + private void renderRbsModeledErrors() { for (StructureShape errorShape : errorShapes) { - renderRbsServerError(errorShape); + renderRbsModeledError(errorShape); } } - private void renderRbsServerError(StructureShape errorShape) { + private void renderRbsModeledError(StructureShape errorShape) { String apiErrorType = getApiErrorType(errorShape.expectTrait(ErrorTrait.class)); String shapeName = symbolProvider.toSymbol(errorShape).getName(); boolean retryable = errorShape.hasTrait(RetryableTrait.class); @@ -381,7 +280,7 @@ private void renderRbsServerError(StructureShape errorShape) { rbsWriter .write("") .openBlock("class $L < $L", shapeName, apiErrorType) - .write("def initialize: (http_resp: Hearth::HTTP::Response, **untyped kwargs) -> void\n") + .write("def initialize: (data: Types::$L, **untyped kwargs) -> void\n", shapeName) .write("attr_reader data: Types::$L", shapeName) .call(() -> { if (retryable) { @@ -405,7 +304,7 @@ private String getApiErrorType(ErrorTrait errorTrait) { apiErrorType = "ApiServerError"; } else { // This should not happen but it's a safe fallback - apiErrorType = "Hearth::HTTP::ApiError"; + apiErrorType = "ApiError"; } return apiErrorType; diff --git a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/EventStreamGenerator.java b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/EventStreamGenerator.java index 7d1643b1b..55e47b329 100644 --- a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/EventStreamGenerator.java +++ b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/EventStreamGenerator.java @@ -116,6 +116,8 @@ private void renderEventStreamHandler(RubyCodeWriter writer, OperationShape oper .write("") .call(() -> renderParseEventMethod(writer, operation, eventStreamUnion)) .write("") + .call(() -> renderParseExceptionMethod(writer, eventStreamUnion)) + .write("") .call(() -> renderParseErrorEventMethod(writer)) .closeBlock("end"); } @@ -218,14 +220,21 @@ private void renderParseEventMethod( writer .openBlock("def parse_event(type, message)") .write("case type") - .write("when 'initial-response' then Parsers::EventStream::$LInitialResponse.parse(message)", + .write("when 'initial-response'") + .indent() + .write("Parsers::EventStream::$LInitialResponse.parse(message)", symbolProvider.toSymbol(operation).getName()) + .dedent() .call(() -> { for (MemberShape memberShape : eventStreamUnion.members()) { Shape target = model.expectShape(memberShape.getTarget()); - writer.write("when '$L' then $T.new(Parsers::EventStream::$L.parse(message))", - symbolProvider.toMemberName(memberShape), symbolProvider.toSymbol(memberShape), - symbolProvider.toSymbol(target).getName()); + writer + .write("when '$L'", symbolProvider.toMemberName(memberShape)) + .indent() + .write("$T.new(Parsers::EventStream::$L.parse(message))", + symbolProvider.toSymbol(memberShape), + symbolProvider.toSymbol(target).getName()) + .dedent(); } }) .openBlock("else") @@ -235,12 +244,46 @@ private void renderParseEventMethod( .closeBlock("end"); } + private void renderParseExceptionMethod( + RubyCodeWriter writer, UnionShape eventStreamUnion) { + writer + .openBlock("def parse_exception_event(type, message)") + .write("case type") + .call(() -> { + for (MemberShape memberShape : eventStreamUnion.members()) { + Shape target = model.expectShape(memberShape.getTarget()); + if (target.hasTrait(ErrorTrait.class)) { + writer + .write("when '$L'", symbolProvider.toMemberName(memberShape)) + .indent() + .write("data = Parsers::EventStream::$L.parse(message)", + symbolProvider.toSymbol(target).getName()) + .write("Errors::$L.new(data: data, error_code: '$L', " + + "metadata: {message: message})", + symbolProvider.toSymbol(target).getName(), + symbolProvider.toSymbol(memberShape)) + .dedent(); + } + + } + }) + .openBlock("else") + .write("data = $T::Unknown.new(name: type || 'unknown', value: message)", + symbolProvider.toSymbol(eventStreamUnion)) + .write("Errors::ApiError.new(error_code: type || 'unknown', " + + "metadata: {data: data, message: message})") + .closeBlock("end") + .closeBlock("end"); + } + private void renderParseErrorEventMethod(RubyCodeWriter writer) { writer .openBlock("def parse_error_event(message)") .write("error_code = message.headers.delete(':error-code')&.value") .write("error_message = message.headers.delete(':error-message')&.value") - .write("Errors::EventStream::Error.new(error_code: error_code, message: error_message)") + .write("metadata = {message: message}") + .write("Errors::ApiError.new(error_code: error_code, metadata: metadata, " + + "message: error_message)") .closeBlock("end"); } diff --git a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/ParserGeneratorBase.java b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/ParserGeneratorBase.java index 501f47b5e..c8d0d496a 100644 --- a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/ParserGeneratorBase.java +++ b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/ParserGeneratorBase.java @@ -205,13 +205,13 @@ public ParserGeneratorBase(GenerationContext context) { *
{@code
      * class ComplexError
      *   ### START code generated by this method
-     *   def self.parse(http_resp)
+     *   def self.parse(http_resp, **kwargs)
      *     data = Types::ComplexError.new
      *     data.header = http_resp.headers['X-Header']
      *     map = Hearth::JSON.parse(http_resp.body.read)
      *     data.top_level = map['TopLevel']
      *     data.nested = (Parsers::ComplexNestedErrorData.parse(map['Nested']) unless map['Nested'].nil?)
-     *     data
+     *     Errors::ComplexError.new(data: data, **kwargs)
      *   end
      *   ### END code generated by this method
      * end
diff --git a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/factories/ParseMiddlewareFactory.java b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/factories/ParseMiddlewareFactory.java
index 8742824f5..ab9deb084 100644
--- a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/factories/ParseMiddlewareFactory.java
+++ b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/factories/ParseMiddlewareFactory.java
@@ -50,14 +50,14 @@ public static Middleware build(GenerationContext context) {
                     }
                     String errors = operation.getErrors()
                             .stream()
-                            .map((error) -> "Errors::"
+                            .map((error) -> "Parsers::"
                                     + symbolProvider.toSymbol(ctx.model().expectShape(error)).getName())
                             .collect(Collectors.joining(", "));
                     String errorParser = """
                             Hearth::HTTP::ErrorParser.new(
                               error_module: Errors,
                               success_status: %s,
-                              errors: [%s]
+                              error_parsers: [%s]
                             )""".formatted(successCode, errors);
                     params.put("error_parser", errorParser);
                     return params;
diff --git a/hearth/lib/hearth/event_stream/handler_base.rb b/hearth/lib/hearth/event_stream/handler_base.rb
index e79c7fc4e..f2e599b5f 100644
--- a/hearth/lib/hearth/event_stream/handler_base.rb
+++ b/hearth/lib/hearth/event_stream/handler_base.rb
@@ -75,8 +75,8 @@ def parse_and_emit_error_event(message)
 
       def parse_and_emit_exception(message)
         type = message.headers.delete(':exception-type')&.value
-        event = parse_event(type, message)
-        emit_event(event.class, event)
+        error = parse_exception_event(type, message)
+        emit_error(error)
       end
 
       def on(type, callback)
diff --git a/hearth/lib/hearth/http.rb b/hearth/lib/hearth/http.rb
index d5ce5e8fe..8d6e05c76 100755
--- a/hearth/lib/hearth/http.rb
+++ b/hearth/lib/hearth/http.rb
@@ -1,7 +1,6 @@
 # frozen_string_literal: true
 
 require 'cgi'
-require_relative 'http/api_error'
 require_relative 'http/client'
 require_relative 'http/error_inspector'
 require_relative 'http/error_parser'
diff --git a/hearth/lib/hearth/http/api_error.rb b/hearth/lib/hearth/http/api_error.rb
deleted file mode 100644
index 8e04dadf2..000000000
--- a/hearth/lib/hearth/http/api_error.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-module Hearth
-  module HTTP
-    # Base class for HTTP errors returned from an API. Inherits from
-    # {Hearth::ApiError}.
-    class ApiError < Hearth::ApiError
-      def initialize(http_resp:, **kwargs)
-        @http_status = http_resp.status
-        @http_fields = http_resp.fields
-        @http_body = http_resp.body
-        super(**kwargs)
-      end
-
-      # @return [Integer]
-      attr_reader :http_status
-
-      # @return [Fields]
-      attr_reader :http_fields
-
-      # @return [IO]
-      attr_reader :http_body
-    end
-  end
-end
diff --git a/hearth/lib/hearth/http/error_parser.rb b/hearth/lib/hearth/http/error_parser.rb
index a82519e80..7f905c0d1 100644
--- a/hearth/lib/hearth/http/error_parser.rb
+++ b/hearth/lib/hearth/http/error_parser.rb
@@ -29,12 +29,12 @@ class ErrorParser
       #   it has the success_status and does not
       #   have an error code.
       #
-      # @param [Array>] errors Array of Error classes
-      #   modeled for the operation.
-      def initialize(error_module:, success_status:, errors:)
+      # @param [Array] error_parsers Array of modeled error parser
+      #   classes for the operation.
+      def initialize(error_module:, success_status:, error_parsers:)
         @error_module = error_module
         @success_status = success_status
-        @errors = errors
+        @error_parsers = error_parsers
       end
 
       # Parse and return the error if the response is not successful.
@@ -67,28 +67,26 @@ def error?(error_code, http_resp)
       end
 
       def create_error(error_code, http_resp, metadata)
-        error_class = error_class(error_code) if error_code
+        error_parser = error_parser(error_code) if error_code
 
         error_opts = {
-          http_resp: http_resp,
           error_code: error_code,
-          metadata: metadata,
+          metadata: metadata.merge(http_resp: http_resp),
           message: error_code # default message
         }
 
-        if error_class
-          error_class.new(**error_opts)
+        if error_parser
+          error_parser.parse(http_resp, **error_opts)
         else
-          generic_error(error_opts)
+          generic_error(http_resp, error_opts)
         end
       end
 
-      def error_class(error_code)
-        @errors.find { |e| e.name.include? error_code }
+      def error_parser(error_code)
+        @error_parsers.find { |e| e.name.include? error_code }
       end
 
-      def generic_error(error_opts)
-        http_resp = error_opts[:http_resp]
+      def generic_error(http_resp, error_opts)
         case http_resp.status
         when HTTP_3XX then @error_module::ApiRedirectError.new(
           location: http_resp.headers['location'], **error_opts
diff --git a/hearth/spec/hearth/http/api_error_spec.rb b/hearth/spec/hearth/http/api_error_spec.rb
deleted file mode 100644
index 277101319..000000000
--- a/hearth/spec/hearth/http/api_error_spec.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-# frozen_string_literal: true
-
-module Hearth
-  module HTTP
-    describe ApiError do
-      let(:http_status) { 404 }
-      let(:http_fields) { Fields.new }
-      let(:http_body) { StringIO.new('body') }
-      let(:http_resp) do
-        Response.new(
-          status: http_status,
-          fields: http_fields,
-          body: http_body
-        )
-      end
-      let(:message) { 'message' }
-
-      subject do
-        ApiError.new(
-          http_resp: http_resp,
-          error_code: 'error_code',
-          metadata: {},
-          message: message
-        )
-      end
-
-      it 'subclasses Hearth::ApiError' do
-        expect(subject).to be_a Hearth::ApiError
-      end
-
-      describe '#http_status' do
-        it 'returns the http status' do
-          expect(subject.http_status).to eq(http_status)
-        end
-      end
-
-      describe '#http_fields' do
-        it 'returns the http fields' do
-          expect(subject.http_fields).to eq(http_fields)
-        end
-      end
-
-      describe '#http_body' do
-        it 'returns the http body' do
-          expect(subject.http_body).to eq(http_body)
-        end
-      end
-    end
-  end
-end