stripe 5.34.0 → 5.35.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: bc39a48fe4d3cca6e57eb21851bf619e07addfbf5e4451d7c2669580c63ee18c
4
- data.tar.gz: 74bd79d861284ffc4e53d34402db13c91742db91d66e522b56b6cd5b287ab4ed
3
+ metadata.gz: f51ef2fd825e626807098544261c034bf55a84cf59852a7ac1c88a213add62ca
4
+ data.tar.gz: 6df0829f6b372f47393a5a22cc8aba022c40620188c0e8c3619d41965d84837b
5
5
  SHA512:
6
- metadata.gz: 973a5efa1c8988bd88dc6b58862e0b5559367162fe982d35d575583706d7ebf868d5330eba0e4e00e4139d70afd297b9826c4059e4f418fc503cebf086c7f0a2
7
- data.tar.gz: f0b14e6f6b69642f26635adef651bfdfb07c3f2cc1b66e11dbeb86518ae79e7e60ec9561293f4b83fb15bb6c8d01da12222605766bb664bc06a710ac9cfe3d6f
6
+ metadata.gz: 1d0c7c97de7a70c94ebd08659f6a0630f3508347772d2ed7a799778f2c08db2e6a1540a9b77b377f31edeadc6d5a874da4e084994c8ce23e4690512db083c987
7
+ data.tar.gz: 180af07a05467d75b0a88907bf59a0f837981ab4cd6ca16673ec4f17bedcff33e7efcf80ff482cd14517b9a2efa25095ce018011fc82b70d62a20bddc1cd2525
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.35.0 - 2021-06-30
4
+ * [#985](https://github.com/stripe/stripe-ruby/pull/985) Update normalize_opts to use dup instead of clone.
5
+ * [#982](https://github.com/stripe/stripe-ruby/pull/982) Deprecate travis
6
+ * [#983](https://github.com/stripe/stripe-ruby/pull/983) Add support for making a request and receiving the response as a stream.
7
+
3
8
  ## 5.34.0 - 2021-06-04
4
9
  * [#981](https://github.com/stripe/stripe-ruby/pull/981) API Updates
5
10
  * Add support for `TaxCode` API.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 5.34.0
1
+ 5.35.0
@@ -6,6 +6,28 @@ module Stripe
6
6
  module ClassMethods
7
7
  def execute_resource_request(method, url,
8
8
  params = {}, opts = {})
9
+ execute_resource_request_internal(
10
+ :execute_request, method, url, params, opts
11
+ )
12
+ end
13
+
14
+ def execute_resource_request_stream(method, url,
15
+ params = {}, opts = {},
16
+ &read_body_chunk_block)
17
+ execute_resource_request_internal(
18
+ :execute_request_stream,
19
+ method,
20
+ url,
21
+ params,
22
+ opts,
23
+ &read_body_chunk_block
24
+ )
25
+ end
26
+
27
+ private def execute_resource_request_internal(client_request_method_sym,
28
+ method, url,
29
+ params, opts,
30
+ &read_body_chunk_block)
9
31
  params ||= {}
10
32
 
11
33
  error_on_invalid_params(params)
@@ -22,10 +44,12 @@ module Stripe
22
44
  client = headers.delete(:client)
23
45
  # Assume all remaining opts must be headers
24
46
 
25
- resp, opts[:api_key] = client.execute_request(
47
+ resp, opts[:api_key] = client.send(
48
+ client_request_method_sym,
26
49
  method, url,
27
50
  api_base: api_base, api_key: api_key,
28
- headers: headers, params: params
51
+ headers: headers, params: params,
52
+ &read_body_chunk_block
29
53
  )
30
54
 
31
55
  # Hash#select returns an array before 1.9
@@ -89,6 +113,15 @@ module Stripe
89
113
  self.class.execute_resource_request(method, url, params, opts)
90
114
  end
91
115
 
116
+ protected def execute_resource_request_stream(method, url,
117
+ params = {}, opts = {},
118
+ &read_body_chunk_block)
119
+ opts = @opts.merge(Util.normalize_opts(opts))
120
+ self.class.execute_resource_request_stream(
121
+ method, url, params, opts, &read_body_chunk_block
122
+ )
123
+ end
124
+
92
125
  # See notes on `alias` above.
93
126
  alias request execute_resource_request
94
127
  end
@@ -115,5 +115,13 @@ module Stripe
115
115
  Util.convert_to_stripe_object(resp.data, opts)
116
116
  end
117
117
  end
118
+
119
+ protected def request_stream(method:, path:, params:, opts: {},
120
+ &read_body_chunk_block)
121
+ resp, = execute_resource_request_stream(
122
+ method, path, params, opts, &read_body_chunk_block
123
+ )
124
+ resp
125
+ end
118
126
  end
119
127
  end
@@ -66,7 +66,8 @@ module Stripe
66
66
 
67
67
  # Executes an HTTP request to the given URI with the given method. Also
68
68
  # allows a request body, headers, and query string to be specified.
69
- def execute_request(method, uri, body: nil, headers: nil, query: nil)
69
+ def execute_request(method, uri, body: nil, headers: nil, query: nil,
70
+ &block)
70
71
  # Perform some basic argument validation because it's easy to get
71
72
  # confused between strings and hashes for things like body and query
72
73
  # parameters.
@@ -92,8 +93,22 @@ module Stripe
92
93
  u.path
93
94
  end
94
95
 
96
+ method_name = method.to_s.upcase
97
+ has_response_body = method_name != "HEAD"
98
+ request = Net::HTTPGenericRequest.new(
99
+ method_name,
100
+ (body ? true : false),
101
+ has_response_body,
102
+ path,
103
+ headers
104
+ )
105
+
95
106
  @mutex.synchronize do
96
- connection.send_request(method.to_s.upcase, path, body, headers)
107
+ # The block parameter is special here. If a block is provided, the block
108
+ # is invoked with the Net::HTTPResponse. However, the body will not have
109
+ # been read yet in the block, and can be streamed by calling
110
+ # HTTPResponse#read_body.
111
+ connection.request(request, body, &block)
97
112
  end
98
113
  end
99
114
 
@@ -213,62 +213,9 @@ module Stripe
213
213
 
214
214
  def execute_request(method, path,
215
215
  api_base: nil, api_key: nil, headers: {}, params: {})
216
- raise ArgumentError, "method should be a symbol" \
217
- unless method.is_a?(Symbol)
218
- raise ArgumentError, "path should be a string" \
219
- unless path.is_a?(String)
220
-
221
- api_base ||= config.api_base
222
- api_key ||= config.api_key
223
- params = Util.objects_to_ids(params)
224
-
225
- check_api_key!(api_key)
226
-
227
- body_params = nil
228
- query_params = nil
229
- case method
230
- when :get, :head, :delete
231
- query_params = params
232
- else
233
- body_params = params
234
- end
235
-
236
- query_params, path = merge_query_params(query_params, path)
237
-
238
- headers = request_headers(api_key, method)
239
- .update(Util.normalize_headers(headers))
240
- url = api_url(path, api_base)
241
-
242
- # Merge given query parameters with any already encoded in the path.
243
- query = query_params ? Util.encode_parameters(query_params) : nil
244
-
245
- # Encoding body parameters is a little more complex because we may have
246
- # to send a multipart-encoded body. `body_log` is produced separately as
247
- # a log-friendly variant of the encoded form. File objects are displayed
248
- # as such instead of as their file contents.
249
- body, body_log =
250
- body_params ? encode_body(body_params, headers) : [nil, nil]
251
-
252
- # stores information on the request we're about to make so that we don't
253
- # have to pass as many parameters around for logging.
254
- context = RequestLogContext.new
255
- context.account = headers["Stripe-Account"]
256
- context.api_key = api_key
257
- context.api_version = headers["Stripe-Version"]
258
- context.body = body_log
259
- context.idempotency_key = headers["Idempotency-Key"]
260
- context.method = method
261
- context.path = path
262
- context.query = query
263
-
264
- http_resp = execute_request_with_rescues(method, api_base, context) do
265
- self.class
266
- .default_connection_manager(config)
267
- .execute_request(method, url,
268
- body: body,
269
- headers: headers,
270
- query: query)
271
- end
216
+ http_resp, api_key = execute_request_internal(
217
+ method, path, api_base, api_key, headers, params
218
+ )
272
219
 
273
220
  begin
274
221
  resp = StripeResponse.from_net_http(http_resp)
@@ -284,6 +231,38 @@ module Stripe
284
231
  [resp, api_key]
285
232
  end
286
233
 
234
+ # Executes a request and returns the body as a stream instead of converting
235
+ # it to a StripeObject. This should be used for any request where we expect
236
+ # an arbitrary binary response.
237
+ #
238
+ # A `read_body_chunk` block can be passed, which will be called repeatedly
239
+ # with the body chunks read from the socket.
240
+ #
241
+ # If a block is passed, a StripeHeadersOnlyResponse is returned as the
242
+ # block is expected to do all the necessary body processing. If no block is
243
+ # passed, then a StripeStreamResponse is returned containing an IO stream
244
+ # with the response body.
245
+ def execute_request_stream(method, path,
246
+ api_base: nil, api_key: nil,
247
+ headers: {}, params: {},
248
+ &read_body_chunk_block)
249
+ unless block_given?
250
+ raise ArgumentError,
251
+ "execute_request_stream requires a read_body_chunk_block"
252
+ end
253
+
254
+ http_resp, api_key = execute_request_internal(
255
+ method, path, api_base, api_key, headers, params, &read_body_chunk_block
256
+ )
257
+
258
+ # When the read_body_chunk_block is given, we no longer have access to the
259
+ # response body at this point and so return a response object containing
260
+ # only the headers. This is because the body was consumed by the block.
261
+ resp = StripeHeadersOnlyResponse.from_net_http(http_resp)
262
+
263
+ [resp, api_key]
264
+ end
265
+
287
266
  def store_last_response(object_id, resp)
288
267
  return unless last_response_has_key?(object_id)
289
268
 
@@ -451,6 +430,83 @@ module Stripe
451
430
  pruned_contexts.count
452
431
  end
453
432
 
433
+ private def execute_request_internal(method, path,
434
+ api_base, api_key, headers, params,
435
+ &read_body_chunk_block)
436
+ raise ArgumentError, "method should be a symbol" \
437
+ unless method.is_a?(Symbol)
438
+ raise ArgumentError, "path should be a string" \
439
+ unless path.is_a?(String)
440
+
441
+ api_base ||= config.api_base
442
+ api_key ||= config.api_key
443
+ params = Util.objects_to_ids(params)
444
+
445
+ check_api_key!(api_key)
446
+
447
+ body_params = nil
448
+ query_params = nil
449
+ case method
450
+ when :get, :head, :delete
451
+ query_params = params
452
+ else
453
+ body_params = params
454
+ end
455
+
456
+ query_params, path = merge_query_params(query_params, path)
457
+
458
+ headers = request_headers(api_key, method)
459
+ .update(Util.normalize_headers(headers))
460
+ url = api_url(path, api_base)
461
+
462
+ # Merge given query parameters with any already encoded in the path.
463
+ query = query_params ? Util.encode_parameters(query_params) : nil
464
+
465
+ # Encoding body parameters is a little more complex because we may have
466
+ # to send a multipart-encoded body. `body_log` is produced separately as
467
+ # a log-friendly variant of the encoded form. File objects are displayed
468
+ # as such instead of as their file contents.
469
+ body, body_log =
470
+ body_params ? encode_body(body_params, headers) : [nil, nil]
471
+
472
+ # stores information on the request we're about to make so that we don't
473
+ # have to pass as many parameters around for logging.
474
+ context = RequestLogContext.new
475
+ context.account = headers["Stripe-Account"]
476
+ context.api_key = api_key
477
+ context.api_version = headers["Stripe-Version"]
478
+ context.body = body_log
479
+ context.idempotency_key = headers["Idempotency-Key"]
480
+ context.method = method
481
+ context.path = path
482
+ context.query = query
483
+
484
+ # A block can be passed in to read the content directly from the response.
485
+ # We want to execute this block only when the response was actually
486
+ # successful. When it wasn't, we defer to the standard error handling as
487
+ # we have to read the body and parse the error JSON.
488
+ response_block =
489
+ if block_given?
490
+ lambda do |response|
491
+ unless should_handle_as_error(response.code.to_i)
492
+ response.read_body(&read_body_chunk_block)
493
+ end
494
+ end
495
+ end
496
+
497
+ http_resp = execute_request_with_rescues(method, api_base, context) do
498
+ self.class
499
+ .default_connection_manager(config)
500
+ .execute_request(method, url,
501
+ body: body,
502
+ headers: headers,
503
+ query: query,
504
+ &response_block)
505
+ end
506
+
507
+ [http_resp, api_key]
508
+ end
509
+
454
510
  private def api_url(url = "", api_base = nil)
455
511
  (api_base || config.api_base) + url
456
512
  end
@@ -490,6 +546,7 @@ module Stripe
490
546
  # that's more condusive to logging.
491
547
  flattened_params =
492
548
  flattened_params.map { |k, v| [k, v.is_a?(String) ? v : v.to_s] }.to_h
549
+
493
550
  else
494
551
  body = Util.encode_parameters(body_params)
495
552
  end
@@ -503,6 +560,10 @@ module Stripe
503
560
  [body, body_log]
504
561
  end
505
562
 
563
+ private def should_handle_as_error(http_status)
564
+ http_status >= 400
565
+ end
566
+
506
567
  private def execute_request_with_rescues(method, api_base, context)
507
568
  num_retries = 0
508
569
 
@@ -520,7 +581,9 @@ module Stripe
520
581
  http_status = resp.code.to_i
521
582
  context = context.dup_from_response_headers(resp)
522
583
 
523
- handle_error_response(resp, context) if http_status >= 400
584
+ if should_handle_as_error(http_status)
585
+ handle_error_response(resp, context)
586
+ end
524
587
 
525
588
  log_response(context, request_start, http_status, resp.body)
526
589
  notify_request_end(context, request_duration, http_status,
@@ -1,63 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stripe
4
- # StripeResponse encapsulates some vitals of a response that came back from
5
- # the Stripe API.
6
- class StripeResponse
7
- # Headers provides an access wrapper to an API response's header data. It
8
- # mainly exists so that we don't need to expose the entire
9
- # `Net::HTTPResponse` object while still getting some of its benefits like
10
- # case-insensitive access to header names and flattening of header values.
11
- class Headers
12
- # Initializes a Headers object from a Net::HTTP::HTTPResponse object.
13
- def self.from_net_http(resp)
14
- new(resp.to_hash)
15
- end
4
+ # Headers provides an access wrapper to an API response's header data. It
5
+ # mainly exists so that we don't need to expose the entire
6
+ # `Net::HTTPResponse` object while still getting some of its benefits like
7
+ # case-insensitive access to header names and flattening of header values.
8
+ class StripeResponseHeaders
9
+ # Initializes a Headers object from a Net::HTTP::HTTPResponse object.
10
+ def self.from_net_http(resp)
11
+ new(resp.to_hash)
12
+ end
16
13
 
17
- # `hash` is expected to be a hash mapping header names to arrays of
18
- # header values. This is the default format generated by calling
19
- # `#to_hash` on a `Net::HTTPResponse` object because headers can be
20
- # repeated multiple times. Using `#[]` will collapse values down to just
21
- # the first.
22
- def initialize(hash)
23
- if !hash.is_a?(Hash) ||
24
- !hash.keys.all? { |n| n.is_a?(String) } ||
25
- !hash.values.all? { |a| a.is_a?(Array) } ||
26
- !hash.values.all? { |a| a.all? { |v| v.is_a?(String) } }
27
- raise ArgumentError,
28
- "expect hash to be a map of string header names to arrays of " \
29
- "header values"
30
- end
14
+ # `hash` is expected to be a hash mapping header names to arrays of
15
+ # header values. This is the default format generated by calling
16
+ # `#to_hash` on a `Net::HTTPResponse` object because headers can be
17
+ # repeated multiple times. Using `#[]` will collapse values down to just
18
+ # the first.
19
+ def initialize(hash)
20
+ if !hash.is_a?(Hash) ||
21
+ !hash.keys.all? { |n| n.is_a?(String) } ||
22
+ !hash.values.all? { |a| a.is_a?(Array) } ||
23
+ !hash.values.all? { |a| a.all? { |v| v.is_a?(String) } }
24
+ raise ArgumentError,
25
+ "expect hash to be a map of string header names to arrays of " \
26
+ "header values"
27
+ end
31
28
 
32
- @hash = {}
29
+ @hash = {}
33
30
 
34
- # This shouldn't be strictly necessary because `Net::HTTPResponse` will
35
- # produce a hash with all headers downcased, but do it anyway just in
36
- # case an object of this class was constructed manually.
37
- #
38
- # Also has the effect of duplicating the hash, which is desirable for a
39
- # little extra object safety.
40
- hash.each do |k, v|
41
- @hash[k.downcase] = v
42
- end
31
+ # This shouldn't be strictly necessary because `Net::HTTPResponse` will
32
+ # produce a hash with all headers downcased, but do it anyway just in
33
+ # case an object of this class was constructed manually.
34
+ #
35
+ # Also has the effect of duplicating the hash, which is desirable for a
36
+ # little extra object safety.
37
+ hash.each do |k, v|
38
+ @hash[k.downcase] = v
43
39
  end
40
+ end
44
41
 
45
- def [](name)
46
- values = @hash[name.downcase]
47
- if values && values.count > 1
48
- warn("Duplicate header values for `#{name}`; returning only first")
49
- end
50
- values ? values.first : nil
42
+ def [](name)
43
+ values = @hash[name.downcase]
44
+ if values && values.count > 1
45
+ warn("Duplicate header values for `#{name}`; returning only first")
51
46
  end
47
+ values ? values.first : nil
52
48
  end
49
+ end
53
50
 
54
- # The data contained by the HTTP body of the response deserialized from
55
- # JSON.
56
- attr_accessor :data
57
-
58
- # The raw HTTP body of the response.
59
- attr_accessor :http_body
60
-
51
+ module StripeResponseBase
61
52
  # A Hash of the HTTP headers of the response.
62
53
  attr_accessor :http_headers
63
54
 
@@ -67,15 +58,52 @@ module Stripe
67
58
  # The Stripe request ID of the response.
68
59
  attr_accessor :request_id
69
60
 
61
+ def self.populate_for_net_http(resp, http_resp)
62
+ resp.http_headers = StripeResponseHeaders.from_net_http(http_resp)
63
+ resp.http_status = http_resp.code.to_i
64
+ resp.request_id = http_resp["request-id"]
65
+ end
66
+ end
67
+
68
+ # StripeResponse encapsulates some vitals of a response that came back from
69
+ # the Stripe API.
70
+ class StripeResponse
71
+ include StripeResponseBase
72
+ # The data contained by the HTTP body of the response deserialized from
73
+ # JSON.
74
+ attr_accessor :data
75
+
76
+ # The raw HTTP body of the response.
77
+ attr_accessor :http_body
78
+
70
79
  # Initializes a StripeResponse object from a Net::HTTP::HTTPResponse
71
80
  # object.
72
81
  def self.from_net_http(http_resp)
73
82
  resp = StripeResponse.new
74
83
  resp.data = JSON.parse(http_resp.body, symbolize_names: true)
75
84
  resp.http_body = http_resp.body
76
- resp.http_headers = Headers.from_net_http(http_resp)
77
- resp.http_status = http_resp.code.to_i
78
- resp.request_id = http_resp["request-id"]
85
+ StripeResponseBase.populate_for_net_http(resp, http_resp)
86
+ resp
87
+ end
88
+ end
89
+
90
+ # We have to alias StripeResponseHeaders to StripeResponse::Headers, as this
91
+ # class used to be embedded within StripeResponse and we want to be backwards
92
+ # compatible.
93
+ StripeResponse::Headers = StripeResponseHeaders
94
+
95
+ # StripeHeadersOnlyResponse includes only header-related vitals of the
96
+ # response. This is used for streaming requests where the response was read
97
+ # directly in a block and we explicitly don't want to store the body of the
98
+ # response in memory.
99
+ class StripeHeadersOnlyResponse
100
+ include StripeResponseBase
101
+
102
+ # Initializes a StripeHeadersOnlyResponse object from a
103
+ # Net::HTTP::HTTPResponse object.
104
+ def self.from_net_http(http_resp)
105
+ resp = StripeHeadersOnlyResponse.new
106
+ StripeResponseBase.populate_for_net_http(resp, http_resp)
79
107
  resp
80
108
  end
81
109
  end
data/lib/stripe/util.rb CHANGED
@@ -208,7 +208,9 @@ module Stripe
208
208
  { api_key: opts }
209
209
  when Hash
210
210
  check_api_key!(opts.fetch(:api_key)) if opts.key?(:api_key)
211
- opts.clone
211
+ # Explicitly use dup here instead of clone to avoid preserving freeze
212
+ # state on input params.
213
+ opts.dup
212
214
  else
213
215
  raise TypeError, "normalize_opts expects a string or a hash"
214
216
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stripe
4
- VERSION = "5.34.0"
4
+ VERSION = "5.35.0"
5
5
  end
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.34.0
4
+ version: 5.35.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stripe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-04 00:00:00.000000000 Z
11
+ date: 2021-06-30 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.