stripe 5.14.0 → 5.15.0

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
2
  SHA256:
3
- metadata.gz: 47a1fda0057d116729cd70477218913ecf097898f633952d287327ecfaf99133
4
- data.tar.gz: ed37e7cb7f025d48a11d075bc507cb6e84f92a36fc542f8d8bcff1bc8729dc50
3
+ metadata.gz: 1b0ca3c982da5bfe967d889047eeadbc2a2ba7ef6807dc860639a844f706cf15
4
+ data.tar.gz: 450bc197cbfd02398b3103aa89a5447f28bf2dd0f74608f3f54953e48e7ba4fd
5
5
  SHA512:
6
- metadata.gz: 84dda96e1a4da56d9643b13ae8bb6c6352f90baae770de860834461c73f3489501a81a03c3f98e4f0d0da9e6284c66111df743b9c907fbc82774979ab4f009d0
7
- data.tar.gz: 4d166d7eded67674487f2f1e678e86d11a6cb54f88848e185a55a4bca93831307f2dca9f8cc2b8608f1305d7193715ddba48e3bbe3baa0e5245b582fe3bd9090
6
+ metadata.gz: 84c4915401aaf4a8e546763a17f44395df5ed284e36e721c6ba8d04612bbe4af9cc3d6bdeb235aba10abb82f5b001039f48ff0077e4d7477aed2f165a9f616a3
7
+ data.tar.gz: e4b3cacde2e92739ad543c2287cb06aa5041d2daa29780c83b3c670f97bdd395be7c537dc880cb452402575112b1498e10417244aada07653aec0fa52e551389
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.15.0 - 2020-02-10
4
+ * [#902](https://github.com/stripe/stripe-ruby/pull/902) Add `request_begin` instrumentation callback
5
+
3
6
  ## 5.14.0 - 2020-01-14
4
7
  * [#896](https://github.com/stripe/stripe-ruby/pull/896) Add support for `CreditNoteLineItem`
5
8
  * [#894](https://github.com/stripe/stripe-ruby/pull/894) Clean up test output by capturing `$stderr` when we expect warnings
data/Gemfile CHANGED
@@ -11,7 +11,10 @@ group :development do
11
11
  gem "rake"
12
12
  gem "shoulda-context"
13
13
  gem "test-unit"
14
- gem "webmock"
14
+
15
+ # Version doesn't matter that much, but this one contains some fixes for Ruby
16
+ # 2.7 warnings that add noise to the test suite.
17
+ gem "webmock", ">= 3.8.0"
15
18
 
16
19
  # Rubocop changes pretty quickly: new cops get added and old cops change
17
20
  # names or go into new namespaces. This is a library and we don't have
data/README.md CHANGED
@@ -14,8 +14,6 @@ The library also provides other features. For example:
14
14
 
15
15
  - Easy configuration path for fast setup and use.
16
16
  - Helpers for pagination.
17
- - Tracking of "fresh" values in API resources so that partial updates can be
18
- executed.
19
17
  - Built-in mechanisms for the serialization of parameters according to the
20
18
  expectations of Stripe's API.
21
19
 
@@ -65,13 +63,11 @@ value:
65
63
  require "stripe"
66
64
  Stripe.api_key = "sk_test_..."
67
65
 
68
- # list charges
69
- Stripe::Charge.list()
66
+ # list customers
67
+ Stripe::Customer.list()
70
68
 
71
- # retrieve single charge
72
- Stripe::Charge.retrieve(
73
- "ch_18atAXCdGbJFKhCuBAa4532Z",
74
- )
69
+ # retrieve single customer
70
+ Stripe::Customer.retrieve("cus_123456789")
75
71
  ```
76
72
 
77
73
  ### Per-request Configuration
@@ -83,7 +79,7 @@ per-request key and/or account:
83
79
  ```ruby
84
80
  require "stripe"
85
81
 
86
- Stripe::Charge.list(
82
+ Stripe::Customer.list(
87
83
  {},
88
84
  {
89
85
  api_key: "sk_test_...",
@@ -92,8 +88,8 @@ Stripe::Charge.list(
92
88
  }
93
89
  )
94
90
 
95
- Stripe::Charge.retrieve(
96
- "ch_18atAXCdGbJFKhCuBAa4532Z",
91
+ Stripe::Customer.retrieve(
92
+ "cus_123456789",
97
93
  {
98
94
  api_key: "sk_test_...",
99
95
  stripe_account: "acct_...",
@@ -101,9 +97,9 @@ Stripe::Charge.retrieve(
101
97
  }
102
98
  )
103
99
 
104
- Stripe::Charge.retrieve(
100
+ Stripe::Customer.retrieve(
105
101
  {
106
- id: "ch_18atAXCdGbJFKhCuBAa4532Z",
102
+ id: "cus_123456789",
107
103
  expand: %w(balance_transaction)
108
104
  },
109
105
  {
@@ -112,8 +108,8 @@ Stripe::Charge.retrieve(
112
108
  }
113
109
  )
114
110
 
115
- Stripe::Charge.capture(
116
- "ch_18atAXCdGbJFKhCuBAa4532Z",
111
+ Stripe::Customer.capture(
112
+ "cus_123456789",
117
113
  {},
118
114
  {
119
115
  stripe_version: "2018-02-28",
@@ -123,6 +119,7 @@ Stripe::Charge.capture(
123
119
  ```
124
120
 
125
121
  Keep in mind that there are different method signatures depending on the action:
122
+
126
123
  - When operating on a collection (e.g. `.list`, `.create`) the method signature is
127
124
  `method(params, opts)`.
128
125
  - When operating on resource (e.g. `.capture`, `.update`) the method signature is
@@ -138,10 +135,8 @@ method:
138
135
 
139
136
  ```ruby
140
137
  client = Stripe::StripeClient.new
141
- charge, resp = client.request do
142
- Stripe::Charge.retrieve(
143
- "ch_18atAXCdGbJFKhCuBAa4532Z",
144
- )
138
+ customer, resp = client.request do
139
+ Stripe::Customer.retrieve("cus_123456789",)
145
140
  end
146
141
  puts resp.request_id
147
142
  ```
@@ -224,19 +219,43 @@ There are a few options for enabling it:
224
219
 
225
220
  ### Instrumentation
226
221
 
227
- The library has a hook for when a HTTP call is made which can be used for
228
- monitoring. The callback receives a `RequestEvent` object with the following
229
- data:
230
- - HTTP method (`Symbol`)
231
- - request path (`String`)
232
- - HTTP response code (`Integer`) if available, or `nil` in case of a lower
233
- level network error
234
- - request duration in seconds (`Float`)
235
- - the number of retries (`Integer`)
222
+ The library has various hooks that user code can tie into by passing a block to
223
+ `Stripe::Instrumentation.subscribe` to be notified about specific events.
224
+
225
+ #### `request_begin`
226
+
227
+ Invoked when an HTTP request starts. Receives `RequestBeginEvent` with the
228
+ following properties:
229
+
230
+ - `method`: HTTP method. (`Symbol`)
231
+ - `num_retries`: The number of retries. (`Integer`)
232
+ - `user_data`: A hash on which users can set arbitrary data, and which will be
233
+ passed through to `request_end` invocations. This could be used, for example,
234
+ to assign unique IDs to each request, and it'd work even if many requests are
235
+ running in parallel. All subscribers share the same object for any particular
236
+ request, so they must be careful to use unique keys that will not conflict
237
+ with other subscribers. (`Hash`)
238
+
239
+ #### `request_end`
240
+
241
+ Invoked when an HTTP request finishes, regardless of whether it terminated with
242
+ a success or error. Receives `RequestEndEvent` with the following properties:
243
+
244
+ - `duration`: Request duration in seconds. (`Float`)
245
+ - `http_status`: HTTP response code (`Integer`) if available, or `nil` in case
246
+ of a lower level network error.
247
+ - `method`: HTTP method. (`Symbol`)
248
+ - `num_retries`: The number of retries. (`Integer`)
249
+ - `path`: Request path. (`String`)
250
+ - `user_data`: A hash on which users may have set arbitrary data in
251
+ `request_begin`. See above for more information. (`Hash`)
252
+
253
+ #### Example
236
254
 
237
255
  For example:
256
+
238
257
  ```ruby
239
- Stripe::Instrumentation.subscribe(:request) do |request_event|
258
+ Stripe::Instrumentation.subscribe(:request_end) do |request_event|
240
259
  tags = {
241
260
  method: request_event.method,
242
261
  resource: request_event.path.split("/")[2],
data/VERSION CHANGED
@@ -1 +1 @@
1
- 5.14.0
1
+ 5.15.0
@@ -2,23 +2,65 @@
2
2
 
3
3
  module Stripe
4
4
  class Instrumentation
5
- class RequestEvent
5
+ # Event emitted on `request_begin` callback.
6
+ class RequestBeginEvent
7
+ attr_reader :method
8
+ attr_reader :path
9
+
10
+ # Arbitrary user-provided data in the form of a Ruby hash that's passed
11
+ # from subscribers on `request_begin` to subscribers on `request_end`.
12
+ # `request_begin` subscribers can set keys which will then be available
13
+ # in `request_end`.
14
+ #
15
+ # Note that all subscribers of `request_begin` share the same object, so
16
+ # they must be careful to set unique keys so as to not conflict with data
17
+ # set by other subscribers.
18
+ attr_reader :user_data
19
+
20
+ def initialize(method:, path:, user_data:)
21
+ @method = method
22
+ @path = path
23
+ @user_data = user_data
24
+ freeze
25
+ end
26
+ end
27
+
28
+ # Event emitted on `request_end` callback.
29
+ class RequestEndEvent
6
30
  attr_reader :duration
7
31
  attr_reader :http_status
8
32
  attr_reader :method
9
33
  attr_reader :num_retries
10
34
  attr_reader :path
11
35
 
12
- def initialize(duration:, http_status:, method:, num_retries:, path:)
36
+ # Arbitrary user-provided data in the form of a Ruby hash that's passed
37
+ # from subscribers on `request_begin` to subscribers on `request_end`.
38
+ # `request_begin` subscribers can set keys which will then be available
39
+ # in `request_end`.
40
+ attr_reader :user_data
41
+
42
+ def initialize(duration:, http_status:, method:, num_retries:, path:,
43
+ user_data: nil)
13
44
  @duration = duration
14
45
  @http_status = http_status
15
46
  @method = method
16
47
  @num_retries = num_retries
17
48
  @path = path
49
+ @user_data = user_data
18
50
  freeze
19
51
  end
20
52
  end
21
53
 
54
+ # This class was renamed for consistency. This alias is here for backwards
55
+ # compatibility.
56
+ RequestEvent = RequestEndEvent
57
+
58
+ # Returns true if there are a non-zero number of subscribers on the given
59
+ # topic, and false otherwise.
60
+ def self.any_subscribers?(topic)
61
+ !subscribers[topic].empty?
62
+ end
63
+
22
64
  def self.subscribe(topic, name = rand, &block)
23
65
  subscribers[topic][name] = block
24
66
  name
@@ -450,19 +450,26 @@ module Stripe
450
450
 
451
451
  private def execute_request_with_rescues(method, api_base, context)
452
452
  num_retries = 0
453
+
453
454
  begin
454
- request_start = Util.monotonic_time
455
+ request_start = nil
456
+ user_data = nil
457
+
455
458
  log_request(context, num_retries)
459
+ user_data = notify_request_begin(context)
460
+
461
+ request_start = Util.monotonic_time
456
462
  resp = yield
457
463
  request_duration = Util.monotonic_time - request_start
464
+
458
465
  http_status = resp.code.to_i
459
466
  context = context.dup_from_response_headers(resp)
460
467
 
461
468
  handle_error_response(resp, context) if http_status >= 400
462
469
 
463
470
  log_response(context, request_start, http_status, resp.body)
464
- notify_subscribers(request_duration, http_status, context,
465
- num_retries)
471
+ notify_request_end(context, request_duration, http_status,
472
+ num_retries, user_data)
466
473
 
467
474
  if Stripe.enable_telemetry? && context.request_id
468
475
  request_duration_ms = (request_duration * 1000).to_i
@@ -477,8 +484,8 @@ module Stripe
477
484
  # If we modify context we copy it into a new variable so as not to
478
485
  # taint the original on a retry.
479
486
  error_context = context
480
- request_duration = Util.monotonic_time - request_start
481
487
  http_status = nil
488
+ request_duration = Util.monotonic_time - request_start if request_start
482
489
 
483
490
  if e.is_a?(Stripe::StripeError)
484
491
  error_context = context.dup_from_response_headers(e.http_headers)
@@ -488,7 +495,8 @@ module Stripe
488
495
  else
489
496
  log_response_error(error_context, request_start, e)
490
497
  end
491
- notify_subscribers(request_duration, http_status, context, num_retries)
498
+ notify_request_end(context, request_duration, http_status, num_retries,
499
+ user_data)
492
500
 
493
501
  if self.class.should_retry?(e, method: method, num_retries: num_retries)
494
502
  num_retries += 1
@@ -512,15 +520,39 @@ module Stripe
512
520
  resp
513
521
  end
514
522
 
515
- private def notify_subscribers(duration, http_status, context, num_retries)
516
- request_event = Instrumentation::RequestEvent.new(
523
+ private def notify_request_begin(context)
524
+ return unless Instrumentation.any_subscribers?(:request_begin)
525
+
526
+ event = Instrumentation::RequestBeginEvent.new(
527
+ method: context.method,
528
+ path: context.path,
529
+ user_data: {}
530
+ )
531
+ Stripe::Instrumentation.notify(:request_begin, event)
532
+
533
+ # This field may be set in the `request_begin` callback. If so, we'll
534
+ # forward it onto `request_end`.
535
+ event.user_data
536
+ end
537
+
538
+ private def notify_request_end(context, duration, http_status, num_retries,
539
+ user_data)
540
+ return if !Instrumentation.any_subscribers?(:request_end) &&
541
+ !Instrumentation.any_subscribers?(:request)
542
+
543
+ event = Instrumentation::RequestEndEvent.new(
517
544
  duration: duration,
518
545
  http_status: http_status,
519
546
  method: context.method,
520
547
  num_retries: num_retries,
521
- path: context.path
548
+ path: context.path,
549
+ user_data: user_data || {}
522
550
  )
523
- Stripe::Instrumentation.notify(:request, request_event)
551
+ Stripe::Instrumentation.notify(:request_end, event)
552
+
553
+ # The name before `request_begin` was also added. Provided for backwards
554
+ # compatibility.
555
+ Stripe::Instrumentation.notify(:request, event)
524
556
  end
525
557
 
526
558
  private def general_api_error(status, body)
@@ -772,8 +804,9 @@ module Stripe
772
804
  end
773
805
 
774
806
  private def log_response_error(context, request_start, error)
807
+ elapsed = request_start ? Util.monotonic_time - request_start : nil
775
808
  Util.log_error("Request error",
776
- elapsed: Util.monotonic_time - request_start,
809
+ elapsed: elapsed,
777
810
  error_message: error.message,
778
811
  idempotency_key: context.idempotency_key,
779
812
  method: context.method,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stripe
4
- VERSION = "5.14.0"
4
+ VERSION = "5.15.0"
5
5
  end
@@ -44,14 +44,27 @@ module Stripe
44
44
  end
45
45
  end
46
46
 
47
- context "RequestEvent" do
47
+ context "RequestEventBegin" do
48
48
  should "return a frozen object" do
49
- event = Stripe::Instrumentation::RequestEvent.new(
49
+ event = Stripe::Instrumentation::RequestBeginEvent.new(
50
+ method: :get,
51
+ path: "/v1/test",
52
+ user_data: nil
53
+ )
54
+
55
+ assert(event.frozen?)
56
+ end
57
+ end
58
+
59
+ context "RequestEventEnd" do
60
+ should "return a frozen object" do
61
+ event = Stripe::Instrumentation::RequestEndEvent.new(
50
62
  duration: 0.1,
51
63
  http_status: 200,
52
64
  method: :get,
53
65
  num_retries: 0,
54
- path: "/v1/test"
66
+ path: "/v1/test",
67
+ user_data: nil
55
68
  )
56
69
 
57
70
  assert(event.frozen?)
@@ -1150,9 +1150,23 @@ module Stripe
1150
1150
  Stripe::Instrumentation.unsubscribe(:request, :test)
1151
1151
  end
1152
1152
 
1153
+ should "notify a subscribe on HTTP request start" do
1154
+ events = []
1155
+ Stripe::Instrumentation.subscribe(:request_end, :test) { |event| events << event }
1156
+
1157
+ stub_request(:get, "#{Stripe.api_base}/v1/charges")
1158
+ .to_return(body: JSON.generate(object: "charge"))
1159
+ Stripe::Charge.list
1160
+
1161
+ assert_equal(1, events.size)
1162
+ event = events.first
1163
+ assert_equal(:get, event.method)
1164
+ assert_equal("/v1/charges", event.path)
1165
+ end
1166
+
1153
1167
  should "notify a subscriber of a successful HTTP request" do
1154
1168
  events = []
1155
- Stripe::Instrumentation.subscribe(:request, :test) { |event| events << event }
1169
+ Stripe::Instrumentation.subscribe(:request_end, :test) { |event| events << event }
1156
1170
 
1157
1171
  stub_request(:get, "#{Stripe.api_base}/v1/charges")
1158
1172
  .to_return(body: JSON.generate(object: "charge"))
@@ -1169,7 +1183,7 @@ module Stripe
1169
1183
 
1170
1184
  should "notify a subscriber of a StripeError" do
1171
1185
  events = []
1172
- Stripe::Instrumentation.subscribe(:request, :test) { |event| events << event }
1186
+ Stripe::Instrumentation.subscribe(:request_end, :test) { |event| events << event }
1173
1187
 
1174
1188
  error = {
1175
1189
  code: "code",
@@ -1197,7 +1211,7 @@ module Stripe
1197
1211
 
1198
1212
  should "notify a subscriber of a network error" do
1199
1213
  events = []
1200
- Stripe::Instrumentation.subscribe(:request, :test) { |event| events << event }
1214
+ Stripe::Instrumentation.subscribe(:request_end, :test) { |event| events << event }
1201
1215
 
1202
1216
  stub_request(:get, "#{Stripe.api_base}/v1/charges")
1203
1217
  .to_raise(Net::OpenTimeout)
@@ -1213,6 +1227,40 @@ module Stripe
1213
1227
  assert(event.duration.positive?)
1214
1228
  assert_equal(0, event.num_retries)
1215
1229
  end
1230
+
1231
+ should "pass `user_data` from `request_begin` to `request_end`" do
1232
+ actual_user_data = nil
1233
+
1234
+ Stripe::Instrumentation.subscribe(:request_begin) do |event|
1235
+ event.user_data[:foo] = :bar
1236
+ end
1237
+ Stripe::Instrumentation.subscribe(:request_end) do |event|
1238
+ actual_user_data = event.user_data
1239
+ end
1240
+
1241
+ stub_request(:get, "#{Stripe.api_base}/v1/charges")
1242
+ .to_return(body: JSON.generate(object: "charge"))
1243
+ Stripe::Charge.list
1244
+
1245
+ assert_equal({ foo: :bar }, actual_user_data)
1246
+ end
1247
+
1248
+ should "provide backward compatibility on `request` topic" do
1249
+ events = []
1250
+ Stripe::Instrumentation.subscribe(:request, :test) { |event| events << event }
1251
+
1252
+ stub_request(:get, "#{Stripe.api_base}/v1/charges")
1253
+ .to_return(body: JSON.generate(object: "charge"))
1254
+ Stripe::Charge.list
1255
+
1256
+ assert_equal(1, events.size)
1257
+ event = events.first
1258
+ assert_equal(:get, event.method)
1259
+ assert_equal("/v1/charges", event.path)
1260
+ assert_equal(200, event.http_status)
1261
+ assert(event.duration.positive?)
1262
+ assert_equal(0, event.num_retries)
1263
+ end
1216
1264
  end
1217
1265
  end
1218
1266
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stripe
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.14.0
4
+ version: 5.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stripe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-14 00:00:00.000000000 Z
11
+ date: 2020-02-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Stripe is the easiest way to accept payments online. See https://stripe.com
14
14
  for details.