shopify_api_retry 0.0.1 → 0.1.0
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 +5 -5
- data/.github/workflows/ci.yml +22 -0
- data/README.md +51 -12
- data/Rakefile +7 -0
- data/lib/shopify_api_retry.rb +108 -16
- data/shopify_api_retry.gemspec +4 -3
- metadata +23 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 949d65d19ed205d18a64ea886ef817bcd9d8b26895036228dddc3dcf4d91f0ca
|
4
|
+
data.tar.gz: 6b776cf5985be1593e791a81c6f4ce31c88ca4f029c7ba03bd8e24ca5f44f26f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 781e898074a0f3b9aaed28f1202087173e42483920375f7921384854437f9ed40ad77a0ef37be5d1221f5e76141c988a35e50368da22e83b5e8722c185558e70
|
7
|
+
data.tar.gz: 243547c486e88cfabed0ea4a2c4fbba9524aa44234c5852110cf4a485f28c551309e78a055222576da2bcc6240232d783706807ebeee31c41956dddd6e130495
|
@@ -0,0 +1,22 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
- push
|
5
|
+
- pull_request
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
test:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
strategy:
|
11
|
+
matrix:
|
12
|
+
ruby: [3.0.0, 2.7.2, 2.6.6, 2.5.8, 2.4.10]
|
13
|
+
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v2
|
16
|
+
- uses: ruby/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
bundler-cache: true
|
19
|
+
ruby-version: ${{ matrix.ruby }}
|
20
|
+
|
21
|
+
- run: bundle install
|
22
|
+
- run: bundle exec rake
|
data/README.md
CHANGED
@@ -1,18 +1,9 @@
|
|
1
1
|
# ShopifyAPIRetry
|
2
2
|
|
3
|
-
|
3
|
+

