stripe 5.8.0 → 5.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/README.md +26 -0
- data/VERSION +1 -1
- data/lib/stripe/instrumentation.rb +40 -0
- data/lib/stripe/stripe_client.rb +24 -4
- data/lib/stripe/version.rb +1 -1
- data/test/stripe/instrumentation_test.rb +61 -0
- data/test/stripe/stripe_client_test.rb +70 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b59015578d0b75db3e8b02959010a9d3a7a3c4212823ad8b51790a869e32b92a
|
4
|
+
data.tar.gz: cc8dd57b285effb4814596431b8da59fe92d1a7e34a76d5dc738250a10673142
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6154e0332f7b4c7d66c2238d4474ce904c0faa129dea9a1e1d03668a03d8b03796c593ddb7f04a76082251e6d08520996a28bd2427d897cb1dd769dfc1c3f8aa
|
7
|
+
data.tar.gz: 3d4bf185574c11c8c867b1998e4f07f1bce7b4dcaab166d475a5e2406e0cf2275e6d9890bf0df7f94523d36cc58c577819edaa9d301ba96b0d722d4806cbcdac
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 5.9.0 - 2019-11-07
|
4
|
+
* [#870](https://github.com/stripe/stripe-ruby/pull/870) Add request instrumentation callback (see `README.md` for usage example)
|
5
|
+
|
3
6
|
## 5.8.0 - 2019-11-05
|
4
7
|
* [#879](https://github.com/stripe/stripe-ruby/pull/879) Add support for `Mandate`
|
5
8
|
* [#876](https://github.com/stripe/stripe-ruby/pull/876) Add additional per-request configuration documentation
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Stripe Ruby Library
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/stripe.svg)](https://badge.fury.io/rb/stripe)
|
3
4
|
[![Build Status](https://travis-ci.org/stripe/stripe-ruby.svg?branch=master)](https://travis-ci.org/stripe/stripe-ruby)
|
4
5
|
[![Coverage Status](https://coveralls.io/repos/github/stripe/stripe-ruby/badge.svg?branch=master)](https://coveralls.io/github/stripe/stripe-ruby?branch=master)
|
5
6
|
|
@@ -221,6 +222,31 @@ There are a few options for enabling it:
|
|
221
222
|
Stripe.log_level = Stripe::LEVEL_INFO
|
222
223
|
```
|
223
224
|
|
225
|
+
### Instrumentation
|
226
|
+
|
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`)
|
236
|
+
|
237
|
+
For example:
|
238
|
+
```ruby
|
239
|
+
Stripe::Instrumentation.subscribe(:request) do |request_event|
|
240
|
+
tags = {
|
241
|
+
method: request_event.method,
|
242
|
+
resource: request_event.path.split("/")[2],
|
243
|
+
code: request_event.http_status,
|
244
|
+
retries: request_event.num_retries
|
245
|
+
}
|
246
|
+
StatsD.distribution('stripe_request', request_event.duration, tags: tags)
|
247
|
+
end
|
248
|
+
```
|
249
|
+
|
224
250
|
### Writing a Plugin
|
225
251
|
|
226
252
|
If you're writing a plugin that uses the library, we'd appreciate it if you
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
5.
|
1
|
+
5.9.0
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Stripe
|
4
|
+
class Instrumentation
|
5
|
+
class RequestEvent
|
6
|
+
attr_reader :duration
|
7
|
+
attr_reader :http_status
|
8
|
+
attr_reader :method
|
9
|
+
attr_reader :num_retries
|
10
|
+
attr_reader :path
|
11
|
+
|
12
|
+
def initialize(duration:, http_status:, method:, num_retries:, path:)
|
13
|
+
@duration = duration
|
14
|
+
@http_status = http_status
|
15
|
+
@method = method
|
16
|
+
@num_retries = num_retries
|
17
|
+
@path = path
|
18
|
+
freeze
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.subscribe(topic, name = rand, &block)
|
23
|
+
subscribers[topic][name] = block
|
24
|
+
name
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.unsubscribe(topic, name)
|
28
|
+
subscribers[topic].delete(name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.notify(topic, event)
|
32
|
+
subscribers[topic].each_value { |subscriber| subscriber.call(event) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.subscribers
|
36
|
+
@subscribers ||= Hash.new { |hash, key| hash[key] = {} }
|
37
|
+
end
|
38
|
+
private_class_method :subscribers
|
39
|
+
end
|
40
|
+
end
|
data/lib/stripe/stripe_client.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "stripe/instrumentation"
|
4
|
+
|
3
5
|
module Stripe
|
4
6
|
# StripeClient executes requests against the Stripe API and allows a user to
|
5
7
|
# recover both a resource a call returns as well as a response object that
|
@@ -452,15 +454,18 @@ module Stripe
|
|
452
454
|
request_start = Util.monotonic_time
|
453
455
|
log_request(context, num_retries)
|
454
456
|
resp = yield
|
457
|
+
request_duration = Util.monotonic_time - request_start
|
458
|
+
http_status = resp.code.to_i
|
455
459
|
context = context.dup_from_response_headers(resp)
|
456
460
|
|
457
|
-
handle_error_response(resp, context) if
|
461
|
+
handle_error_response(resp, context) if http_status >= 400
|
458
462
|
|
459
|
-
log_response(context, request_start,
|
463
|
+
log_response(context, request_start, http_status, resp.body)
|
464
|
+
notify_subscribers(request_duration, http_status, context,
|
465
|
+
num_retries)
|
460
466
|
|
461
467
|
if Stripe.enable_telemetry? && context.request_id
|
462
|
-
request_duration_ms =
|
463
|
-
((Util.monotonic_time - request_start) * 1000).to_int
|
468
|
+
request_duration_ms = (request_duration * 1000).to_i
|
464
469
|
@last_request_metrics =
|
465
470
|
StripeRequestMetrics.new(context.request_id, request_duration_ms)
|
466
471
|
end
|
@@ -472,14 +477,18 @@ module Stripe
|
|
472
477
|
# If we modify context we copy it into a new variable so as not to
|
473
478
|
# taint the original on a retry.
|
474
479
|
error_context = context
|
480
|
+
request_duration = Util.monotonic_time - request_start
|
481
|
+
http_status = nil
|
475
482
|
|
476
483
|
if e.is_a?(Stripe::StripeError)
|
477
484
|
error_context = context.dup_from_response_headers(e.http_headers)
|
485
|
+
http_status = resp.code.to_i
|
478
486
|
log_response(error_context, request_start,
|
479
487
|
e.http_status, e.http_body)
|
480
488
|
else
|
481
489
|
log_response_error(error_context, request_start, e)
|
482
490
|
end
|
491
|
+
notify_subscribers(request_duration, http_status, context, num_retries)
|
483
492
|
|
484
493
|
if self.class.should_retry?(e, method: method, num_retries: num_retries)
|
485
494
|
num_retries += 1
|
@@ -503,6 +512,17 @@ module Stripe
|
|
503
512
|
resp
|
504
513
|
end
|
505
514
|
|
515
|
+
private def notify_subscribers(duration, http_status, context, num_retries)
|
516
|
+
request_event = Instrumentation::RequestEvent.new(
|
517
|
+
duration: duration,
|
518
|
+
http_status: http_status,
|
519
|
+
method: context.method,
|
520
|
+
num_retries: num_retries,
|
521
|
+
path: context.path
|
522
|
+
)
|
523
|
+
Stripe::Instrumentation.notify(:request, request_event)
|
524
|
+
end
|
525
|
+
|
506
526
|
private def general_api_error(status, body)
|
507
527
|
APIError.new("Invalid response object from API: #{body.inspect} " \
|
508
528
|
"(HTTP response code was #{status})",
|
data/lib/stripe/version.rb
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../test_helper"
|
4
|
+
|
5
|
+
module Stripe
|
6
|
+
class InstrumentationTest < Test::Unit::TestCase
|
7
|
+
context ".notify" do
|
8
|
+
teardown do
|
9
|
+
Stripe::Instrumentation.send(:subscribers).clear
|
10
|
+
end
|
11
|
+
|
12
|
+
should "notify subscribers for the right topic" do
|
13
|
+
sub1_events = []
|
14
|
+
Stripe::Instrumentation.subscribe(:test1, :sub1) { |event| sub1_events << event }
|
15
|
+
sub2_events = []
|
16
|
+
Stripe::Instrumentation.subscribe(:test2, :sub2) { |event| sub2_events << event }
|
17
|
+
|
18
|
+
Stripe::Instrumentation.notify(:test1, "hello")
|
19
|
+
assert_equal(1, sub1_events.size)
|
20
|
+
assert_equal(0, sub2_events.size)
|
21
|
+
end
|
22
|
+
|
23
|
+
should "notify multiple subscribers of the same topic" do
|
24
|
+
sub1_events = []
|
25
|
+
Stripe::Instrumentation.subscribe(:test, :sub1) { |event| sub1_events << event }
|
26
|
+
sub2_events = []
|
27
|
+
Stripe::Instrumentation.subscribe(:test, :sub2) { |event| sub2_events << event }
|
28
|
+
|
29
|
+
Stripe::Instrumentation.notify(:test, "hello")
|
30
|
+
assert_equal(1, sub1_events.size)
|
31
|
+
assert_equal(1, sub2_events.size)
|
32
|
+
end
|
33
|
+
|
34
|
+
should "not notify a subscriber once it has unsubscribed" do
|
35
|
+
events = []
|
36
|
+
Stripe::Instrumentation.subscribe(:test, :sub) { |event| events << event }
|
37
|
+
|
38
|
+
Stripe::Instrumentation.notify(:test, "hello")
|
39
|
+
assert_equal(1, events.size)
|
40
|
+
|
41
|
+
Stripe::Instrumentation.unsubscribe(:test, :sub)
|
42
|
+
Stripe::Instrumentation.notify(:test, "hello")
|
43
|
+
assert_equal(1, events.size)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "RequestEvent" do
|
48
|
+
should "return a frozen object" do
|
49
|
+
event = Stripe::Instrumentation::RequestEvent.new(
|
50
|
+
duration: 0.1,
|
51
|
+
http_status: 200,
|
52
|
+
method: :get,
|
53
|
+
num_retries: 0,
|
54
|
+
path: "/v1/test"
|
55
|
+
)
|
56
|
+
|
57
|
+
assert(event.frozen?)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -1144,6 +1144,76 @@ module Stripe
|
|
1144
1144
|
assert(!trace_payload["last_request_metrics"]["request_duration_ms"].nil?)
|
1145
1145
|
end
|
1146
1146
|
end
|
1147
|
+
|
1148
|
+
context "instrumentation" do
|
1149
|
+
teardown do
|
1150
|
+
Stripe::Instrumentation.unsubscribe(:request, :test)
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
should "notify a subscriber of a successful HTTP request" do
|
1154
|
+
events = []
|
1155
|
+
Stripe::Instrumentation.subscribe(:request, :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
|
+
assert_equal(200, event.http_status)
|
1166
|
+
assert(event.duration.positive?)
|
1167
|
+
assert_equal(0, event.num_retries)
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
should "notify a subscriber of a StripeError" do
|
1171
|
+
events = []
|
1172
|
+
Stripe::Instrumentation.subscribe(:request, :test) { |event| events << event }
|
1173
|
+
|
1174
|
+
error = {
|
1175
|
+
code: "code",
|
1176
|
+
message: "message",
|
1177
|
+
param: "param",
|
1178
|
+
type: "type",
|
1179
|
+
}
|
1180
|
+
stub_request(:get, "#{Stripe.api_base}/v1/charges")
|
1181
|
+
.to_return(
|
1182
|
+
body: JSON.generate(error: error),
|
1183
|
+
status: 500
|
1184
|
+
)
|
1185
|
+
assert_raises(Stripe::APIError) do
|
1186
|
+
Stripe::Charge.list
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
assert_equal(1, events.size)
|
1190
|
+
event = events.first
|
1191
|
+
assert_equal(:get, event.method)
|
1192
|
+
assert_equal("/v1/charges", event.path)
|
1193
|
+
assert_equal(500, event.http_status)
|
1194
|
+
assert(event.duration.positive?)
|
1195
|
+
assert_equal(0, event.num_retries)
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
should "notify a subscriber of a network error" do
|
1199
|
+
events = []
|
1200
|
+
Stripe::Instrumentation.subscribe(:request, :test) { |event| events << event }
|
1201
|
+
|
1202
|
+
stub_request(:get, "#{Stripe.api_base}/v1/charges")
|
1203
|
+
.to_raise(Net::OpenTimeout)
|
1204
|
+
assert_raises(Stripe::APIConnectionError) do
|
1205
|
+
Stripe::Charge.list
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
assert_equal(1, events.size)
|
1209
|
+
event = events.first
|
1210
|
+
assert_equal(:get, event.method)
|
1211
|
+
assert_equal("/v1/charges", event.path)
|
1212
|
+
assert_nil(event.http_status)
|
1213
|
+
assert(event.duration.positive?)
|
1214
|
+
assert_equal(0, event.num_retries)
|
1215
|
+
end
|
1216
|
+
end
|
1147
1217
|
end
|
1148
1218
|
|
1149
1219
|
class SystemProfilerTest < Test::Unit::TestCase
|
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.
|
4
|
+
version: 5.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stripe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-11-
|
11
|
+
date: 2019-11-07 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.
|
@@ -49,6 +49,7 @@ files:
|
|
49
49
|
- lib/stripe/connection_manager.rb
|
50
50
|
- lib/stripe/error_object.rb
|
51
51
|
- lib/stripe/errors.rb
|
52
|
+
- lib/stripe/instrumentation.rb
|
52
53
|
- lib/stripe/list_object.rb
|
53
54
|
- lib/stripe/multipart_encoder.rb
|
54
55
|
- lib/stripe/oauth.rb
|
@@ -167,6 +168,7 @@ files:
|
|
167
168
|
- test/stripe/exchange_rate_test.rb
|
168
169
|
- test/stripe/file_link_test.rb
|
169
170
|
- test/stripe/file_test.rb
|
171
|
+
- test/stripe/instrumentation_test.rb
|
170
172
|
- test/stripe/invoice_item_test.rb
|
171
173
|
- test/stripe/invoice_line_item_test.rb
|
172
174
|
- test/stripe/invoice_test.rb
|
@@ -249,7 +251,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
249
251
|
- !ruby/object:Gem::Version
|
250
252
|
version: '0'
|
251
253
|
requirements: []
|
252
|
-
rubygems_version: 3.0.
|
254
|
+
rubygems_version: 3.0.6
|
253
255
|
signing_key:
|
254
256
|
specification_version: 4
|
255
257
|
summary: Ruby bindings for the Stripe API
|
@@ -283,6 +285,7 @@ test_files:
|
|
283
285
|
- test/stripe/exchange_rate_test.rb
|
284
286
|
- test/stripe/file_link_test.rb
|
285
287
|
- test/stripe/file_test.rb
|
288
|
+
- test/stripe/instrumentation_test.rb
|
286
289
|
- test/stripe/invoice_item_test.rb
|
287
290
|
- test/stripe/invoice_line_item_test.rb
|
288
291
|
- test/stripe/invoice_test.rb
|