hearth 1.0.0.pre2 → 1.0.0.pre3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/VERSION +1 -1
  4. data/lib/hearth/anonymous_auth_resolver.rb +11 -0
  5. data/lib/hearth/auth_schemes/anonymous.rb +3 -3
  6. data/lib/hearth/auth_schemes.rb +3 -3
  7. data/lib/hearth/client.rb +66 -0
  8. data/lib/hearth/client_stubs.rb +1 -3
  9. data/lib/hearth/config/resolver.rb +6 -5
  10. data/lib/hearth/context.rb +1 -0
  11. data/lib/hearth/dns/host_address.rb +20 -16
  12. data/lib/hearth/endpoint_rules.rb +154 -0
  13. data/lib/hearth/http/client.rb +5 -7
  14. data/lib/hearth/http/error_inspector.rb +2 -2
  15. data/lib/hearth/http/field.rb +4 -19
  16. data/lib/hearth/http/header_list_builder.rb +42 -0
  17. data/lib/hearth/http/header_list_parser.rb +92 -0
  18. data/lib/hearth/http/middleware/content_length.rb +3 -3
  19. data/lib/hearth/http/middleware/content_md5.rb +0 -1
  20. data/lib/hearth/http/middleware/request_compression.rb +7 -10
  21. data/lib/hearth/http.rb +2 -0
  22. data/lib/hearth/{identity_resolver.rb → identity_provider.rb} +1 -1
  23. data/lib/hearth/interceptor_context.rb +8 -4
  24. data/lib/hearth/interceptors.rb +2 -1
  25. data/lib/hearth/json.rb +4 -4
  26. data/lib/hearth/middleware/auth.rb +9 -6
  27. data/lib/hearth/middleware/build.rb +0 -1
  28. data/lib/hearth/middleware/endpoint.rb +79 -0
  29. data/lib/hearth/middleware/host_prefix.rb +1 -2
  30. data/lib/hearth/middleware/initialize.rb +0 -1
  31. data/lib/hearth/middleware/parse.rb +0 -1
  32. data/lib/hearth/middleware/retry.rb +9 -2
  33. data/lib/hearth/middleware/send.rb +0 -1
  34. data/lib/hearth/middleware.rb +1 -0
  35. data/lib/hearth/middleware_stack.rb +1 -1
  36. data/lib/hearth/number_helper.rb +1 -1
  37. data/lib/hearth/query/param.rb +7 -3
  38. data/lib/hearth/query/param_matcher.rb +5 -6
  39. data/lib/hearth/{refreshing_identity_resolver.rb → refreshing_identity_provider.rb} +2 -2
  40. data/lib/hearth/request.rb +2 -2
  41. data/lib/hearth/response.rb +5 -2
  42. data/lib/hearth/retry/adaptive.rb +2 -2
  43. data/lib/hearth/retry/client_rate_limiter.rb +8 -6
  44. data/lib/hearth/retry/exponential_backoff.rb +1 -1
  45. data/lib/hearth/retry/standard.rb +2 -2
  46. data/lib/hearth/retry.rb +16 -3
  47. data/lib/hearth/structure.rb +7 -3
  48. data/lib/hearth/stubs.rb +12 -4
  49. data/lib/hearth/time_helper.rb +1 -2
  50. data/lib/hearth/validator.rb +37 -21
  51. data/lib/hearth/waiters/poller.rb +4 -2
  52. data/lib/hearth/waiters/waiter.rb +6 -5
  53. data/lib/hearth/xml/node.rb +0 -1
  54. data/lib/hearth/xml/node_matcher.rb +0 -1
  55. data/lib/hearth.rb +8 -4
  56. data/sig/lib/hearth/aliases.rbs +5 -3
  57. data/sig/lib/hearth/anonymous_auth_resolver.rbs +5 -0
  58. data/sig/lib/hearth/auth_schemes.rbs +1 -1
  59. data/sig/lib/hearth/client.rbs +9 -0
  60. data/sig/lib/hearth/configuration.rbs +2 -2
  61. data/sig/lib/hearth/dns/host_address.rbs +1 -3
  62. data/sig/lib/hearth/dns/host_resolver.rbs +3 -3
  63. data/sig/lib/hearth/endpoint_rules.rbs +17 -0
  64. data/sig/lib/hearth/http/field.rbs +1 -1
  65. data/sig/lib/hearth/http/fields.rbs +1 -1
  66. data/sig/lib/hearth/http/header_list_builder.rbs +15 -0
  67. data/sig/lib/hearth/http/header_list_parser.rbs +19 -0
  68. data/sig/lib/hearth/http/networking_error.rbs +6 -0
  69. data/sig/lib/hearth/http/response.rbs +1 -1
  70. data/sig/lib/hearth/identities.rbs +1 -1
  71. data/sig/lib/hearth/{identity_resolver.rbs → identity_provider.rbs} +1 -1
  72. data/sig/lib/hearth/interceptor_context.rbs +4 -2
  73. data/sig/lib/hearth/interfaces.rbs +52 -30
  74. data/sig/lib/hearth/json/parse_error.rbs +9 -0
  75. data/sig/lib/hearth/networking_error.rbs +7 -0
  76. data/sig/lib/hearth/output.rbs +4 -4
  77. data/sig/lib/hearth/plugin_list.rbs +5 -7
  78. data/sig/lib/hearth/query/param.rbs +2 -2
  79. data/sig/lib/hearth/refreshing_identity_provider.rbs +10 -0
  80. data/sig/lib/hearth/request.rbs +2 -2
  81. data/sig/lib/hearth/response.rbs +2 -2
  82. data/sig/lib/hearth/retry/exponential_backoff.rbs +1 -1
  83. data/sig/lib/hearth/retry.rbs +1 -1
  84. data/sig/lib/hearth/structure.rbs +1 -2
  85. data/sig/lib/hearth/stubs.rbs +9 -0
  86. data/sig/lib/hearth/union.rbs +1 -1
  87. data/sig/lib/hearth/xml/parse_error.rbs +9 -0
  88. metadata +26 -10
  89. data/lib/hearth/retry/strategy.rb +0 -20
