stripe 5.14.0 → 5.15.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
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.