Skip to content

Commit 611ecf0

Browse files
Ignore unset properties of V2 Request param classes when making requests (#1796)
* Add some tests * Fixed V2 request param serialization of unset parameters * Added a test case for updating a property after initialization --------- Co-authored-by: Michael Broshi <mbroshi@stripe.com>
1 parent 707d703 commit 611ecf0

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

lib/stripe/request_params.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,46 @@ module Stripe
55
# For internal use only. Does not provide a stable API and may be broken
66
# with future non-major changes.
77
class RequestParams
8+
class << self
9+
# Override the object instantiation flow in Ruby in order to track explicitly set keys
10+
# V2 APIs accept null values on the backend. However, we do not want to set a key to nil
11+
# if the user has not explicitly set them to nil
12+
# TODO(major): https://go/j/DEVSDK-2990
13+
def new(**kwargs)
14+
instance = allocate
15+
# Track explicitly set keys for V2 classes only
16+
instance.instance_variable_set(:@_explicitly_set_keys, kwargs.keys.to_set) if name&.start_with?("Stripe::V2::")
17+
instance.send(:initialize, **kwargs)
18+
instance
19+
end
20+
21+
# Override attr_accessor to create setters that track explicitly set keys for V2 classes
22+
def attr_accessor(*names)
23+
names.each do |name|
24+
# Define getter
25+
define_method(name) { instance_variable_get("@#{name}") }
26+
27+
# Define setter that tracks the key as explicitly set
28+
define_method("#{name}=") do |value|
29+
@_explicitly_set_keys&.add(name)
30+
instance_variable_set("@#{name}", value)
31+
end
32+
end
33+
end
34+
end
35+
836
def to_h
937
instance_variables.each_with_object({}) do |var, hash|
38+
# _explicitly_set_keys is set as an instance variable.
39+
# Ignore the var if it is _explicitly_set_keys itself.
40+
next if var == :@_explicitly_set_keys
41+
1042
key = var.to_s.delete("@").to_sym
1143
value = instance_variable_get(var)
1244

45+
# For V2 classes, only include keys that were explicitly set
46+
next if @_explicitly_set_keys && !@_explicitly_set_keys.include?(key)
47+
1348
hash[key] = if value.is_a?(RequestParams)
1449
value.to_h
1550
# Check if value is an array and contains RequestParams objects

test/stripe/request_params_test.rb

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,5 +269,77 @@ def initialize(games: nil)
269269
assert_equal expected, params.to_h
270270
end
271271
end
272+
273+
context "V2 RequestParams explicit key tracking" do
274+
should "only serialize explicitly set fields for V2 classes" do
275+
params = Stripe::V2::Billing::MeterEventCreateParams.new(
276+
event_name: "my_event",
277+
payload: { stripe_customer_id: "cus_123", value: "25" }
278+
)
279+
280+
result = params.to_h
281+
282+
assert_equal "my_event", result[:event_name]
283+
assert_equal({ stripe_customer_id: "cus_123", value: "25" }, result[:payload])
284+
refute result.key?(:identifier), "identifier was not set and should not be in the hash"
285+
refute result.key?(:timestamp), "timestamp was not set and should not be in the hash"
286+
end
287+
288+
should "serialize fields explicitly set to nil for V2 classes" do
289+
params = Stripe::V2::Billing::MeterEventCreateParams.new(
290+
event_name: "my_event",
291+
identifier: nil,
292+
payload: { stripe_customer_id: "cus_123", value: "25" }
293+
)
294+
295+
result = params.to_h
296+
297+
assert_equal "my_event", result[:event_name]
298+
assert result.key?(:identifier), "identifier was explicitly set to nil and should be in the hash"
299+
assert_nil result[:identifier]
300+
assert_equal({ stripe_customer_id: "cus_123", value: "25" }, result[:payload])
301+
refute result.key?(:timestamp), "timestamp was not set and should not be in the hash"
302+
end
303+
304+
should "serialize fields explicitly set after initialization for V2 classes" do
305+
params = Stripe::V2::Billing::MeterEventCreateParams.new(
306+
event_name: "my_event",
307+
payload: { stripe_customer_id: "cus_123", value: "25" }
308+
)
309+
310+
params.identifier = "Something"
311+
params.timestamp = nil
312+
313+
result = params.to_h
314+
315+
assert_equal "my_event", result[:event_name]
316+
assert_equal "Something", result[:identifier]
317+
assert_equal({ stripe_customer_id: "cus_123", value: "25" }, result[:payload])
318+
assert result.key?(:timestamp)
319+
assert_nil result[:timestamp]
320+
end
321+
322+
should "serialize all fields even if some fields are set after initialization for V1 classes" do
323+
params = FooCreateParams.new
324+
325+
params.fun = "games"
326+
327+
result = params.to_h
328+
329+
assert_equal "games", result[:fun]
330+
assert result.key?(:team), "V1 classes should include all fields even if not explicitly set"
331+
assert_nil result[:team]
332+
end
333+
334+
should "serialize all fields including nil defaults for V1 classes" do
335+
params = FooCreateParams.new(fun: "games")
336+
337+
result = params.to_h
338+
339+
assert_equal "games", result[:fun]
340+
assert result.key?(:team), "V1 classes should include all fields even if not explicitly set"
341+
assert_nil result[:team]
342+
end
343+
end
272344
end
273345
end

0 commit comments

Comments
 (0)