@@ -5,7 +5,6 @@ module Hearth
5
5
  module Middleware
6
6
  # A middleware that compresses the request body and
7
7
  # adds the Content-Encoding header
8
- # @api private
9
8
  class RequestCompression
10
9
  include Hearth::Middleware::Logging
11
10
 
@@ -62,17 +61,16 @@ module Hearth
62
61
  def update_content_encoding(encoding, request)
63
62
  headers = request.headers
64
63
  if headers['Content-Encoding']
65
- headers['Content-Encoding'] += ",#{encoding}"
64
+ headers['Content-Encoding'] += ", #{encoding}"
66
65
  else
67
66
  headers['Content-Encoding'] = encoding
68
67
  end
69
68
  end
70
69
 
71
70
  def compress_body(encoding, request)
72
- case encoding
73
- when 'gzip'
74
- gzip_compress(request)
75
- end
71
+ return unless encoding == 'gzip'
72
+
73
+ gzip_compress(request)
76
74
  update_content_encoding(encoding, request)
77
75
  end
78
76
 
@@ -100,10 +98,9 @@ module Hearth
100
98
  end
101
99
 
102
100
  def compress_streaming_body(encoding, request)
103
- case encoding
104
- when 'gzip'
105
- request.body = GzipIO.new(request.body)
106
- end
101
+ return unless encoding == 'gzip'
102
+
103
+ request.body = GzipIO.new(request.body)
107
104
  update_content_encoding(encoding, request)
108
105
  end
109
106
 
data/lib/hearth/http.rb CHANGED
@@ -7,6 +7,8 @@ require_relative 'http/error_inspector'
7
7
  require_relative 'http/error_parser'
8
8
  require_relative 'http/field'
9
9
  require_relative 'http/fields'
10
+ require_relative 'http/header_list_builder'
11
+ require_relative 'http/header_list_parser'
10
12
  require_relative 'http/middleware'
11
13
  require_relative 'http/networking_error'
12
14
  require_relative 'http/request'
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Hearth
4
4
  # Basic Identity resolver that uses a proc to resolve an Identity.
5
- class IdentityResolver
5
+ class IdentityProvider
6
6
  # @param [Proc] proc A proc that takes identity properties (Hash)
7
7
  # and returns a {Hearth::Identities::Base}.
8
8
  def initialize(proc)
