Skip to content

Commit

Permalink
Add support for updatable singleton resources (#1304)
Browse files Browse the repository at this point in the history
  • Loading branch information
pakrym-stripe authored Dec 21, 2023
1 parent 678757f commit c46f4a6
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 1 deletion.
1 change: 1 addition & 0 deletions lib/stripe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
require "stripe/api_operations/nested_resource"
require "stripe/api_operations/request"
require "stripe/api_operations/save"
require "stripe/api_operations/singleton_save"
require "stripe/api_operations/search"

# API resource support classes
Expand Down
86 changes: 86 additions & 0 deletions lib/stripe/api_operations/singleton_save.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# frozen_string_literal: true

module Stripe
module APIOperations
module SingletonSave
module ClassMethods
# Updates a singleton API resource
#
# Updates the identified resource with the passed in parameters.
#
# ==== Attributes
#
# * +params+ - A hash of parameters to pass to the API
# * +opts+ - A Hash of additional options (separate from the params /
# object values) to be added to the request. E.g. to allow for an
# idempotency_key to be passed in the request headers, or for the
# api_key to be overwritten. See
# {APIOperations::Request.execute_resource_request}.
def update(params = {}, opts = {})
params.each_key do |k|
raise ArgumentError, "Cannot update protected field: #{k}" if protected_fields.include?(k)
end

request_stripe_object(
method: :post,
path: resource_url,
params: params,
opts: opts
)
end
end

# The `save` method is DEPRECATED and will be removed in a future major
# version of the library. Use the `update` method on the resource instead.
#
# Updates a singleton API resource.
#
# If the resource doesn't yet have an assigned ID and the resource is one
# that can be created, then the method attempts to create the resource.
# The resource is updated otherwise.
#
# ==== Attributes
#
# * +params+ - Overrides any parameters in the resource's serialized data
# and includes them in the create or update. If +:req_url:+ is included
# in the list, it overrides the update URL used for the create or
# update.
# * +opts+ - A Hash of additional options (separate from the params /
# object values) to be added to the request. E.g. to allow for an
# idempotency_key to be passed in the request headers, or for the
# api_key to be overwritten. See
# {APIOperations::Request.execute_resource_request}.
def save(params = {}, opts = {})
# We started unintentionally (sort of) allowing attributes sent to
# +save+ to override values used during the update. So as not to break
# the API, this makes that official here.
update_attributes(params)

# Now remove any parameters that look like object attributes.
params = params.reject { |k, _| respond_to?(k) }

values = serialize_params(self).merge(params)

resp, opts = execute_resource_request(:post, resource_url, values, opts, ["save"])
initialize_from(resp.data, opts)
end
extend Gem::Deprecate
deprecate :save, "the `update` class method (for examples " \
"see https://github.com/stripe/stripe-ruby" \
"/wiki/Migration-guide-for-v8)", 2022, 11

def self.included(base)
# Set `metadata` as additive so that when it's set directly we remember
# to clear keys that may have been previously set by sending empty
# values for them.
#
# It's possible that not every object with `Save` has `metadata`, but
# it's a close enough heuristic, and having this option set when there
# is no `metadata` field is not harmful.
base.additive_object_param(:metadata)

base.extend(ClassMethods)
end
end
end
end
2 changes: 1 addition & 1 deletion lib/stripe/resources/tax/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Tax
#
# Related guide: [Using the Settings API](https://stripe.com/docs/tax/settings-api)
class Settings < SingletonAPIResource
include Stripe::APIOperations::Save
include Stripe::APIOperations::SingletonSave

OBJECT_NAME = "tax.settings"
end
Expand Down
53 changes: 53 additions & 0 deletions test/stripe/api_resource_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,59 @@ def say_hello(params = {}, opts = {})
end
end

context "singleton resource" do
class TestSingletonResource < SingletonAPIResource # rubocop:todo Lint/ConstantDefinitionInBlock
include Stripe::APIOperations::SingletonSave
OBJECT_NAME = "test.singleton"
end

setup do
Util.instance_variable_set(
:@object_classes,
Stripe::ObjectTypes.object_names_to_classes.merge(
"test.singleton" => TestSingletonResource
)
)
end
teardown do
Util.class.instance_variable_set(:@object_classes, Stripe::ObjectTypes.object_names_to_classes)
end

should "be retrievable" do
stub_request(:get, "#{Stripe.api_base}/v1/test/singleton")
.with(query: { foo: "bar" }, headers: { "Stripe-Account" => "acct_hi" })
.to_return(body: JSON.generate({ object: "test.singleton", result: "hi!" }))

settings = TestSingletonResource.retrieve({ foo: "bar" }, { stripe_account: "acct_hi" })
assert settings.is_a? TestSingletonResource
assert_equal "hi!", settings.result
end

should "be updatable" do
stub_request(:post, "#{Stripe.api_base}/v1/test/singleton")
.with(body: { foo: "bar" }, headers: { "Stripe-Account" => "acct_hi" })
.to_return(body: JSON.generate({ object: "test.singleton", result: "hi!" }))

settings = TestSingletonResource.update({ foo: "bar" }, { stripe_account: "acct_hi" })
assert settings.is_a? TestSingletonResource
assert_equal "hi!", settings.result
end

should "be saveable" do
stub_request(:get, "#{Stripe.api_base}/v1/test/singleton")
.to_return(body: JSON.generate({ object: "test.singleton", result: "hi!" }))

stub_request(:post, "#{Stripe.api_base}/v1/test/singleton")
.with(body: { result: "hello" })
.to_return(body: JSON.generate({ object: "test.singleton", result: "hello" }))

settings = TestSingletonResource.retrieve
settings.result = "hello"
settings.save
assert_requested :post, "#{Stripe.api_base}/v1/test/singleton", times: 1
end
end

@@fixtures = {} # rubocop:disable Style/ClassVars
setup do
if @@fixtures.empty?
Expand Down
8 changes: 8 additions & 0 deletions test/stripe/generated_examples_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1571,6 +1571,14 @@ class CodegennedExampleTest < Test::Unit::TestCase
Stripe::TaxRate.update("txr_xxxxxxxxxxxxx", { active: false })
assert_requested :post, "#{Stripe.api_base}/v1/tax_rates/txr_xxxxxxxxxxxxx"
end
should "Test tax settings get" do
Stripe::Tax::Settings.retrieve
assert_requested :get, "#{Stripe.api_base}/v1/tax/settings?"
end
should "Test tax settings post" do
Stripe::Tax::Settings.update({ defaults: { tax_code: "txcd_10000000" } })
assert_requested :post, "#{Stripe.api_base}/v1/tax/settings"
end
should "Test tax transactions create from calculation post" do
Stripe::Tax::Transaction.create_from_calculation({
calculation: "xxx",
Expand Down

0 comments on commit c46f4a6

Please sign in to comment.