faraday-retry 2.1.0 → 2.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2b88675fec154db2d70b835b98124879b77dbcd283b898fea74fe88c1a75a4b
4
- data.tar.gz: 57eefe94dd39d192dff652e90cfadda2ce06d244a3a20e849dd9f3df6a88ffb4
3
+ metadata.gz: 76aff4f986315a337600432e6d2458f4080004755f37583ac413bf454713602c
4
+ data.tar.gz: fe612cfa054e9f293d533404759734672284f92258f43a17fb9febbb455f5b61
5
5
  SHA512:
6
- metadata.gz: de85932e530b33168b83399f0135fd862071c10318faaaef324935c3e51bc1914dd9361c68a5c2a6d3a4fc81e9f0193b928a4fe65c2dbc34826998573465af86
7
- data.tar.gz: f5e12ca382154fac0fbff872f9f906a62c0a990b06226363c701fa52d05ee927d23b0358f005e6de974e7fad938944beb2621413419b48a5d43b29e4b3180af8
6
+ metadata.gz: 11f9e7f0a270962bcc25ab31690fa8c14dc0fb461a74b8a7db94d7d719e48679590f12b554e37da579ea57824dc35fc13fc6820e4772b4de08b269e88924202f
7
+ data.tar.gz: 305559acfa19db8b994f2dc8e2e734f31fd1fdbe255b82eb3612ce5630d0c9b325ad46621892675a4e7fa6b85f99329fa0af635923619d650b69ce5a837689e5
data/CHANGELOG.md CHANGED
@@ -1,14 +1,24 @@
1
1
  # Changelog
2
2
 
3
- ## v2.1.1 (2023-02-17)
3
+ ## Unreleased
4
4
 
5
- ### Changed
5
+ _nothing yet_
6
6
 