|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
```rb
|
8
|
-
ShopifyAPIRetry.retry { customer.update_attribute(:tags, "foo") }
|
9
|
-
ShopifyAPIRetry.retry(30) { customer.update_attribute(:tags, "foo") } # Retry after 30 seconds on HTTP 429
|
10
|
-
customer = ShopifyAPIRetry.retry { ShopifyAPI::Customer.find(id) }
|
11
|
-
```
|
12
|
-
|
13
|
-
If no retry time is provided the value of the HTTP header `Retry-After` is used. If it's not given (it always is) `2` is used.
|
14
|
-
|
15
|
-
If the retry fails the original error is raised (`ActiveResource::ClientError` or subclass).
|
5
|
+
Simple Ruby module to retry a [Shopify API request](https://github.com/Shopify/shopify_api) if rate limited (HTTP 429) or other errors
|
6
|
+
occur.
|
16
7
|
|
17
8
|
## Installation
|
18
9
|
|
@@ -28,6 +19,54 @@ Gem:
|
|
28
19
|
gem install shopify_api_retry
|
29
20
|
```
|
30
21
|
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
By default requests are retried when a Shopify rate limit error is returned. The retry happens once after waiting for
|
25
|
+
[the seconds given by the HTTP `Retry-After` header](https://shopify.dev/concepts/about-apis/rate-limits):
|
26
|
+
```rb
|
27
|
+
require "shopify_api_retry" # requires "shopify_api" for you
|
28
|
+
|
29
|
+
ShopifyAPIRetry.retry { customer.update_attribute(:tags, "foo") }
|
30
|
+
customer = ShopifyAPIRetry.retry { ShopifyAPI::Customer.find(id) }
|
31
|
+
```
|
32
|
+
|
33
|
+
You can override this:
|
34
|
+
```rb
|
35
|
+
ShopifyAPIRetry.retry(:wait => 3, :tries => 5) { customer.update_attribute(:tags, "foo") }
|
36
|
+
```
|
37
|
+
This will try the request 5 times, waiting 3 seconds between each attempt. If a retry fails after the given number
|
38
|
+
of `:tries` the original error will be raised.
|
39
|
+
|
40
|
+
You can also retry requests when other errors occur:
|
41
|
+
```rb
|
42
|
+
ShopifyAPIRetry.retry "5XX" => { :wait => 10, :tries => 2 } do
|
43
|
+
customer.update_attribute(:tags, "foo")
|
44
|
+
end
|
45
|
+
```
|
46
|
+
This still retries rate limit requests, but also all HTTP 5XX errors.
|
47
|
+
|
48
|
+
Classes can be specified too:
|
49
|
+
```rb
|
50
|
+
ShopifyAPIRetry.retry SocketError => { :wait => 1, :tries => 5 } do
|
51
|
+
customer.update_attribute(:tags, "foo")
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
Global defaults can be set as well:
|
56
|
+
```rb
|
57
|
+
ShopifyAPIRetry.configure do |config|
|
58
|
+
config.default_wait = 2.5
|
59
|
+
config.default_tries = 10
|
60
|
+
|
61
|
+
# Use defaults for these
|
62
|
+
config.on ["5XX", Net::TimeoutError]
|
63
|
+
|
64
|
+
config.on SocketError, :tries => 2, :wait => 1
|
65
|
+
end
|
66
|
+
|
67
|
+
ShopifyAPIRetry.retry { customer.update_attribute(:tags, "foo") }
|
68
|
+
```
|
69
|
+
|
31
70
|
## License
|
32
71
|
|
33
72
|
Released under the MIT License: www.opensource.org/licenses/MIT
|
data/Rakefile
CHANGED
data/lib/shopify_api_retry.rb
CHANGED
@@ -1,35 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "shopify_api"
|
2
4
|
|
3
5
|
module ShopifyAPIRetry
|
4
|
-
VERSION = "0.0
|
5
|
-
|
6
|
+
VERSION = "0.1.0"
|
7
|
+
|
8
|
+
HTTP_RETRY_AFTER = "Retry-After"
|
9
|
+
HTTP_RETRY_STATUS = "429"
|
10
|
+
|
11
|
+
class Config
|
12
|
+
attr_writer :default_wait
|
13
|
+
attr_writer :default_tries
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@settings = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def default_wait
|
20
|
+
@default_wait ||= nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def default_tries
|
24
|
+
@default_tries ||= 2
|
25
|
+
end
|
26
|
+
|
27
|
+
def on(errors, options = nil)
|
28
|
+
options = (options || {}).dup
|
29
|
+
options[:wait] ||= default_wait
|
30
|
+
options[:tries] ||= default_tries
|
31
|
+
|
32
|
+
Array(errors).each do |status_or_class|
|
33
|
+
@settings[status_or_class] = options
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def clear
|
38
|
+
@settings.clear
|
39
|
+
@default_wait = nil
|
40
|
+
@default_tries = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_h
|
44
|
+
settings = { HTTP_RETRY_STATUS => { :tries => default_tries, :wait => default_wait } }
|
45
|
+
@settings.each_with_object(settings) { |(k, v), o| o[k.to_s] = v.dup }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
@config = Config.new
|
6
50
|
|
51
|
+
def self.configure
|
52
|
+
return @config unless block_given?
|
53
|
+
yield @config
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.config
|
58
|
+
@config
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Execute the provided block. If an HTTP 429 response is returned try
|
63
|
+
# it again. If any errors are provided try them according to their wait/return spec.
|
7
64
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# is used. If it's not given (it always is) +2+ is used.
|
65
|
+
# If no spec is provided the value of the HTTP header <code>Retry-After</code>
|
66
|
+
# is waited for before retrying. If it's not given (it always is) +2+ is used.
|
11
67
|
#
|
12
|
-
# If retry fails the original error is raised
|
68
|
+
# If retry fails the original error is raised.
|
13
69
|
#
|
14
70
|
# Returns the value of the block.
|
15
71
|
#
|
16
|
-
def retry(
|
72
|
+
def retry(cfg = nil)
|
17
73
|
raise ArgumentError, "block required" unless block_given?
|
18
|
-
raise ArgumentError, "seconds to wait must be > 0" unless seconds_to_wait.nil? || seconds_to_wait > 0
|
19
74
|
|
20
|
-
|
21
|
-
retried = false
|
75
|
+
attempts = build_config(cfg)
|
22
76
|
|
23
77
|
begin
|
24
78
|
result = yield
|
25
|
-
rescue
|
26
|
-
|
27
|
-
raise
|
79
|
+
rescue => e
|
80
|
+
handler = attempts[e.class.name]
|
81
|
+
raise if handler.nil? && (!e.is_a?(ActiveResource::ClientError) || !e.response.respond_to?(:code))
|
28
82
|
|
29
|
-
|
30
|
-
|
83
|
+
handler ||= attempts[e.response.code] || attempts["#{e.response.code[0]}XX"]
|
84
|
+
handler[:wait] ||= e.response[HTTP_RETRY_AFTER] || config.default_wait if e.response.code == HTTP_RETRY_STATUS
|
85
|
+
|
86
|
+
handler[:attempts] ||= 1
|
87
|
+
raise if handler[:attempts] == handler[:tries]
|
88
|
+
|
89
|
+
snooze = handler[:wait].to_i
|
90
|
+
waited = sleep snooze
|
91
|
+
snooze -= waited
|
92
|
+
# Worth looping?
|
93
|
+
sleep snooze if snooze > 0
|
94
|
+
|
95
|
+
handler[:attempts] += 1
|
31
96
|
|
32
|
-
retried = true
|
33
97
|
retry
|
34
98
|
end
|
35
99
|
|
@@ -37,4 +101,32 @@ module ShopifyAPIRetry
|
|
37
101
|
end
|
38
102
|
|
39
103
|
module_function :retry
|
104
|
+
|
105
|
+
def self.build_config(userconfig)
|
106
|
+
config = ShopifyAPIRetry.config.to_h
|
107
|
+
return config unless userconfig
|
108
|
+
|
109
|
+
if userconfig.is_a?(Integer)
|
110
|
+
userconfig = { :wait => cfg }
|
111
|
+
warn "passing an Integer to retry is deprecated and will be removed, use an :wait => #{cfg} instead"
|
112
|
+
elsif !userconfig.is_a?(Hash)
|
113
|
+
raise ArgumentError, "config must be a Hash"
|
114
|
+
end
|
115
|
+
|
116
|
+
userconfig.each do |k, v|
|
117
|
+
if v.is_a?(Hash)
|
118
|
+
config[k.to_s] = v.dup
|
119
|
+
else
|
120
|
+
config[HTTP_RETRY_STATUS][k] = v
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
config.values.each do |cfg|
|
125
|
+
raise ArgumentError, "seconds to wait must be >= 0" if cfg[:wait] && cfg[:wait] < 0
|
126
|
+
end
|
127
|
+
|
128
|
+
config
|
129
|
+
end
|
130
|
+
|
131
|
+
private_class_method :build_config
|
40
132
|
end
|
data/shopify_api_retry.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "shopify_api_retry"
|
3
|
-
spec.version = "0.0
|
3
|
+
spec.version = "0.1.0"
|
4
4
|
spec.authors = ["Skye Shaw"]
|
5
5
|
spec.email = ["skye.shaw@gmail.com"]
|
6
6
|
|
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_dependency "shopify_api", ">= 4.0"
|
22
22
|
|
23
|
-
spec.add_development_dependency "
|
24
|
-
spec.add_development_dependency "
|
23
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
24
|
+
spec.add_development_dependency "bundler"
|
25
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
25
26
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shopify_api_retry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Skye Shaw
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: shopify_api
|
@@ -25,33 +25,47 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '5.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '5.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rake
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '12.0'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
68
|
+
version: '12.0'
|
55
69
|
description: Simple module to retry a ShopifyAPI request if an HTTP 429 (too many
|
56
70
|
requests) is returned. No monkey patching.
|
57
71
|
email:
|
@@ -60,6 +74,7 @@ executables: []
|
|
60
74
|
extensions: []
|
61
75
|
extra_rdoc_files: []
|
62
76
|
files:
|
77
|
+
- ".github/workflows/ci.yml"
|
63
78
|
- ".gitignore"
|
64
79
|
- ".travis.yml"
|
65
80
|
- Gemfile
|
@@ -88,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
103
|
version: '0'
|
89
104
|
requirements: []
|
90
105
|
rubyforge_project:
|
91
|
-
rubygems_version: 2.6
|
106
|
+
rubygems_version: 2.7.6
|
92
107
|
signing_key:
|
93
108
|
specification_version: 4
|
94
109
|
summary: Retry a ShopifyAPI request if an HTTP 429 (too many requests) is returned
|