payjp 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 27457effbc47ae96c13305248fc780f9e2f1ca00
4
- data.tar.gz: 4a06532b2b0eaf162e2db5b3795584f92d98d3ef
2
+ SHA256:
3
+ metadata.gz: 21b1328487d0db17d3b7e78afe2b1a9f00df6506c2da1b896c216eec238b4936
4
+ data.tar.gz: 6416cc480d88f3fcf135ed0005ee37a2d2d8d70e4d10c7ed4fa68f9a408dbe78
5
5
  SHA512:
6
- metadata.gz: b65d4c3f8df6e10e198dc8a289210adbd06098d8e8220497d2d1f40d5315c6bb775ab7ecb73d94b87b6374bbb6b6116fdeae486ddab71512c95311b64e3556bd
7
- data.tar.gz: ee4ee024077834fd5d9840a251784dd568ea4e0c33e2893c663d100b91cf8c6f89951e49d604c57641819ba82304ece75bb62619524a10ec6165fc04187a4612
6
+ metadata.gz: 9d907d975eb58780bd31183c3e6fd68fa99c21fa34bcbe545be98b8d9f18453d822c1cb98e7f6020235dbcf70aeedaf3e2b5022132b97c62671be847d5326923
7
+ data.tar.gz: 0be34aabaceb678126e61f71d8a0832140972f554789e135e043693996cf288875aa196383fe3f97b87073e448df265ed028ffc32c3bf26ac1a3ce10226a7be7
@@ -0,0 +1,20 @@
1
+ name: Ruby Build Test
2
+
3
+ on: push
4
+
5
+ jobs:
6
+ build-test:
7
+
8
+ runs-on: ubuntu-latest
9
+ strategy:
10
+ matrix:
11
+ ruby-version: [2.0.0, 2.1, 2.2, 2.3.0, jruby-9.2.17.0]
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+ - name: Set up Ruby ${{ matrix.ruby-version }}
15
+ uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: ${{ matrix.ruby-version }}
18
+ bundler-cache: true
19
+ - name: Run tests
20
+ run: bundle exec rake
data/lib/payjp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Payjp
2
- VERSION = '0.0.7'
2
+ VERSION = '0.0.8'
3
3
  end
data/lib/payjp.rb CHANGED
@@ -50,23 +50,39 @@ module Payjp
50
50
  @ssl_ca_file = nil
51
51
  @ssl_ca_path = nil
52
52
  @ssl_cert_store = nil
53
+ @max_retry = 0
54
+ @retry_initial_delay = 2
55
+ @retry_max_delay = 32
53
56
 
54
57
  class << self
55
58
  attr_accessor :api_key, :api_base, :api_version, :connect_base, :uploads_base,
56
- :open_timeout, :read_timeout, :ssl_ca_file, :ssl_ca_path, :ssl_cert_store
59
+ :open_timeout, :read_timeout, :ssl_ca_file, :ssl_ca_path, :ssl_cert_store, :max_retry, :retry_initial_delay, :retry_max_delay
57
60
  end
58
61
 
59
62
  def self.api_url(url = '', api_base_url = nil)
60
63
  (api_base_url || @api_base) + url
61
64
  end
62
65
 
63
- def self.request(method, url, api_key, params = {}, headers = {}, api_base_url = nil, open_timeout = nil, read_timeout = nil, ssl_ca_file = nil, ssl_ca_path = nil, ssl_cert_store = nil)
66
+ def self.get_retry_delay(retry_count, retry_initial_delay, retry_max_delay)
67
+ # Get retry delay seconds.
68
+ # Based on "Exponential backoff with equal jitter" algorithm.
69
+ # https://aws.amazon.com/jp/blogs/architecture/exponential-backoff-and-jitter/
70
+
71
+ wait = [retry_max_delay, retry_initial_delay * 2 ** retry_count].min
72
+ random = Random.new()
73
+ (wait / 2) + (random.rand(wait / 2.0))
74
+ end
75
+
76
+ def self.request(method, url, api_key, params = {}, headers = {}, api_base_url = nil, open_timeout = nil, read_timeout = nil, ssl_ca_file = nil, ssl_ca_path = nil, ssl_cert_store = nil, max_retry = nil, retry_initial_delay= nil, retry_max_delay = nil)
64
77
  api_base_url ||= @api_base
65
78
  open_timeout ||= @open_timeout
66
79
  read_timeout ||= @read_timeout
67
80
  ssl_ca_file ||= @ssl_ca_file
68
81
  ssl_ca_path ||= @ssl_ca_path
69
82
  ssl_cert_store ||= @ssl_cert_store
83
+ max_retry ||= @max_retry
84
+ retry_initial_delay ||= @retry_initial_delay
85
+ retry_max_delay ||= @retry_max_delay
70
86
 
71
87
  unless api_key ||= @api_key
