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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9f6d412f4f64e2d2ac4d7e741008526b7a71b767
4
- data.tar.gz: 1db395165fe1466210334c70efb1db523242837b
2
+ SHA256:
3
+ metadata.gz: 949d65d19ed205d18a64ea886ef817bcd9d8b26895036228dddc3dcf4d91f0ca
4
+ data.tar.gz: 6b776cf5985be1593e791a81c6f4ce31c88ca4f029c7ba03bd8e24ca5f44f26f
5
5
  SHA512:
6
- metadata.gz: 871762fa4d151434d30a088cdaad6c2e2da6d5207b3a2671fe0e7ee1076b9f1b7a179e238596841463eb9dbde4023e4d0c542ae350dfd06f7d85cce6b87226c6
7
- data.tar.gz: b0d9c33fbf726213a3eba185df8dac8839539a29867c53a15b5730dd535946efbd4f19bac143788c4084f17cec0bcbac9a35edaecf19369edd5ee6f49ebb2e09
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
- Simple module to retry a [ShopifyAPI](https://github.com/Shopify/shopify_api) request if an HTTP 429 (too many requests) is returned. No monkey patching.
3
+ ![CI](https://github.com/ScreenStaring/shopify_api_retry/workflows/CI/badge.svg)
4
4
 
5
- ## Usage
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
@@ -1 +1,8 @@
1
+ require "rake"
2
+ require "rake/testtask"
1
3
  require "bundler/gem_tasks"
4
+
5
+ task :default => :test
6
+ Rake::TestTask.new do |t|
7
+ t.pattern = "spec/*_spec.rb"
8
+ end
@@ -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.1".freeze
5
- HTTP_RETRY_AFTER = "Retry-After".freeze
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
- # Execute the provided block. If an HTTP 429 response is return try
9
- # it again. If no retry time is provided the value of the HTTP header <code>Retry-After</code>
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 (`ActiveResource::ClientError` or subclass).
68
+ # If retry fails the original error is raised.
13
69
  #
14
70
  # Returns the value of the block.
15
71
  #
16
- def retry(seconds_to_wait = nil)
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
- result = nil
21
- retried = false
75
+ attempts = build_config(cfg)
22
76
 
23
77
  begin
24
78
  result = yield
25
- rescue ActiveResource::ClientError => e
26
- # Not 100% if we need to check for code method, I think I saw a NoMethodError...
27
- raise unless !retried && e.response.respond_to?(:code) && e.response.code.to_i == 429
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
- seconds_to_wait = (e.response[HTTP_RETRY_AFTER] || 2).to_i unless seconds_to_wait
30
- sleep seconds_to_wait
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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "shopify_api_retry"
3
- spec.version = "0.0.1"
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 "bundler", "~> 1.16"
24
- spec.add_development_dependency "rake", "~> 10.0"
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.1
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: 2019-07-08 00:00:00.000000000 Z
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: bundler
28
+ name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.16'
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: '1.16'
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: '10.0'
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: '10.0'
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.14
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