Skip to content

Commit 41ad43a

Browse files
Fix v2 typed params sending nil values on GET requests (#1787)
objects_to_ids was preserving nil values for all v2 API requests, but this caused issues on GET requests where nil values become empty strings in query parameters, leading to API errors like: "limit: Expected an Integer value got: ." This change updates the logic to only preserve nil values in the request body (for POST/PUT/PATCH requests) where they are needed for v2 semantics, while filtering them from query parameters (for GET/HEAD/DELETE requests). Changed from symbol-based semantics parameter to a boolean serialize_empty parameter for clarity.
1 parent ca32076 commit 41ad43a

File tree

3 files changed

+27
-42
lines changed

3 files changed

+27
-42
lines changed

lib/stripe/api_requestor.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,17 +471,17 @@ def self.maybe_gc_connection_managers
471471
raise ArgumentError, "api_base cannot be empty" if base_url.nil? || base_url.empty?
472472

473473
api_key ||= opts[:api_key]
474-
params = Util.objects_to_ids(params, api_mode)
475474

476475
check_api_key!(api_key)
477476

478477
body_params = nil
479478
query_params = nil
480479
case method
481480
when :get, :head, :delete
482-
query_params = params
481+
query_params = Util.objects_to_ids(params)
483482
else
484-
body_params = params
483+
# For v2 body params, serialize nil values to preserve explicit nulls
484+
body_params = Util.objects_to_ids(params, serialize_empty: api_mode == :v2)
485485
end
486486

487487
query_params, path = merge_query_params(query_params, path)

lib/stripe/util.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@ module Util
77
LEGAL_FIRST_CHARACTER = /[a-zA-Z_]/.freeze
88
LEGAL_VARIABLE_CHARACTER = /[a-zA-Z0-9_]/.freeze
99

10-
def self.objects_to_ids(obj, semantics)
10+
def self.objects_to_ids(obj, serialize_empty: false)
1111
case obj
1212
when APIResource
1313
obj.id
1414
when Hash
1515
res = {}
1616
obj.each do |k, v|
1717
if !v.nil?
18-
res[k] = objects_to_ids(v, semantics)
19-
elsif semantics == :v2
18+
res[k] = objects_to_ids(v, serialize_empty: serialize_empty)
19+
elsif serialize_empty
2020
res[k] = nil
2121
end
2222
end
2323
res
2424
when Array
25-
obj.map { |v| objects_to_ids(v, semantics) }
25+
obj.map { |v| objects_to_ids(v, serialize_empty: serialize_empty) }
2626
else
2727
obj
2828
end

test/stripe/util_test.rb

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -161,53 +161,46 @@ class UtilTest < Test::Unit::TestCase
161161
context "#objects_to_ids" do
162162
should "convert APIResource to id" do
163163
resource = Stripe::Charge.construct_from(id: "ch_123", object: "charge")
164-
result = Util.objects_to_ids(resource, :v1)
164+
result = Util.objects_to_ids(resource)
165165
assert_equal "ch_123", result
166166
end
167167

168168
should "pass through primitives unchanged" do
169-
assert_equal "string", Util.objects_to_ids("string", :v1)
170-
assert_equal 123, Util.objects_to_ids(123, :v1)
171-
assert_equal true, Util.objects_to_ids(true, :v1)
169+
assert_equal "string", Util.objects_to_ids("string")
170+
assert_equal 123, Util.objects_to_ids(123)
171+
assert_equal true, Util.objects_to_ids(true)
172172
end
173173

174-
should "skip nil values in hashes with v1 semantics" do
174+
should "skip nil values in hashes by default" do
175175
input = { a: "value", b: nil, c: "another" }
176-
result = Util.objects_to_ids(input, :v1)
176+
result = Util.objects_to_ids(input)
177177
assert_equal({ a: "value", c: "another" }, result)
178178
refute result.key?(:b)
179179
end
180180

181-
should "keep nil values in hashes with v2 semantics" do
181+
should "keep nil values in hashes when serialize_empty is true" do
182182
input = { a: "value", b: nil, c: "another" }
183-
result = Util.objects_to_ids(input, :v2)
183+
result = Util.objects_to_ids(input, serialize_empty: true)
184184
assert_equal({ a: "value", b: nil, c: "another" }, result)
185185
assert result.key?(:b)
186186
assert_nil result[:b]
187187
end
188188

189-
should "recurse on non-nil hash values with v1 semantics" do
189+
should "recurse on non-nil hash values" do
190190
resource = Stripe::Charge.construct_from(id: "ch_123", object: "charge")
191191
input = { charge: resource, amount: 100 }
192-
result = Util.objects_to_ids(input, :v1)
192+
result = Util.objects_to_ids(input)
193193
assert_equal({ charge: "ch_123", amount: 100 }, result)
194194
end
195195

196-
should "recurse on non-nil hash values with v2 semantics" do
197-
resource = Stripe::Charge.construct_from(id: "ch_123", object: "charge")
198-
input = { charge: resource, amount: 100 }
199-
result = Util.objects_to_ids(input, :v2)
200-
assert_equal({ charge: "ch_123", amount: 100 }, result)
201-
end
202-
203-
should "handle nested hashes with nil values in v1 semantics" do
196+
should "handle nested hashes with nil values by default" do
204197
resource = Stripe::Charge.construct_from(id: "ch_123", object: "charge")
205198
input = {
206199
charge: resource,
207200
metadata: { key: "value", empty: nil },
208201
description: nil,
209202
}
210-
result = Util.objects_to_ids(input, :v1)
203+
result = Util.objects_to_ids(input)
211204
expected = {
212205
charge: "ch_123",
213206
metadata: { key: "value" },
@@ -217,14 +210,14 @@ class UtilTest < Test::Unit::TestCase
217210
refute result.key?(:description)
218211
end
219212

220-
should "handle nested hashes with nil values in v2 semantics" do
213+
should "handle nested hashes with nil values when serialize_empty is true" do
221214
resource = Stripe::Charge.construct_from(id: "ch_123", object: "charge")
222215
input = {
223216
charge: resource,
224217
metadata: { key: "value", empty: nil },
225218
description: nil,
226219
}
227-
result = Util.objects_to_ids(input, :v2)
220+
result = Util.objects_to_ids(input, serialize_empty: true)
228221
expected = {
229222
charge: "ch_123",
230223
metadata: { key: "value", empty: nil },
@@ -235,43 +228,35 @@ class UtilTest < Test::Unit::TestCase
235228
assert result.key?(:description)
236229
end
237230

238-
should "process arrays with v1 semantics" do
239-
resource1 = Stripe::Charge.construct_from(id: "ch_123", object: "charge")
240-
resource2 = Stripe::Charge.construct_from(id: "ch_456", object: "charge")
241-
input = [resource1, "string", resource2]
242-
result = Util.objects_to_ids(input, :v1)
243-
assert_equal %w[ch_123 string ch_456], result
244-
end
245-
246-
should "process arrays with v2 semantics" do
231+
should "process arrays" do
247232
resource1 = Stripe::Charge.construct_from(id: "ch_123", object: "charge")
248233
resource2 = Stripe::Charge.construct_from(id: "ch_456", object: "charge")
249234
input = [resource1, "string", resource2]
250-
result = Util.objects_to_ids(input, :v2)
235+
result = Util.objects_to_ids(input)
251236
assert_equal %w[ch_123 string ch_456], result
252237
end
253238

254-
should "handle complex nested structures with v1 semantics" do
239+
should "handle complex nested structures by default" do
255240
resource = Stripe::Charge.construct_from(id: "ch_123", object: "charge")
256241
input = {
257242
charges: [resource, nil],
258243
metadata: { key: nil, nested: { value: "test", empty: nil } },
259244
}
260-
result = Util.objects_to_ids(input, :v1)
245+
result = Util.objects_to_ids(input)
261246
expected = {
262247
charges: ["ch_123", nil],
263248
metadata: { nested: { value: "test" } },
264249
}
265250
assert_equal expected, result
266251
end
267252

268-
should "handle complex nested structures with v2 semantics" do
253+
should "handle complex nested structures when serialize_empty is true" do
269254
resource = Stripe::Charge.construct_from(id: "ch_123", object: "charge")
270255
input = {
271256
charges: [resource, nil],
272257
metadata: { key: nil, nested: { value: "test", empty: nil } },
273258
}
274-
result = Util.objects_to_ids(input, :v2)
259+
result = Util.objects_to_ids(input, serialize_empty: true)
275260
expected = {
276261
charges: ["ch_123", nil],
277262
metadata: { key: nil, nested: { value: "test", empty: nil } },

0 commit comments

Comments
 (0)