stripe 5.34.0 → 5.35.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: 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.