72
88
  raise AuthenticationError.new('No API key provided. ' \
@@ -107,6 +123,8 @@ module Payjp
107
123
  :ssl_ca_file => ssl_ca_file, :ssl_ca_path => ssl_ca_path,
108
124
  :ssl_cert_store => ssl_cert_store)
109
125
 
126
+ retry_count = 1
127
+
110
128
  begin
111
129
  # $stderr.puts request_opts
112
130
 
@@ -122,6 +140,12 @@ module Payjp
122
140
  raise
123
141
  end
124
142
  rescue RestClient::ExceptionWithResponse => e
143
+ if e.http_code == 429 and retry_count <= max_retry then
144
+ sleep get_retry_delay(retry_count, retry_initial_delay, retry_max_delay)
145
+ retry_count += 1
146
+ retry
147
+ end
148
+
125
149
  if rcode = e.http_code and rbody = e.http_body
126
150
  handle_api_error(rcode, rbody)
127
151
  else
@@ -239,6 +263,8 @@ module Payjp
239
263
  raise authentication_error error, rcode, rbody, error_obj
240
264
  when 402
241
265
  raise card_error error, rcode, rbody, error_obj
266
+ when 429
267
+ raise api_error error, rcode, rbody, error_obj
242
268
  else
243
269
  raise api_error error, rcode, rbody, error_obj
244
270
  end
data/payjp.gemspec CHANGED
@@ -23,5 +23,8 @@ Gem::Specification.new do |s|
23
23
  s.files = `git ls-files`.split("\n")
24
24
  s.test_files = `git ls-files -- test/*`.split("\n")
25
25
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
26
- s.require_paths = ['lib']
26
+ s.require_paths = ['lib']
27
+ s.metadata = {
28
+ "source_code_uri" => "https://github.com/payjp/payjp-ruby",
29
+ }
27
30
  end
@@ -411,6 +411,25 @@ module Payjp
411
411
 
412
412
  assert_equal true, rescued
413
413
  end
414
+
415
+ should "429s should raise a APIError of over capacity error code" do
416
+ response = test_response(test_over_capacity_error, 429)
417
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 429))
418
+
419
+ rescued = false
420
+ begin
421
+ Payjp::Customer.new("test_customer").refresh
422
+ assert false # shouldn't get here either
423
+ rescue Payjp::APIError => e # we don't use assert_raises because we want to examine e
424
+ rescued = true
425
+ assert e.is_a? Payjp::APIError
426
+ assert_equal(429, e.http_status)
427
+ assert_equal(true, !!e.http_body)
428
+ assert_equal("over_capacity", e.json_body[:error][:code])
429
+ end
430
+
431
+ assert_equal true, rescued
432
+ end
414
433
  end
415
434
 
416
435
  should 'save nothing if nothing changes' do
@@ -442,4 +461,120 @@ module Payjp
442
461
  end
443
462
  end
444
463
  end