@@ -5,17 +5,18 @@ module Hearth
5
5
  # context to read and modify the input, request, response, and output.
6
6
  # Attributes can be used to pass additional data between interceptors.
7
7
  class InterceptorContext
8
- # @param [Struct] input
8
+ # @param [Hearth::Structure] input
9
9
  # @param [Hearth::Request] request
10
10
  # @param [Hearth::Response] response
11
11
  # @param [Hearth::Output] output
12
- # @param [Hash] attributes ({}) Additional interceptor data
13
- def initialize(input:, request:, response:, output:, attributes: {})
12
+ # @param [Logger] logger
13
+ def initialize(input:, request:, response:, output:, logger:)
14
14
  @input = input
15
15
  @request = request
16
16
  @response = response
17
17
  @output = output
18
- @attributes = attributes
18
+ @logger = logger
19
+ @attributes = {}
19
20
  end
20
21
 
21
22
  # @return [Struct] Modeled input, i.e. Types::<Operation>Input
@@ -30,6 +31,9 @@ module Hearth
30
31
  # @return [Hearth::Output] Operation output
31
32
  attr_reader :output
32
33
 
34
+ # @return [Logger] logger
35
+ attr_reader :logger
36
+
33
37
  # @return [Hash] attributes Additional interceptor data
34
38
  attr_reader :attributes
35
39
  end
@@ -42,7 +42,8 @@ module Hearth
42
42
  input: input,
43
43
  request: context.request,
44
44
  response: context.response,
45
- output: output
45
+ output: output,
46
+ logger: context.logger
46
47
  )
47
48
  end
48
49
 
data/lib/hearth/json.rb CHANGED
@@ -12,10 +12,10 @@ module Hearth
12
12
  class << self
13
13
  # @param [String] json
14
14
  # @return [Hash]
15
- def load(json)
16
- # rubocop:disable Security/JSONLoad
17
- ::JSON.load(json)
18
- # rubocop:enable Security/JSONLoad
15
+ def parse(json)
16
+ return nil if json.empty?
17
+
18
+ ::JSON.parse(json)
19
19
  rescue ::JSON::ParserError => e
20
20
  raise ParseError, e
21
21
  end
@@ -3,7 +3,6 @@
3
3
  module Hearth
4
4
  module Middleware
5
5
  # A middleware that resolves identities for signing requests.
6
- # @api private
7
6
  class Auth
8
7
  include Middleware::Logging
9
8
 
@@ -23,11 +22,11 @@ module Hearth
23
22
  @auth_params = auth_params
24
23
  @auth_schemes = auth_schemes.to_h { |s| [s.scheme_id, s] }
25
24
 
26
- @identity_resolvers = {}
25
+ @identity_providers = {}
27
26
  kwargs.each do |key, value|
28
27
  next unless key.superclass == Hearth::Identities::Base
29
28
 
30
- @identity_resolvers[key] = value
29
+ @identity_providers[key] = value
31
30
  end
32
31
  end
33
32
 
@@ -46,6 +45,7 @@ module Hearth
46
45
  private
47
46
 