7
- * Support for custom RateLimit headers. [PR #13](https://github.com/lostisland/faraday-retry/pull/13). Thanks, [@brookemckim]!
7
+ ## v2.2.1 (2024-04-15)
8
+
9
+ * Avoid deprecation warning about ::UploadIO constant when used without faraday-multipart gem [PR #37](https://github.com/lostisland/faraday-retry/pull/37) [@iMacTia]
10
+ * Documentation update [PR #30](https://github.com/lostisland/faraday-retry/pull/30) [@olleolleolle]
11
+ * Documentation update [PR #32](https://github.com/lostisland/faraday-retry/pull/32) Thanks, [@Drowze]!
12
+
13
+ ## v2.2.0 (2023-06-01)
8
14
 
9
- ## v2.1.0 (2023-02-17)
15
+ * Support new `header_parser_block` option. [PR #28](https://github.com/lostisland/faraday-retry/pull/28). Thanks, [@zavan]!
16
+
17
+ ## v2.1.0 (2023-03-03)
18
+
19
+ * Support for custom RateLimit headers. [PR #13](https://github.com/lostisland/faraday-retry/pull/13). Thanks, [@brookemckim]!
10
20
 
11
- Invalid release, use v2.1.1.
21
+ v2.1.1 (2023-02-17) is a spurious not-released version that you may have seen mentioned in this CHANGELOG.
12
22
 
13
23
  ## v2.0.0 (2022-06-08)
14
24
 
@@ -33,3 +43,7 @@ This release consists of the same middleware that was previously bundled with Fa
33
43
 
34
44
  [@maxprokopiev]: https://github.com/maxprokopiev
35
45
  [@brookemckim]: https://github.com/brookemckim
46
+ [@zavan]: https://github.com/zavan
47
+ [@Drowze]: https://github.com/Drowze
48
+ [@olleolleolle]: https://github.com/olleolleolle
49
+ [@iMacTia]: https://github.com/iMacTia
data/README.md CHANGED
@@ -75,7 +75,7 @@ retry_options = {
75
75
  #### Specify which exceptions should trigger a retry
76
76
 
77
77
  You can provide an `exceptions` option with a list of exceptions that will replace
78
- the default list of network-related exceptions: `Errno::ETIMEDOUT`, `Timeout::Error`, `Faraday::TimeoutError`.
78
+ the default exceptions: `Errno::ETIMEDOUT`, `Timeout::Error`, `Faraday::TimeoutError`, `Faraday::Error::RetriableResponse`.
79
79
  This can be particularly useful when combined with the [RaiseError][raise_error] middleware.
80
80
 
81
81
  ```ruby
@@ -84,6 +84,14 @@ retry_options = {
84
84
  }
85
85
  ```
86
86
 
87
+ If you want to inherit default exceptions, do it this way.
88
+
89
+ ```ruby
90
+ retry_options = {
91
+ exceptions: Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS + [Faraday::ResourceNotFound, Faraday::UnauthorizedError]
92
+ }
93
+ ```
94
+
87
95
  #### Specify on which response statuses to retry
88
96
 
89
97
  By default the `Retry` middleware will only retry the request if one of the expected exceptions arise.
@@ -111,13 +119,14 @@ retry_options = {
111
119
  }
112
120
  ```
113
121
 
114
- If you are working with an API which does not comply with the Rate Limit RFC you can specify custom headers to be used for retry and reset.
122
+ If you are working with an API which does not comply with the Rate Limit RFC you can specify custom headers to be used for retry and reset, as well as a block to parse the headers:
115
123
 
116
124
  ```ruby
117
125
  retry_options = {
118
126
  retry_statuses: [429],
119
127
  rate_limit_retry_header: 'x-rate-limit-retry-after',
120
- rate_limit_reset_header: 'x-rate-limit-reset'
128
+ rate_limit_reset_header: 'x-rate-limit-reset',
129
+ header_parser_block: ->(value) { Time.at(value.to_i).utc - Time.now.utc }
121
130
  }
122
131
  ```
123
132
 
@@ -140,13 +149,11 @@ retry_options = {
140
149
 
141
150
  ### Call a block on every retry
142
151
 
143
- You can specify a proc object through the `retry_block` option that will be called before every
144
- retry, before There are many different applications for this feature, spacing from instrumentation to monitoring.
145
-
152
+ You can specify a proc object through the `retry_block` option that will be called before every retry.
153
+ There are many different applications for this feature, ranging from instrumentation to monitoring.
146
154
 
147
155
  The block is passed keyword arguments with contextual information: Request environment, middleware options, current number of retries, exception, and amount of time we will wait before retrying. (retry_block is called before the wait time happens)
148
156
 
149
-
150
157
  For example, you might want to keep track of the response statuses:
151
158
 
152
159
  ```ruby
@@ -156,6 +163,27 @@ retry_options = {
156
163
  }
157
164
  ```
158
165
 
166
+ ### Call a block after retries have been exhausted
167
+
168
+ You can specify a lambda object through the `exhausted_retries_block` option that will be called after all retries are exhausted.
169
+ This block will be called once.
170
+
171
+ The block is passed keyword arguments with contextual information and passed your data:
172
+ * Request environment,
173
+ * exception,
174
+ * middleware options
175
+ * and your data.
176
+
177
+ In a lambda you can pass any logic for further work.
178
+
179
+ For example, you might want to update user by request query.
180
+
181
+ ```ruby
182
+ retry_options = {
183
+ exhausted_retries_block: -> (user_id:, env:, exception:, options:) { User.find_by!(id: user_id).do_admin! }
184
+ }
185
+ ```
186
+
159
187
  ## Development
160
188
 
161
189
  After checking out the repo, run `bin/setup` to install dependencies.
@@ -176,4 +204,4 @@ Bug reports and pull requests are welcome on [GitHub](https://github.com/lostisl
176
204
 
177
205
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
178
206
 
179
- [raise_error]: https://lostisland.github.io/faraday/middleware/raise-error
207
+ [raise_error]: https://lostisland.github.io/faraday/#/middleware/included/raising-errors
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'retryable'
4
+
3
5
  module Faraday
4
6
  module Retry
5
7
  # This class provides the main implementation for your middleware.
@@ -15,6 +17,8 @@ module Faraday
15
17
  # (see "retry" middleware: https://github.com/lostisland/faraday/blob/main/lib/faraday/request/retry.rb#L142).
16
18
  # IMPORTANT: Remember to call `@app.call(env)` or `super` to not interrupt the middleware chain!
17
19
  class Middleware < Faraday::Middleware
20
+ include Retryable
21
+
18
22
  DEFAULT_EXCEPTIONS = [
19
23
  Errno::ETIMEDOUT, 'Timeout::Error',
20
24
  Faraday::TimeoutError, Faraday::RetriableResponse
@@ -27,7 +31,8 @@ module Faraday
27
31
  :backoff_factor, :exceptions,
28
32
  :methods, :retry_if, :retry_block,
29
33
  :retry_statuses, :rate_limit_retry_header,
30
- :rate_limit_reset_header)
34
+ :rate_limit_reset_header, :header_parser_block,
35
+ :exhausted_retries_block)
31
36
 
32
37
  DEFAULT_CHECK = ->(_env, _exception) { false }
33
38
 
@@ -78,6 +83,10 @@ module Faraday
78
83
  def retry_statuses
79
84
  Array(self[:retry_statuses] ||= [])
80
85
  end
86
+
87
+ def exhausted_retries_block
88
+ self[:exhausted_retries_block] ||= proc {}
89
+ end
81
90
  end
82
91
 
83
92
  # @param app [#call]
@@ -95,8 +104,8 @@ module Faraday
95
104
  # 'Timeout::Error', Faraday::TimeoutError, Faraday::RetriableResponse])
96
105
  # The list of exceptions to handle. Exceptions can be given as
97
106
  # Class, Module, or String.
98
- # @option options [Array] :methods (the idempotent HTTP methods
99
- # in IDEMPOTENT_METHODS) A list of HTTP methods to retry without
107
+ # @option options [Array<Symbol>] :methods (the idempotent HTTP methods
108
+ # in IDEMPOTENT_METHODS) A list of HTTP methods, as symbols, to retry without
100
109
  # calling retry_if. Pass an empty Array to call retry_if
101
110
  # for all exceptions.
102
111
  # @option options [Block] :retry_if (false) block that will receive
@@ -120,6 +129,17 @@ module Faraday
120
129
  # codes or a single Integer value that determines whether to raise
121
130
  # a Faraday::RetriableResponse exception based on the HTTP status code
122
131
  # of an HTTP response.
132
+ # @option options [Block] :header_parser_block block that will receive
133
+ # the the value of the retry header and should return the number of
134
+ # seconds to wait before retrying the request. This is useful if the
135
+ # value of the header is not a number of seconds or a RFC 2822 formatted date.
136
+ # @option options [Block] :exhausted_retries_block block will receive
137
+ # when all attempts are exhausted. The block will be yielded keyword arguments:
138
+ # * env [Faraday::Env]: Request environment
139
+ # * exception [Exception]: exception that triggered the retry,
140
+ # will be the synthetic `Faraday::RetriableResponse` if the
141
+ # retry was triggered by something other than an exception.
142
+ # * options [Faraday::Options]: middleware options
123
143
  def initialize(app, options = nil)
124
144
  super(app)
125
145
  @options = Options.from(options)
@@ -143,32 +163,14 @@ module Faraday
143
163
  def call(env)
144
164
  retries = @options.max
145
165
  request_body = env[:body]
146
- begin
166
+
167
+ with_retries(env: env, options: @options, retries: retries, body: request_body, errmatch: @errmatch) do
147
168
  # after failure env[:body] is set to the response body
148
169
  env[:body] = request_body
170
+
149
171
  @app.call(env).tap do |resp|
150
172
  raise Faraday::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status)
151
173
  end
152
- rescue @errmatch => e
153
- if retries.positive? && retry_request?(env, e)
154
- retries -= 1
155
- rewind_files(request_body)
156
- if (sleep_amount = calculate_sleep_amount(retries + 1, env))
157
- @options.retry_block.call(
158
- env: env,
159
- options: @options,
160
- retry_count: @options.max - (retries + 1),
161
- exception: e,
162
- will_retry_in: sleep_amount
163
- )
164
- sleep sleep_amount
165
- retry
166
- end
167
- end
168
-
169
- raise unless e.is_a?(Faraday::RetriableResponse)
170
-
171
- e.response
172
174
  end
173
175
  end
174
176
 
@@ -205,11 +207,11 @@ module Faraday
205
207
  end
206
208
 
207
209
  def rewind_files(body)
208
- return unless defined?(UploadIO)
210
+ return unless defined?(Faraday::UploadIO)
209
211
  return unless body.is_a?(Hash)
210
212
 
211
213
  body.each do |_, value|
212
- value.rewind if value.is_a?(UploadIO)
214
+ value.rewind if value.is_a?(Faraday::UploadIO)
213
215
  end
214
216
  end
215
217
 
@@ -244,12 +246,16 @@ module Faraday
244
246
 
245
247
  retry_after_value = env[:response_headers][header]
246
248
 
247
- # Try to parse date from the header value
248
- begin
249
- datetime = DateTime.rfc2822(retry_after_value)
250
- datetime.to_time - Time.now.utc
251
- rescue ArgumentError
252
- retry_after_value.to_f
249
+ if @options.header_parser_block
250
+ @options.header_parser_block.call(retry_after_value)
251
+ else
252
+ # Try to parse date from the header value
253
+ begin
254
+ datetime = DateTime.rfc2822(retry_after_value)
255
+ datetime.to_time - Time.now.utc
256
+ rescue ArgumentError
257
+ retry_after_value.to_f
258
+ end
253
259
  end
254
260
  end
255
261
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ # Adds the ability to retry a request based on settings and errors that have occurred.
5
+ module Retryable
6
+ def with_retries(env:, options:, retries:, body:, errmatch:)
7
+ yield
8
+ rescue errmatch => e
9
+ exhausted_retries(options, env, e) if retries_zero?(retries, env, e)
10
+
11
+ if retries.positive? && retry_request?(env, e)
12
+ retries -= 1
13
+ rewind_files(body)
14
+ if (sleep_amount = calculate_sleep_amount(retries + 1, env))
15
+ options.retry_block.call(
16
+ env: env,
17
+ options: options,
18
+ retry_count: options.max - (retries + 1),
19
+ exception: e,
20
+ will_retry_in: sleep_amount
21
+ )
22
+ sleep sleep_amount
23
+ retry
24
+ end
25
+ end
26
+
27
+ raise unless e.is_a?(Faraday::RetriableResponse)
28
+
29
+ e.response
30
+ end
31
+
32
+ private
33
+
34
+ def retries_zero?(retries, env, exception)
35
+ retries.zero? && retry_request?(env, exception)
36
+ end
37
+
38
+ def exhausted_retries(options, env, exception)
39
+ options.exhausted_retries_block.call(
40
+ env: env,
41
+ exception: exception,
42
+ options: options
43
+ )
44
+ end
45
+ end
46
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Faraday
4
4
  module Retry
5
- VERSION = '2.1.0'
5
+ VERSION = '2.3.1'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faraday-retry
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Giuffrida
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-03 00:00:00.000000000 Z
11
+ date: 2025-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -151,17 +151,18 @@ files:
151
151
  - lib/faraday/retriable_response.rb
152
152
  - lib/faraday/retry.rb
153
153
  - lib/faraday/retry/middleware.rb
154
+ - lib/faraday/retry/retryable.rb
154
155
  - lib/faraday/retry/version.rb
155
156
  homepage: https://github.com/lostisland/faraday-retry
156
157
  licenses:
157
158
  - MIT
158
159
  metadata:
159
160
  bug_tracker_uri: https://github.com/lostisland/faraday-retry/issues
160
- changelog_uri: https://github.com/lostisland/faraday-retry/blob/v2.1.0/CHANGELOG.md
161
- documentation_uri: http://www.rubydoc.info/gems/faraday-retry/2.1.0
161
+ changelog_uri: https://github.com/lostisland/faraday-retry/blob/v2.3.1/CHANGELOG.md
162
+ documentation_uri: http://www.rubydoc.info/gems/faraday-retry/2.3.1
162
163
  homepage_uri: https://github.com/lostisland/faraday-retry
163
164
  source_code_uri: https://github.com/lostisland/faraday-retry
164
- post_install_message:
165
+ post_install_message:
165
166
  rdoc_options: []
166
167
  require_paths:
167
168
  - lib
@@ -180,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
180
181
  version: '0'
181
182
  requirements: []
182
183
  rubygems_version: 3.1.6
183
- signing_key:
184
+ signing_key:
184
185
  specification_version: 4
185
186
  summary: Catches exceptions and retries each request a limited number of times
186
187
  test_files: []