464
+
465
+ class APIRequestorRetryTest < Test::Unit::TestCase
466
+
467
+ setup do
468
+ Payjp.max_retry = 0
469
+ Payjp.retry_initial_delay = 0.1
470
+ end
471
+
472
+ def mock_exceptions(error_codes, call_count)
473
+ exception_mock = nil
474
+ error_codes.with_index(1) { |code, id|
475
+ if id == 1
476
+ exception_mock = @mock.expects(:get).raises(RestClient::ExceptionWithResponse.new(test_response(test_api_error, code)))
477
+ else
478
+ exception_mock = exception_mock.then.raises(RestClient::ExceptionWithResponse.new(test_response(test_api_error, code)))
479
+ end
480
+ }
481
+ exception_mock.at_least(call_count)
482
+ end
483
+
484
+ context "checking retry" do
485
+ should "over capacity retry disabled" do
486
+ error_codes = [429, 599].to_enum
487
+ # returns 599 at 2nd try but max_retry 0 then retry disabled
488
+ mock_exceptions(error_codes, 1)
489
+ rescued = false
490
+
491
+ begin
492
+ Payjp::Charge.retrieve("test_charge")
493
+ rescue Payjp::APIError => e # we don't use assert_raises because we want to examine e
494
+ assert e.is_a? Payjp::APIError
495
+ assert_equal(429, e.http_status)
496
+ rescued = true
497
+ end
498
+ assert_equal true, rescued
499
+ end
500
+
501
+ should "no retry" do
502
+ Payjp.max_retry = 2
503
+ Payjp.retry_initial_delay = 0.1
504
+ error_codes = [599, 429, 429, 429].to_enum
505
+ # returns 599 at first try
506
+ mock_exceptions(error_codes, 1)
507
+ rescued = false
508
+
509
+ begin
510
+ Payjp::Charge.retrieve("test_charge")
511
+ rescue Payjp::APIError => e # we don't use assert_raises because we want to examine e
512
+ assert e.is_a? Payjp::APIError
513
+ assert_equal(599, e.http_status)
514
+ assert_equal(true, !!e.http_body)
515
+ rescued = true
516
+ end
517
+ assert_equal true, rescued
518
+ end
519
+
520
+ should "over capacity full retry" do
521
+ Payjp.max_retry = 2
522
+ Payjp.retry_initial_delay = 0.1
523
+ error_codes = [429, 429, 429, 429, 599].to_enum
524
+
525
+ # first try + 2 retries + unexpected 599
526
+ mock_exceptions(error_codes, 3)
527
+ rescued = false
528
+
529
+ begin
530
+ Payjp::Charge.retrieve("test_charge")
531
+ rescue Payjp::APIError => e # we don't use assert_raises because we want to examine e
532
+ assert e.is_a? Payjp::APIError
533
+ assert_equal(429, e.http_status)
534
+ rescued = true
535
+ end
536
+
537
+ assert_equal true, rescued
538
+ end
539
+
540
+ should "over capacity halfway of retries" do
541
+ Payjp.max_retry = 5
542
+ Payjp.retry_initial_delay = 0.1
543
+
544
+ error_codes = [429, 599, 429, 429, 429].to_enum
545
+ rescued = false
546
+
547
+ # returns not 429 status at 2nd try
548
+ mock_exceptions(error_codes, 2)
549
+ begin
550
+ Payjp::Charge.retrieve("test_charge")
551
+ rescue Payjp::APIError => e # we don't use assert_raises because we want to examine e
552
+ assert e.is_a? Payjp::APIError
553
+ assert_equal(599, e.http_status)
554
+ rescued = true
555
+ end
556
+
557
+ assert_equal true, rescued
558
+ end
559
+ end
560
+
561
+ context "retry interval" do
562
+ should "retry initial delay" do
563
+ retry_initial_delay = 2
564
+ retry_max_delay = 32
565
+
566
+ assert_equal(true, Payjp.get_retry_delay(0, retry_initial_delay, retry_max_delay).between?(1, 2))
567
+ assert_equal(true, Payjp.get_retry_delay(1, retry_initial_delay, retry_max_delay).between?(2, 4))
568
+ assert_equal(true, Payjp.get_retry_delay(2, retry_initial_delay, retry_max_delay).between?(4, 8))
569
+ # cap
570
+ assert_equal(true, Payjp.get_retry_delay(4, retry_initial_delay, retry_max_delay).between?(16, 32))
571
+ assert_equal(true, Payjp.get_retry_delay(10, retry_initial_delay, retry_max_delay).between?(16, 32))
572
+ end
573
+ end
574
+
575
+ teardown do
576
+ Payjp.max_retry = 0
577
+ Payjp.retry_initial_delay = 0.1
578
+ end
579
+ end
445
580
  end
data/test/test_data.rb CHANGED
@@ -287,5 +287,14 @@ module Payjp
287
287
  }
288
288
  }
289
289
  end
290
+
291
+ def test_over_capacity_error
292
+ {
293
+ :error => {
294
+ :code => "over_capacity",
295
+ :type => "api_error"
296
+ }
297
+ }
298
+ end
290
299
  end
291
300
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: payjp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - PAY.JP
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-22 00:00:00.000000000 Z
11
+ date: 2021-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rest-client
@@ -107,10 +107,10 @@ executables: []
107
107
  extensions: []
108
108
  extra_rdoc_files: []
109
109
  files:
110
+ - ".github/workflows/test.yml"
110
111
  - ".gitignore"
111
112
  - ".rubocop.yml"
112
113
  - ".rubocop_todo.yml"
113
- - ".travis.yml"
114
114
  - CONTRIBUTORS
115
115
  - Gemfile
116
116
  - History.txt
@@ -163,8 +163,9 @@ files:
163
163
  homepage: https://pay.jp
164
164
  licenses:
165
165
  - MIT
166
- metadata: {}
167
- post_install_message:
166
+ metadata:
167
+ source_code_uri: https://github.com/payjp/payjp-ruby
168
+ post_install_message:
168
169
  rdoc_options: []
169
170
  require_paths:
170
171
  - lib
@@ -179,9 +180,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
179
180
  - !ruby/object:Gem::Version
180
181
  version: '0'
181
182
  requirements: []
182
- rubyforge_project:
183
- rubygems_version: 2.5.2.3
184
- signing_key:
183
+ rubygems_version: 3.1.2
184
+ signing_key:
185
185
  specification_version: 4
186
186
  summary: Ruby bindings for the Payjp API
187
187
  test_files:
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- language: ruby
2
-
3
- rvm:
4
- - 2.0.0
5
- - 2.1
6
- - 2.2
7
- - 2.3.0
8
- - jruby-9.0.5.0
9
-
10
- sudo: false