48
47
  ResolvedAuth = Struct.new(
48
+ :scheme_id,
49
49
  :signer,
50
50
  :signer_properties,
51
51
  :identity,
@@ -56,6 +56,8 @@ module Hearth
56
56
  def resolve_auth(auth_options)
57
57
  failures = []
58
58
 
59
+ raise 'No auth options were resolved' if auth_options.empty?
60
+
59
61
  auth_options.each do |auth_option|
60
62
  auth_scheme = @auth_schemes[auth_option.scheme_id]
61
63
  resolved_auth = try_load_auth_scheme(
@@ -78,17 +80,18 @@ module Hearth
78
80
  return
79
81
  end
80
82
 
81
- identity_resolver = auth_scheme.identity_resolver(@identity_resolvers)
82
- unless identity_resolver
83
+ identity_provider = auth_scheme.identity_provider(@identity_providers)
84
+ unless identity_provider
83
85
  failures << "Auth scheme #{scheme_id} did not have an " \
84
86
  'identity resolver configured'
85
87
  return
86
88
  end
87
89
 
88
90
  identity_properties = auth_option.identity_properties
89
- identity = identity_resolver.identity(identity_properties)
91
+ identity = identity_provider.identity(identity_properties)
90
92
 
91
93
  ResolvedAuth.new(
94
+ scheme_id: scheme_id,
92
95
  identity: identity,
93
96
  identity_properties: auth_option.identity_properties,
94
97
  signer: auth_scheme.signer,
@@ -3,7 +3,6 @@
3
3
  module Hearth
4
4
  module Middleware
5
5
  # A middleware that builds a request object.
6
- # @api private
7
6
  class Build
8
7
  include Middleware::Logging
9
8
 
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ # in Hearth middleware
4
+ module Hearth
5
+ module Middleware
6
+ # Resolves endpoints from endpoint parameters and modifies
7
+ # the request with resolved endpoint, headers and auth schemes.
8
+ class Endpoint
9
+ include Middleware::Logging
10
+
11
+ # @param [Class] app The next middleware in the stack.
12
+ # @param [#resolve(endpoint_params)] endpoint_resolver An object
13
+ # that responds to a `resolve(endpoint_params)` method
14
+ # where `endpoint_params` is a service specific struct.
15
+ # The method must return an {Hearth::Endpoints::Endpoint} object.
16
+ # @param [#build(config, input, context)] param_builder An object that
17
+ # responds to a `build(config, input, context)` method and returns
18
+ # an endpoint_params object.
19
+ def initialize(
20
+ app, endpoint_resolver:, param_builder:, **kwargs
21
+ )
22
+ @app = app
23
+ @param_builder = param_builder
24
+ @endpoint_resolver = endpoint_resolver
25
+ @config = kwargs
26
+ end
27
+
28
+ def call(input, context)
29
+ params = @param_builder.build(@config, input, context)
30
+ log_debug(context, "Endpoint params: #{params}")
31
+ endpoint = @endpoint_resolver.resolve(params)
32
+ log_debug(context, "Resolved endpoint: #{endpoint}")
33
+ update_request(context, endpoint)
34
+ log_debug(context, "Updated request: #{context.request}")
35
+ update_auth_properties(context, endpoint.auth_schemes)
36
+ log_debug(context, "Updated auth properties: #{context.auth}")
37
+
38
+ @app.call(input, context)
39
+ end
40
+
41
+ private
42
+
43
+ # apply the resolved endpoint to the request
44
+ def update_request(context, endpoint)
45
+ context.request.uri = merge_endpoints(
46
+ URI(endpoint.uri), context.request.uri
47
+ )
48
+ endpoint.headers.each do |key, val|
49
+ context.request.headers[key] = val
50
+ end
51
+ end
52
+
53
+ # merge the path and query parameters from serialization
54
+ # URIs from endpoint resolution MUST contain port and hostname
55
+ # and MAY contain port and base path.
56
+ def merge_endpoints(resolved_uri, request_uri)
57
+ merged = URI(resolved_uri)
58
+ merged.path = resolved_uri.path + request_uri.path
59
+ merged.query = request_uri.query
60
+ merged
61
+ end
62
+
63
+ # merge properties from the endpoint resolved auth schemes onto
64
+ # the auth scheme resolved by the auth resolver.
65
+ def update_auth_properties(context, auth_schemes)
66
+ context[:endpoint_auth_schemes] = auth_schemes
67
+ return unless context.auth
68
+
69
+ matching = auth_schemes.find do |a|
70
+ context.auth.scheme_id == a.scheme_id
71
+ end
72
+
73
+ return unless matching
74
+
75
+ context.auth.signer_properties.merge!(matching.properties)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -3,7 +3,6 @@
3
3
  module Hearth
4
4
  module Middleware
5
5
  # A middleware that prefixes the host.
6
- # @api private
7
6
  class HostPrefix
8
7
  include Middleware::Logging
9
8
 
@@ -37,7 +36,7 @@ module Hearth
37
36
  end
38
37
 
39
38
  def apply_labels(host_prefix, input)
40
- host_prefix.gsub(/\{.+?\}/) do |host_label|
39
+ host_prefix.gsub(/\{.+?}/) do |host_label|
41
40
  key = host_label.delete('{}')
42
41
  value = input[key.to_sym]
43
42
  if value.nil? || value.empty?
@@ -3,7 +3,6 @@
3
3
  module Hearth
4
4
  module Middleware
5
5
  # A middleware used to initialize the request, called first in the stack
6
- # @api private
7
6
  class Initialize
8
7
  include Middleware::Logging
9
8
 
@@ -3,7 +3,6 @@
3
3
  module Hearth
4
4
  module Middleware
5
5
  # A middleware that parses a response object.
6
- # @api private
7
6
  class Parse
8
7
  include Middleware::Logging
9
8
 
@@ -3,7 +3,6 @@
3
3
  module Hearth
4
4
  module Middleware
5
5
  # A middleware that retries the request using a retry strategy.
6
- # @api private
7
6
  class Retry
8
7
  include Middleware::Logging
9
8
 
@@ -75,6 +74,8 @@ module Hearth
75
74
 
76
75
  if (error = output.error)
77
76
  log_debug(context, "Request failed with error: #{error}")
77
+ break unless retryable?(context.request)
78
+
78
79
  error_info = @error_inspector_class.new(error, context.response)
79
80
  token = @retry_strategy.refresh_retry_token(token, error_info)
80
81
  break unless token
@@ -93,14 +94,20 @@ module Hearth
93
94
  @retries += 1
94
95
  end
95
96
  log_debug(context, 'Finished retry loop')
97
+ log_debug(context, "Total retries: #{@retries}")
96
98
  output
97
99
  end
98
100
 
99
101
  private
100
102
 
103
+ def retryable?(request)
104
+ # IO responds to #rewind however it returns an illegal seek error
105
+ request.body.respond_to?(:rewind) && !request.body.instance_of?(IO)
106
+ end
107
+
101
108
  def reset_request(context)
102
109
  request = context.request
103
- request.body.rewind if request.body.respond_to?(:rewind)
110
+ request.body.rewind
104
111
 
105
112
  context.auth.signer.reset(
106
113
  request: request,
@@ -3,7 +3,6 @@
3
3
  module Hearth
4
4
  module Middleware
5
5
  # A middleware used to send the request.
6
- # @api private
7
6
  class Send
8
7
  include Middleware::Logging
9
8
 
@@ -18,6 +18,7 @@ end
18
18
 
19
19
  require_relative 'middleware/auth'
20
20
  require_relative 'middleware/build'
21
+ require_relative 'middleware/endpoint'
21
22
  require_relative 'middleware/host_prefix'
22
23
  require_relative 'middleware/parse'
23
24
  require_relative 'middleware/retry'
@@ -15,7 +15,7 @@ module Hearth
15
15
  # @param context
16
16
  # @return [Output]
17
17
  def run(input, context)
18
- stack.call(input, context)
18
+ stack&.call(input, context) || Output.new
19
19
  end
20
20
 
21
21
  private
@@ -18,7 +18,7 @@ module Hearth
18
18
  end
19
19
 
20
20
  # @param [String] str
21
- # @return [Number] The input as a number
21
+ # @return [Float, nil] The input as a number
22
22
  def deserialize(str)
23
23
  case str
24
24
  when 'Infinity' then ::Float::INFINITY
@@ -5,7 +5,7 @@ module Hearth
5
5
  # A class used to represent a query parameter before serialization.
6
6
  class Param
7
7
  # @param [String] name
8
- # @param [String, Array<String>] value (nil)
8
+ # @param [String, Array<String>, nil] value (nil)
9
9
  def initialize(name, value = nil)
10
10
  @name = name
11
11
  @value = value
@@ -20,7 +20,7 @@ module Hearth
20
20
  # @return [String]
21
21
  def to_s
22
22
  if value.is_a?(Array)
23
- value.map { |v| serialize(name, v) }.join('&')
23
+ serialize_array(name, value)
24
24
  else
25
25
  serialize(name, value)
26
26
  end
@@ -33,7 +33,7 @@ module Hearth
33
33
  other.value == value
34
34
  end
35
35
 
36
- # @return [Boolean]
36
+ # @return [Integer]
37
37
  def <=>(other)
38
38
  name <=> other.name
39
39
  end
@@ -44,6 +44,10 @@ module Hearth
44
44
  value.nil? ? escape(name) : "#{escape(name)}=#{escape(value)}"
45
45
  end
46
46
 
47
+ def serialize_array(name, values)
48
+ values.map { |v| serialize(name, v) }.join('&')
49
+ end
50
+
47
51
  def escape(value)
48
52
  Hearth::HTTP.uri_escape(value.to_s)
49
53
  end
@@ -3,7 +3,6 @@
3
3
  require 'rspec/expectations'
4
4
 
5
5
  # Provides an rspec matcher for CGI.parse to check Float precision.
6
- # @api private
7
6
  RSpec::Matchers.define :match_query_params do |expected|
8
7
  match do |actual|
9
8
  return true if actual == expected
@@ -19,11 +18,11 @@ RSpec::Matchers.define :match_query_params do |expected|
19
18
 
20
19
  a.zip(e).each do |a0, e0|
21
20
  # Timestamps can have optional precision.
22
- if (float = Float(a0) rescue false)
23
- expect(float).to eq(e0.to_f)
24
- else
25
- expect(a0).to eq(e0)
26
- end
21
+
22
+ float = Float(a0)
23
+ expect(float).to eq(e0.to_f)
24
+ rescue StandardError
25
+ expect(a0).to eq(e0)
27
26
  end
28
27
  end
29
28
  end
@@ -5,7 +5,7 @@ module Hearth
5
5
  # The class must implement #refresh(properties) that sets @identity. The
6
6
  # refresh method will be called when #identity is called and the identity
7
7
  # is nil or near expiration.
8
- module RefreshingIdentityResolver
8
+ module RefreshingIdentityProvider
9
9
  SYNC_EXPIRATION_LENGTH = 300 # 5 minutes
10
10
  ASYNC_EXPIRATION_LENGTH = 600 # 10 minutes
11
11
 
@@ -13,7 +13,7 @@ module Hearth
13
13
  @mutex = Mutex.new
14
14
  end
15
15
 
16
- # @return [Identity]
16
+ # @return [Identities::Base]
17
17
  def identity(properties = {})
18
18
  if @identity
19
19
  refresh_if_near_expiration!(properties)
@@ -7,7 +7,7 @@ module Hearth
7
7
  # Represents a base request.
8
8
  class Request
9
9
  # @param [URI] uri (URI(''))
10
- # @param [IO] body (StringIO.new)
10
+ # @param [IO, StringIO] body (StringIO.new)
11
11
  def initialize(uri: URI(''), body: StringIO.new)
12
12
  @uri = uri
13
13
  @body = body
@@ -16,7 +16,7 @@ module Hearth
16
16
  # @return [URI]
17
17
  attr_accessor :uri
18
18
 
19
- # @return [IO]
19
+ # @return [IO, StringIO]
20
20
  attr_accessor :body
21
21
  end
22
22
  end
@@ -5,12 +5,12 @@ require 'stringio'
5
5
  module Hearth
6
6
  # Represents a base response.
7
7
  class Response
8
- # @param [IO] body (StringIO.new)
8
+ # @param [IO, StringIO] body (StringIO.new)
9
9
  def initialize(body: StringIO.new)
10
10
  @body = body
11
11
  end
12
12
 
13
- # @return [IO]
13
+ # @return [IO, StringIO]
14
14
  attr_accessor :body
15
15
 
16
16
  # Replace attributes from other response
@@ -26,7 +26,10 @@ module Hearth
26
26
  # Resets the response.
27
27
  # @return [Response]
28
28
  def reset
29
+ # IO does not respond to #truncate but it does respond to #rewind
30
+ # however it returns an illegal seek error.
29
31
  @body.truncate(0) if @body.respond_to?(:truncate)
32
+ @body.rewind if @body.respond_to?(:rewind) && !@body.instance_of?(IO)
30
33
  self
31
34
  end
32
35
  end
@@ -3,7 +3,7 @@
3
3
  module Hearth
4
4
  module Retry
5
5
  # Adaptive retry strategy for retrying requests.
6
- class Adaptive < Strategy
6
+ class Adaptive
7
7
  # @param [#call] backoff (ExponentialBackoff) A callable object that
8
8
  # calculates a backoff delay for a retry attempt.
9
9
  # @param [Integer] max_attempts (3) The maximum number of attempts that
@@ -22,7 +22,7 @@ module Hearth
22
22
  # instance state
23
23
  @client_rate_limiter = ClientRateLimiter.new
24
24
  @retry_quota = RetryQuota.new
25
- @capacity_amount = nil
25
+ @capacity_amount = 0
26
26
  end
27
27
 
28
28
  def acquire_initial_retry_token(_token_scope = nil)
@@ -52,12 +52,6 @@ module Hearth
52
52
  update_measured_rate
53
53
 
54
54
  if is_throttling_error
55
- rate_to_use = if @enabled
56
- [@measured_tx_rate, @fill_rate].min
57
- else
58
- @measured_tx_rate
59
- end
60
-
61
55
  # The fill_rate is from the token bucket
62
56
  @last_max_rate = rate_to_use
63
57
  calculate_time_window
@@ -138,6 +132,14 @@ module Hearth
138
132
  def cubic_throttle(rate_to_use)
139
133
  rate_to_use * BETA
140
134
  end
135
+
136
+ def rate_to_use
137
+ if @enabled
138
+ [@measured_tx_rate, @fill_rate].min
139
+ else
140
+ @measured_tx_rate
141
+ end
142
+ end
141
143
  end
142
144
  end
143
145
  end
@@ -8,7 +8,7 @@ module Hearth
8
8
  MAX_BACKOFF = 20
9
9
 
10
10
  def call(attempts)
11
- [Kernel.rand * (2**attempts), MAX_BACKOFF].min
11
+ [Kernel.rand * (2**attempts), MAX_BACKOFF].min || 0
12
12
  end
13
13
  end
14
14
  end
@@ -3,7 +3,7 @@
3
3
  module Hearth
4
4
  module Retry
5
5
  # Standard retry strategy for retrying requests.
6
- class Standard < Strategy
6
+ class Standard
7
7
  # @param [#call] backoff (ExponentialBackoff) A callable object that
8
8
  # calculates a backoff delay for a retry attempt.
9
9
  # @param [Integer] max_attempts (3) The maximum number of attempts that
@@ -15,7 +15,7 @@ module Hearth
15
15
 
16
16
  # instance state
17
17
  @retry_quota = RetryQuota.new
18
- @capacity_amount = nil
18
+ @capacity_amount = 0
19
19
  end
20
20
 
21
21
  def acquire_initial_retry_token(_token_scope = nil)
data/lib/hearth/retry.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'retry/strategy'
4
-
5
3
  require_relative 'retry/adaptive'
6
4
  require_relative 'retry/capacity_not_available_error'
7
5
  require_relative 'retry/client_rate_limiter'
@@ -11,6 +9,21 @@ require_relative 'retry/standard'
11
9
 
12
10
  module Hearth
13
11
  module Retry
14
- Token = Struct.new(:retry_count, :retry_delay, keyword_init: true)
12
+ # Represents a token that can be used to retry an operation.
13
+ # @!attribute retry_count
14
+ # The number of times the operation has been retried.
15
+ # @return [Integer]
16
+ # @!attribute retry_delay
17
+ # The delay before the next retry.
18
+ # @return [Numeric]
19
+ Token = Struct.new(:retry_count, :retry_delay, keyword_init: true) do
20
+ # @option args [Integer] :retry_count (0)
21
+ # @option args [Numeric] :retry_delay (0)
22
+ def initialize(*args)
23
+ super
24
+ self.retry_count ||= 0
25
+ self.retry_delay ||= 0
26
+ end
27
+ end
15
28
  end
16
29
  end
@@ -6,15 +6,15 @@ module Hearth
6
6
  # Deeply converts the Struct into a hash. Structure members that
7
7
  # are `nil` are omitted from the resultant hash.
8
8
  #
9
- # @return [Hash]
9
+ # @return [Hash, Structure]
10
10
  def to_h(obj = self)
11
11
  case obj
12
12
  when Struct
13
13
  _to_h_struct(obj)
14
14
  when Hash
15
15
  _to_h_hash(obj)
16
- when Array, Set
17
- obj.collect { |value| to_hash(value) }
16
+ when Array
17
+ _to_h_array(obj)
18
18
  when Union
19
19
  obj.to_h
20
20
  else
@@ -36,5 +36,9 @@ module Hearth
36
36
  hash[key] = to_hash(value)
37
37
  end
38
38
  end
39
+
40
+ def _to_h_array(obj)
41
+ obj.collect { |value| to_hash(value) }
42
+ end
39
43
  end
40
44
  end