mirror of
https://github.com/stripe/stripe-ruby.git
synced 2025-12-04 00:01:22 -05:00
Allow StripeClient to be configured per instance (#968)
This changes allows for each instance of StripeClient to have its own configuration object instead of relying on the global config. Each instance can be configured to override any global config values previously set.
This commit is contained in:
parent
f864e68bf7
commit
21643f0716
@ -62,6 +62,8 @@ module Stripe
|
|||||||
class << self
|
class << self
|
||||||
extend Forwardable
|
extend Forwardable
|
||||||
|
|
||||||
|
attr_reader :configuration
|
||||||
|
|
||||||
# User configurable options
|
# User configurable options
|
||||||
def_delegators :@configuration, :api_key, :api_key=
|
def_delegators :@configuration, :api_key, :api_key=
|
||||||
def_delegators :@configuration, :api_version, :api_version=
|
def_delegators :@configuration, :api_version, :api_version=
|
||||||
|
|||||||
@ -15,8 +15,10 @@ module Stripe
|
|||||||
# by `StripeClient` to determine whether a connection manager should be
|
# by `StripeClient` to determine whether a connection manager should be
|
||||||
# garbage collected or not.
|
# garbage collected or not.
|
||||||
attr_reader :last_used
|
attr_reader :last_used
|
||||||
|
attr_reader :config
|
||||||
|
|
||||||
def initialize
|
def initialize(config = Stripe.configuration)
|
||||||
|
@config = config
|
||||||
@active_connections = {}
|
@active_connections = {}
|
||||||
@last_used = Util.monotonic_time
|
@last_used = Util.monotonic_time
|
||||||
|
|
||||||
@ -117,17 +119,17 @@ module Stripe
|
|||||||
# reused Go's default for `DefaultTransport`.
|
# reused Go's default for `DefaultTransport`.
|
||||||
connection.keep_alive_timeout = 30
|
connection.keep_alive_timeout = 30
|
||||||
|
|
||||||
connection.open_timeout = Stripe.open_timeout
|
connection.open_timeout = config.open_timeout
|
||||||
connection.read_timeout = Stripe.read_timeout
|
connection.read_timeout = config.read_timeout
|
||||||
if connection.respond_to?(:write_timeout=)
|
if connection.respond_to?(:write_timeout=)
|
||||||
connection.write_timeout = Stripe.write_timeout
|
connection.write_timeout = config.write_timeout
|
||||||
end
|
end
|
||||||
|
|
||||||
connection.use_ssl = uri.scheme == "https"
|
connection.use_ssl = uri.scheme == "https"
|
||||||
|
|
||||||
if Stripe.verify_ssl_certs
|
if config.verify_ssl_certs
|
||||||
connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
||||||
connection.cert_store = Stripe.ca_store
|
connection.cert_store = config.ca_store
|
||||||
else
|
else
|
||||||
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||||
warn_ssl_verify_none
|
warn_ssl_verify_none
|
||||||
@ -141,10 +143,10 @@ module Stripe
|
|||||||
# out those pieces to make passing them into a new connection a little less
|
# out those pieces to make passing them into a new connection a little less
|
||||||
# ugly.
|
# ugly.
|
||||||
private def proxy_parts
|
private def proxy_parts
|
||||||
if Stripe.proxy.nil?
|
if config.proxy.nil?
|
||||||
[nil, nil, nil, nil]
|
[nil, nil, nil, nil]
|
||||||
else
|
else
|
||||||
u = URI.parse(Stripe.proxy)
|
u = URI.parse(config.proxy)
|
||||||
[u.host, u.port, u.user, u.password]
|
[u.host, u.port, u.user, u.password]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -7,8 +7,8 @@ module Stripe
|
|||||||
|
|
||||||
def self.execute_resource_request(method, url, params, opts)
|
def self.execute_resource_request(method, url, params, opts)
|
||||||
opts = Util.normalize_opts(opts)
|
opts = Util.normalize_opts(opts)
|
||||||
opts[:client] ||= StripeClient.active_client
|
opts[:client] ||= opts[:client] || StripeClient.active_client
|
||||||
opts[:api_base] ||= Stripe.connect_base
|
opts[:api_base] ||= opts[:client].config.connect_base
|
||||||
|
|
||||||
super(method, url, params, opts)
|
super(method, url, params, opts)
|
||||||
end
|
end
|
||||||
@ -29,7 +29,8 @@ module Stripe
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.authorize_url(params = {}, opts = {})
|
def self.authorize_url(params = {}, opts = {})
|
||||||
base = opts[:connect_base] || Stripe.connect_base
|
client = opts[:client] || StripeClient.active_client
|
||||||
|
base = opts[:connect_base] || client.config.connect_base
|
||||||
|
|
||||||
path = "/oauth/authorize"
|
path = "/oauth/authorize"
|
||||||
path = "/express" + path if opts[:express]
|
path = "/express" + path if opts[:express]
|
||||||
|
|||||||
@ -45,12 +45,8 @@ module Stripe
|
|||||||
end
|
end
|
||||||
|
|
||||||
# @override To make id optional
|
# @override To make id optional
|
||||||
def self.retrieve(id = ARGUMENT_NOT_PROVIDED, opts = {})
|
def self.retrieve(id = nil, opts = {})
|
||||||
id = if id.equal?(ARGUMENT_NOT_PROVIDED)
|
Util.check_string_argument!(id) if id
|
||||||
nil
|
|
||||||
else
|
|
||||||
Util.check_string_argument!(id)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Account used to be a singleton, where this method's signature was
|
# Account used to be a singleton, where this method's signature was
|
||||||
# `(opts={})`. For the sake of not breaking folks who pass in an OAuth
|
# `(opts={})`. For the sake of not breaking folks who pass in an OAuth
|
||||||
@ -136,11 +132,10 @@ module Stripe
|
|||||||
client_id: client_id,
|
client_id: client_id,
|
||||||
stripe_user_id: id,
|
stripe_user_id: id,
|
||||||
}
|
}
|
||||||
|
opts = @opts.merge(Util.normalize_opts(opts))
|
||||||
OAuth.deauthorize(params, opts)
|
OAuth.deauthorize(params, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
ARGUMENT_NOT_PROVIDED = Object.new
|
|
||||||
|
|
||||||
private def serialize_additional_owners(legal_entity, additional_owners)
|
private def serialize_additional_owners(legal_entity, additional_owners)
|
||||||
original_value =
|
original_value =
|
||||||
legal_entity
|
legal_entity
|
||||||
|
|||||||
@ -25,8 +25,9 @@ module Stripe
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
config = opts[:client]&.config || Stripe.configuration
|
||||||
opts = {
|
opts = {
|
||||||
api_base: Stripe.uploads_base,
|
api_base: config.uploads_base,
|
||||||
content_type: MultipartEncoder::MULTIPART_FORM_DATA,
|
content_type: MultipartEncoder::MULTIPART_FORM_DATA,
|
||||||
}.merge(Util.normalize_opts(opts))
|
}.merge(Util.normalize_opts(opts))
|
||||||
super
|
super
|
||||||
|
|||||||
@ -9,19 +9,35 @@ module Stripe
|
|||||||
class StripeClient
|
class StripeClient
|
||||||
# A set of all known thread contexts across all threads and a mutex to
|
# A set of all known thread contexts across all threads and a mutex to
|
||||||
# synchronize global access to them.
|
# synchronize global access to them.
|
||||||
@thread_contexts_with_connection_managers = []
|
@thread_contexts_with_connection_managers = Set.new
|
||||||
@thread_contexts_with_connection_managers_mutex = Mutex.new
|
@thread_contexts_with_connection_managers_mutex = Mutex.new
|
||||||
@last_connection_manager_gc = Util.monotonic_time
|
@last_connection_manager_gc = Util.monotonic_time
|
||||||
|
|
||||||
# Initializes a new `StripeClient`.
|
# Initializes a new StripeClient
|
||||||
#
|
def initialize(config_overrides = {})
|
||||||
# Takes a connection manager object for backwards compatibility only, and
|
|
||||||
# that use is DEPRECATED.
|
|
||||||
def initialize(_connection_manager = nil)
|
|
||||||
@system_profiler = SystemProfiler.new
|
@system_profiler = SystemProfiler.new
|
||||||
@last_request_metrics = nil
|
@last_request_metrics = nil
|
||||||
|
|
||||||
|
# Supports accepting a connection manager object for backwards
|
||||||
|
# compatibility only, and that use is DEPRECATED.
|
||||||
|
@config_overrides = case config_overrides
|
||||||
|
when Stripe::ConnectionManager
|
||||||
|
{}
|
||||||
|
when String
|
||||||
|
{ api_key: config_overrides }
|
||||||
|
else
|
||||||
|
config_overrides
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Always base config off the global Stripe configuration to ensure the
|
||||||
|
# client picks up any changes to the config.
|
||||||
|
def config
|
||||||
|
Stripe.configuration.reverse_duplicate_merge(@config_overrides)
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :options
|
||||||
|
|
||||||
# Gets a currently active `StripeClient`. Set for the current thread when
|
# Gets a currently active `StripeClient`. Set for the current thread when
|
||||||
# `StripeClient#request` is being run so that API operations being executed
|
# `StripeClient#request` is being run so that API operations being executed
|
||||||
# inside of that block can find the currently active client. It's reset to
|
# inside of that block can find the currently active client. It's reset to
|
||||||
@ -51,8 +67,8 @@ module Stripe
|
|||||||
# its connection manager and remove our reference to it. If it ever
|
# its connection manager and remove our reference to it. If it ever
|
||||||
# makes a new request we'll give it a new connection manager and
|
# makes a new request we'll give it a new connection manager and
|
||||||
# it'll go back into `@thread_contexts_with_connection_managers`.
|
# it'll go back into `@thread_contexts_with_connection_managers`.
|
||||||
thread_context.default_connection_manager.clear
|
thread_context.default_connection_managers.map { |_, cm| cm.clear }
|
||||||
thread_context.default_connection_manager = nil
|
thread_context.reset_connection_managers
|
||||||
end
|
end
|
||||||
@thread_contexts_with_connection_managers.clear
|
@thread_contexts_with_connection_managers.clear
|
||||||
end
|
end
|
||||||
@ -63,10 +79,11 @@ module Stripe
|
|||||||
current_thread_context.default_client ||= StripeClient.new
|
current_thread_context.default_client ||= StripeClient.new
|
||||||
end
|
end
|
||||||
|
|
||||||
# A default connection manager for the current thread.
|
# A default connection manager for the current thread scoped to the
|
||||||
def self.default_connection_manager
|
# configuration object that may be provided.
|
||||||
current_thread_context.default_connection_manager ||= begin
|
def self.default_connection_manager(config = Stripe.configuration)
|
||||||
connection_manager = ConnectionManager.new
|
current_thread_context.default_connection_managers[config.key] ||= begin
|
||||||
|
connection_manager = ConnectionManager.new(config)
|
||||||
|
|
||||||
@thread_contexts_with_connection_managers_mutex.synchronize do
|
@thread_contexts_with_connection_managers_mutex.synchronize do
|
||||||
maybe_gc_connection_managers
|
maybe_gc_connection_managers
|
||||||
@ -80,8 +97,9 @@ module Stripe
|
|||||||
# Checks if an error is a problem that we should retry on. This includes
|
# Checks if an error is a problem that we should retry on. This includes
|
||||||
# both socket errors that may represent an intermittent problem and some
|
# both socket errors that may represent an intermittent problem and some
|
||||||
# special HTTP statuses.
|
# special HTTP statuses.
|
||||||
def self.should_retry?(error, method:, num_retries:)
|
def self.should_retry?(error,
|
||||||
return false if num_retries >= Stripe.max_network_retries
|
method:, num_retries:, config: Stripe.configuration)
|
||||||
|
return false if num_retries >= config.max_network_retries
|
||||||
|
|
||||||
case error
|
case error
|
||||||
when Net::OpenTimeout, Net::ReadTimeout
|
when Net::OpenTimeout, Net::ReadTimeout
|
||||||
@ -127,13 +145,13 @@ module Stripe
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.sleep_time(num_retries)
|
def self.sleep_time(num_retries, config: Stripe.configuration)
|
||||||
# Apply exponential backoff with initial_network_retry_delay on the
|
# Apply exponential backoff with initial_network_retry_delay on the
|
||||||
# number of num_retries so far as inputs. Do not allow the number to
|
# number of num_retries so far as inputs. Do not allow the number to
|
||||||
# exceed max_network_retry_delay.
|
# exceed max_network_retry_delay.
|
||||||
sleep_seconds = [
|
sleep_seconds = [
|
||||||
Stripe.initial_network_retry_delay * (2**(num_retries - 1)),
|
config.initial_network_retry_delay * (2**(num_retries - 1)),
|
||||||
Stripe.max_network_retry_delay,
|
config.max_network_retry_delay,
|
||||||
].min
|
].min
|
||||||
|
|
||||||
# Apply some jitter by randomizing the value in the range of
|
# Apply some jitter by randomizing the value in the range of
|
||||||
@ -141,9 +159,7 @@ module Stripe
|
|||||||
sleep_seconds *= (0.5 * (1 + rand))
|
sleep_seconds *= (0.5 * (1 + rand))
|
||||||
|
|
||||||
# But never sleep less than the base sleep seconds.
|
# But never sleep less than the base sleep seconds.
|
||||||
sleep_seconds = [Stripe.initial_network_retry_delay, sleep_seconds].max
|
[config.initial_network_retry_delay, sleep_seconds].max
|
||||||
|
|
||||||
sleep_seconds
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Gets the connection manager in use for the current `StripeClient`.
|
# Gets the connection manager in use for the current `StripeClient`.
|
||||||
@ -187,8 +203,8 @@ module Stripe
|
|||||||
raise ArgumentError, "path should be a string" \
|
raise ArgumentError, "path should be a string" \
|
||||||
unless path.is_a?(String)
|
unless path.is_a?(String)
|
||||||
|
|
||||||
api_base ||= Stripe.api_base
|
api_base ||= config.api_base
|
||||||
api_key ||= Stripe.api_key
|
api_key ||= config.api_key
|
||||||
params = Util.objects_to_ids(params)
|
params = Util.objects_to_ids(params)
|
||||||
|
|
||||||
check_api_key!(api_key)
|
check_api_key!(api_key)
|
||||||
@ -231,10 +247,12 @@ module Stripe
|
|||||||
context.query = query
|
context.query = query
|
||||||
|
|
||||||
http_resp = execute_request_with_rescues(method, api_base, context) do
|
http_resp = execute_request_with_rescues(method, api_base, context) do
|
||||||
self.class.default_connection_manager.execute_request(method, url,
|
self.class
|
||||||
body: body,
|
.default_connection_manager(config)
|
||||||
headers: headers,
|
.execute_request(method, url,
|
||||||
query: query)
|
body: body,
|
||||||
|
headers: headers,
|
||||||
|
query: query)
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@ -246,13 +264,21 @@ module Stripe
|
|||||||
# If being called from `StripeClient#request`, put the last response in
|
# If being called from `StripeClient#request`, put the last response in
|
||||||
# thread-local memory so that it can be returned to the user. Don't store
|
# thread-local memory so that it can be returned to the user. Don't store
|
||||||
# anything otherwise so that we don't leak memory.
|
# anything otherwise so that we don't leak memory.
|
||||||
if self.class.current_thread_context.last_responses&.key?(object_id)
|
store_last_response(object_id, resp)
|
||||||
self.class.current_thread_context.last_responses[object_id] = resp
|
|
||||||
end
|
|
||||||
|
|
||||||
[resp, api_key]
|
[resp, api_key]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def store_last_response(object_id, resp)
|
||||||
|
return unless last_response_has_key?(object_id)
|
||||||
|
|
||||||
|
self.class.current_thread_context.last_responses[object_id] = resp
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_response_has_key?(object_id)
|
||||||
|
self.class.current_thread_context.last_responses&.key?(object_id)
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# private
|
# private
|
||||||
#
|
#
|
||||||
@ -328,11 +354,6 @@ module Stripe
|
|||||||
# the user hasn't specified their own.
|
# the user hasn't specified their own.
|
||||||
attr_accessor :default_client
|
attr_accessor :default_client
|
||||||
|
|
||||||
# A default `ConnectionManager` for the thread. Normally shared between
|
|
||||||
# all `StripeClient` objects on a particular thread, and created so as to
|
|
||||||
# minimize the number of open connections that an application needs.
|
|
||||||
attr_accessor :default_connection_manager
|
|
||||||
|
|
||||||
# A temporary map of object IDs to responses from last executed API
|
# A temporary map of object IDs to responses from last executed API
|
||||||
# calls. Used to return a responses from calls to `StripeClient#request`.
|
# calls. Used to return a responses from calls to `StripeClient#request`.
|
||||||
#
|
#
|
||||||
@ -345,6 +366,17 @@ module Stripe
|
|||||||
# because that's wrapped in an `ensure` block, they should never leave
|
# because that's wrapped in an `ensure` block, they should never leave
|
||||||
# garbage in `Thread.current`.
|
# garbage in `Thread.current`.
|
||||||
attr_accessor :last_responses
|
attr_accessor :last_responses
|
||||||
|
|
||||||
|
# A map of connection mangers for the thread. Normally shared between
|
||||||
|
# all `StripeClient` objects on a particular thread, and created so as to
|
||||||
|
# minimize the number of open connections that an application needs.
|
||||||
|
def default_connection_managers
|
||||||
|
@default_connection_managers ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset_connection_managers
|
||||||
|
@default_connection_managers = {}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Access data stored for `StripeClient` within the thread's current
|
# Access data stored for `StripeClient` within the thread's current
|
||||||
@ -382,11 +414,19 @@ module Stripe
|
|||||||
|
|
||||||
pruned_thread_contexts = []
|
pruned_thread_contexts = []
|
||||||
@thread_contexts_with_connection_managers.each do |thread_context|
|
@thread_contexts_with_connection_managers.each do |thread_context|
|
||||||
connection_manager = thread_context.default_connection_manager
|
thread_context
|
||||||
next if connection_manager.last_used > last_used_threshold
|
.default_connection_managers
|
||||||
|
.each do |config_key, connection_manager|
|
||||||
|
next if connection_manager.last_used > last_used_threshold
|
||||||
|
|
||||||
|
connection_manager.clear
|
||||||
|
thread_context.default_connection_managers.delete(config_key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@thread_contexts_with_connection_managers.each do |thread_context|
|
||||||
|
next unless thread_context.default_connection_managers.empty?
|
||||||
|
|
||||||
connection_manager.clear
|
|
||||||
thread_context.default_connection_manager = nil
|
|
||||||
pruned_thread_contexts << thread_context
|
pruned_thread_contexts << thread_context
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -397,7 +437,7 @@ module Stripe
|
|||||||
end
|
end
|
||||||
|
|
||||||
private def api_url(url = "", api_base = nil)
|
private def api_url(url = "", api_base = nil)
|
||||||
(api_base || Stripe.api_base) + url
|
(api_base || config.api_base) + url
|
||||||
end
|
end
|
||||||
|
|
||||||
private def check_api_key!(api_key)
|
private def check_api_key!(api_key)
|
||||||
@ -471,7 +511,7 @@ module Stripe
|
|||||||
notify_request_end(context, request_duration, http_status,
|
notify_request_end(context, request_duration, http_status,
|
||||||
num_retries, user_data)
|
num_retries, user_data)
|
||||||
|
|
||||||
if Stripe.enable_telemetry? && context.request_id
|
if config.enable_telemetry? && context.request_id
|
||||||
request_duration_ms = (request_duration * 1000).to_i
|
request_duration_ms = (request_duration * 1000).to_i
|
||||||
@last_request_metrics =
|
@last_request_metrics =
|
||||||
StripeRequestMetrics.new(context.request_id, request_duration_ms)
|
StripeRequestMetrics.new(context.request_id, request_duration_ms)
|
||||||
@ -498,9 +538,12 @@ module Stripe
|
|||||||
notify_request_end(context, request_duration, http_status, num_retries,
|
notify_request_end(context, request_duration, http_status, num_retries,
|
||||||
user_data)
|
user_data)
|
||||||
|
|
||||||
if self.class.should_retry?(e, method: method, num_retries: num_retries)
|
if self.class.should_retry?(e,
|
||||||
|
method: method,
|
||||||
|
num_retries: num_retries,
|
||||||
|
config: config)
|
||||||
num_retries += 1
|
num_retries += 1
|
||||||
sleep self.class.sleep_time(num_retries)
|
sleep self.class.sleep_time(num_retries, config: config)
|
||||||
retry
|
retry
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -622,7 +665,8 @@ module Stripe
|
|||||||
error_param: error_data[:param],
|
error_param: error_data[:param],
|
||||||
error_type: error_data[:type],
|
error_type: error_data[:type],
|
||||||
idempotency_key: context.idempotency_key,
|
idempotency_key: context.idempotency_key,
|
||||||
request_id: context.request_id)
|
request_id: context.request_id,
|
||||||
|
config: config)
|
||||||
|
|
||||||
# The standard set of arguments that can be used to initialize most of
|
# The standard set of arguments that can be used to initialize most of
|
||||||
# the exceptions.
|
# the exceptions.
|
||||||
@ -671,7 +715,8 @@ module Stripe
|
|||||||
error_code: error_code,
|
error_code: error_code,
|
||||||
error_description: description,
|
error_description: description,
|
||||||
idempotency_key: context.idempotency_key,
|
idempotency_key: context.idempotency_key,
|
||||||
request_id: context.request_id)
|
request_id: context.request_id,
|
||||||
|
config: config)
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
http_status: resp.http_status, http_body: resp.http_body,
|
http_status: resp.http_status, http_body: resp.http_body,
|
||||||
@ -703,7 +748,8 @@ module Stripe
|
|||||||
Util.log_error("Stripe network error",
|
Util.log_error("Stripe network error",
|
||||||
error_message: error.message,
|
error_message: error.message,
|
||||||
idempotency_key: context.idempotency_key,
|
idempotency_key: context.idempotency_key,
|
||||||
request_id: context.request_id)
|
request_id: context.request_id,
|
||||||
|
config: config)
|
||||||
|
|
||||||
errors, message = NETWORK_ERROR_MESSAGES_MAP.detect do |(e, _)|
|
errors, message = NETWORK_ERROR_MESSAGES_MAP.detect do |(e, _)|
|
||||||
error.is_a?(e)
|
error.is_a?(e)
|
||||||
@ -714,7 +760,7 @@ module Stripe
|
|||||||
"with Stripe. Please let us know at support@stripe.com."
|
"with Stripe. Please let us know at support@stripe.com."
|
||||||
end
|
end
|
||||||
|
|
||||||
api_base ||= Stripe.api_base
|
api_base ||= config.api_base
|
||||||
message = message % api_base
|
message = message % api_base
|
||||||
|
|
||||||
message += " Request was retried #{num_retries} times." if num_retries > 0
|
message += " Request was retried #{num_retries} times." if num_retries > 0
|
||||||
@ -735,7 +781,7 @@ module Stripe
|
|||||||
"Content-Type" => "application/x-www-form-urlencoded",
|
"Content-Type" => "application/x-www-form-urlencoded",
|
||||||
}
|
}
|
||||||
|
|
||||||
if Stripe.enable_telemetry? && !@last_request_metrics.nil?
|
if config.enable_telemetry? && !@last_request_metrics.nil?
|
||||||
headers["X-Stripe-Client-Telemetry"] = JSON.generate(
|
headers["X-Stripe-Client-Telemetry"] = JSON.generate(
|
||||||
last_request_metrics: @last_request_metrics.payload
|
last_request_metrics: @last_request_metrics.payload
|
||||||
)
|
)
|
||||||
@ -743,12 +789,12 @@ module Stripe
|
|||||||
|
|
||||||
# It is only safe to retry network failures on post and delete
|
# It is only safe to retry network failures on post and delete
|
||||||
# requests if we add an Idempotency-Key header
|
# requests if we add an Idempotency-Key header
|
||||||
if %i[post delete].include?(method) && Stripe.max_network_retries > 0
|
if %i[post delete].include?(method) && config.max_network_retries > 0
|
||||||
headers["Idempotency-Key"] ||= SecureRandom.uuid
|
headers["Idempotency-Key"] ||= SecureRandom.uuid
|
||||||
end
|
end
|
||||||
|
|
||||||
headers["Stripe-Version"] = Stripe.api_version if Stripe.api_version
|
headers["Stripe-Version"] = config.api_version if config.api_version
|
||||||
headers["Stripe-Account"] = Stripe.stripe_account if Stripe.stripe_account
|
headers["Stripe-Account"] = config.stripe_account if config.stripe_account
|
||||||
|
|
||||||
user_agent = @system_profiler.user_agent
|
user_agent = @system_profiler.user_agent
|
||||||
begin
|
begin
|
||||||
@ -772,11 +818,13 @@ module Stripe
|
|||||||
idempotency_key: context.idempotency_key,
|
idempotency_key: context.idempotency_key,
|
||||||
method: context.method,
|
method: context.method,
|
||||||
num_retries: num_retries,
|
num_retries: num_retries,
|
||||||
path: context.path)
|
path: context.path,
|
||||||
|
config: config)
|
||||||
Util.log_debug("Request details",
|
Util.log_debug("Request details",
|
||||||
body: context.body,
|
body: context.body,
|
||||||
idempotency_key: context.idempotency_key,
|
idempotency_key: context.idempotency_key,
|
||||||
query: context.query)
|
query: context.query,
|
||||||
|
config: config)
|
||||||
end
|
end
|
||||||
|
|
||||||
private def log_response(context, request_start, status, body)
|
private def log_response(context, request_start, status, body)
|
||||||
@ -788,11 +836,13 @@ module Stripe
|
|||||||
method: context.method,
|
method: context.method,
|
||||||
path: context.path,
|
path: context.path,
|
||||||
request_id: context.request_id,
|
request_id: context.request_id,
|
||||||
status: status)
|
status: status,
|
||||||
|
config: config)
|
||||||
Util.log_debug("Response details",
|
Util.log_debug("Response details",
|
||||||
body: body,
|
body: body,
|
||||||
idempotency_key: context.idempotency_key,
|
idempotency_key: context.idempotency_key,
|
||||||
request_id: context.request_id)
|
request_id: context.request_id,
|
||||||
|
config: config)
|
||||||
|
|
||||||
return unless context.request_id
|
return unless context.request_id
|
||||||
|
|
||||||
@ -800,7 +850,8 @@ module Stripe
|
|||||||
idempotency_key: context.idempotency_key,
|
idempotency_key: context.idempotency_key,
|
||||||
request_id: context.request_id,
|
request_id: context.request_id,
|
||||||
url: Util.request_id_dashboard_url(context.request_id,
|
url: Util.request_id_dashboard_url(context.request_id,
|
||||||
context.api_key))
|
context.api_key),
|
||||||
|
config: config)
|
||||||
end
|
end
|
||||||
|
|
||||||
private def log_response_error(context, request_start, error)
|
private def log_response_error(context, request_start, error)
|
||||||
@ -810,7 +861,8 @@ module Stripe
|
|||||||
error_message: error.message,
|
error_message: error.message,
|
||||||
idempotency_key: context.idempotency_key,
|
idempotency_key: context.idempotency_key,
|
||||||
method: context.method,
|
method: context.method,
|
||||||
path: context.path)
|
path: context.path,
|
||||||
|
config: config)
|
||||||
end
|
end
|
||||||
|
|
||||||
# RequestLogContext stores information about a request that's begin made so
|
# RequestLogContext stores information about a request that's begin made so
|
||||||
|
|||||||
@ -101,6 +101,14 @@ module Stripe
|
|||||||
@max_network_retries = val.to_i
|
@max_network_retries = val.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def max_network_retry_delay=(val)
|
||||||
|
@max_network_retry_delay = val.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def initial_network_retry_delay=(val)
|
||||||
|
@initial_network_retry_delay = val.to_i
|
||||||
|
end
|
||||||
|
|
||||||
def open_timeout=(open_timeout)
|
def open_timeout=(open_timeout)
|
||||||
@open_timeout = open_timeout
|
@open_timeout = open_timeout
|
||||||
StripeClient.clear_all_connection_managers
|
StripeClient.clear_all_connection_managers
|
||||||
@ -174,5 +182,13 @@ module Stripe
|
|||||||
def enable_telemetry?
|
def enable_telemetry?
|
||||||
enable_telemetry
|
enable_telemetry
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Generates a deterministic key to identify configuration objects with
|
||||||
|
# identical configuration values.
|
||||||
|
def key
|
||||||
|
instance_variables
|
||||||
|
.collect { |variable| instance_variable_get(variable) }
|
||||||
|
.join
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -76,24 +76,30 @@ module Stripe
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.log_error(message, data = {})
|
def self.log_error(message, data = {})
|
||||||
if !Stripe.logger.nil? ||
|
config = data.delete(:config) || Stripe.configuration
|
||||||
!Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_ERROR
|
logger = config.logger || Stripe.logger
|
||||||
|
if !logger.nil? ||
|
||||||
|
!config.log_level.nil? && config.log_level <= Stripe::LEVEL_ERROR
|
||||||
log_internal(message, data, color: :cyan, level: Stripe::LEVEL_ERROR,
|
log_internal(message, data, color: :cyan, level: Stripe::LEVEL_ERROR,
|
||||||
logger: Stripe.logger, out: $stderr)
|
logger: Stripe.logger, out: $stderr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.log_info(message, data = {})
|
def self.log_info(message, data = {})
|
||||||
if !Stripe.logger.nil? ||
|
config = data.delete(:config) || Stripe.configuration
|
||||||
!Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_INFO
|
logger = config.logger || Stripe.logger
|
||||||
|
if !logger.nil? ||
|
||||||
|
!config.log_level.nil? && config.log_level <= Stripe::LEVEL_INFO
|
||||||
log_internal(message, data, color: :cyan, level: Stripe::LEVEL_INFO,
|
log_internal(message, data, color: :cyan, level: Stripe::LEVEL_INFO,
|
||||||
logger: Stripe.logger, out: $stdout)
|
logger: Stripe.logger, out: $stdout)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.log_debug(message, data = {})
|
def self.log_debug(message, data = {})
|
||||||
if !Stripe.logger.nil? ||
|
config = data.delete(:config) || Stripe.configuration
|
||||||
!Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_DEBUG
|
logger = config.logger || Stripe.logger
|
||||||
|
if !logger.nil? ||
|
||||||
|
!config.log_level.nil? && config.log_level <= Stripe::LEVEL_DEBUG
|
||||||
log_internal(message, data, color: :blue, level: Stripe::LEVEL_DEBUG,
|
log_internal(message, data, color: :blue, level: Stripe::LEVEL_DEBUG,
|
||||||
logger: Stripe.logger, out: $stdout)
|
logger: Stripe.logger, out: $stdout)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -79,6 +79,49 @@ module Stripe
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when a StripeClient has different configurations" do
|
||||||
|
should "correctly initialize a connection" do
|
||||||
|
old_proxy = Stripe.proxy
|
||||||
|
|
||||||
|
old_open_timeout = Stripe.open_timeout
|
||||||
|
old_read_timeout = Stripe.read_timeout
|
||||||
|
|
||||||
|
begin
|
||||||
|
client = StripeClient.new(
|
||||||
|
proxy: "http://other:pass@localhost:8080",
|
||||||
|
open_timeout: 400,
|
||||||
|
read_timeout: 500,
|
||||||
|
verify_ssl_certs: true
|
||||||
|
)
|
||||||
|
conn = Stripe::ConnectionManager.new(client.config)
|
||||||
|
.connection_for("https://stripe.com")
|
||||||
|
|
||||||
|
# Host/port
|
||||||
|
assert_equal "stripe.com", conn.address
|
||||||
|
assert_equal 443, conn.port
|
||||||
|
|
||||||
|
# Proxy
|
||||||
|
assert_equal "localhost", conn.proxy_address
|
||||||
|
assert_equal 8080, conn.proxy_port
|
||||||
|
assert_equal "other", conn.proxy_user
|
||||||
|
assert_equal "pass", conn.proxy_pass
|
||||||
|
|
||||||
|
# Timeouts
|
||||||
|
assert_equal 400, conn.open_timeout
|
||||||
|
assert_equal 500, conn.read_timeout
|
||||||
|
|
||||||
|
assert_equal true, conn.use_ssl?
|
||||||
|
assert_equal OpenSSL::SSL::VERIFY_PEER, conn.verify_mode
|
||||||
|
assert_equal Stripe.ca_store, conn.cert_store
|
||||||
|
ensure
|
||||||
|
Stripe.proxy = old_proxy
|
||||||
|
|
||||||
|
Stripe.open_timeout = old_open_timeout
|
||||||
|
Stripe.read_timeout = old_read_timeout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
should "produce the same connection multiple times" do
|
should "produce the same connection multiple times" do
|
||||||
conn1 = @manager.connection_for("https://stripe.com")
|
conn1 = @manager.connection_for("https://stripe.com")
|
||||||
conn2 = @manager.connection_for("https://stripe.com")
|
conn2 = @manager.connection_for("https://stripe.com")
|
||||||
|
|||||||
@ -44,6 +44,14 @@ module Stripe
|
|||||||
assert_equal("connect.stripe.com", uri.host)
|
assert_equal("connect.stripe.com", uri.host)
|
||||||
assert_equal("/express/oauth/authorize", uri.path)
|
assert_equal("/express/oauth/authorize", uri.path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "override the api base path when a StripeClient is provided" do
|
||||||
|
client = Stripe::StripeClient.new(connect_base: "https://other.stripe.com")
|
||||||
|
uri_str = OAuth.authorize_url({}, client: client)
|
||||||
|
|
||||||
|
uri = URI.parse(uri_str)
|
||||||
|
assert_equal("other.stripe.com", uri.host)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context ".token" do
|
context ".token" do
|
||||||
@ -83,6 +91,29 @@ module Stripe
|
|||||||
code: "this_is_an_authorization_code")
|
code: "this_is_an_authorization_code")
|
||||||
assert_equal("another_access_token", resp.access_token)
|
assert_equal("another_access_token", resp.access_token)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "override the api base path when a StripeClient is provided" do
|
||||||
|
stub_request(:post, "https://other.stripe.com/oauth/token")
|
||||||
|
.with(body: {
|
||||||
|
"grant_type" => "authorization_code",
|
||||||
|
"code" => "this_is_an_authorization_code",
|
||||||
|
})
|
||||||
|
.to_return(body: JSON.generate(access_token: "sk_access_token",
|
||||||
|
scope: "read_only",
|
||||||
|
livemode: false,
|
||||||
|
token_type: "bearer",
|
||||||
|
refresh_token: "sk_refresh_token",
|
||||||
|
stripe_user_id: "acct_test",
|
||||||
|
stripe_publishable_key: "pk_test"))
|
||||||
|
|
||||||
|
client = Stripe::StripeClient.new(connect_base: "https://other.stripe.com")
|
||||||
|
resp = OAuth.token(
|
||||||
|
{ grant_type: "authorization_code", code: "this_is_an_authorization_code" },
|
||||||
|
client: client
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_equal("sk_access_token", resp.access_token)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context ".deauthorize" do
|
context ".deauthorize" do
|
||||||
@ -99,6 +130,20 @@ module Stripe
|
|||||||
resp = OAuth.deauthorize(stripe_user_id: "acct_test_deauth")
|
resp = OAuth.deauthorize(stripe_user_id: "acct_test_deauth")
|
||||||
assert_equal("acct_test_deauth", resp.stripe_user_id)
|
assert_equal("acct_test_deauth", resp.stripe_user_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "override the api base path when a StripeClient is provided" do
|
||||||
|
stub_request(:post, "https://other.stripe.com/oauth/deauthorize")
|
||||||
|
.with(body: {
|
||||||
|
"client_id" => "ca_test",
|
||||||
|
"stripe_user_id" => "acct_test_deauth",
|
||||||
|
})
|
||||||
|
.to_return(body: JSON.generate(stripe_user_id: "acct_test_deauth"))
|
||||||
|
|
||||||
|
client = Stripe::StripeClient.new(connect_base: "https://other.stripe.com")
|
||||||
|
resp = OAuth.deauthorize({ stripe_user_id: "acct_test_deauth" }, client: client)
|
||||||
|
|
||||||
|
assert_equal("acct_test_deauth", resp.stripe_user_id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -4,6 +4,24 @@ require ::File.expand_path("../test_helper", __dir__)
|
|||||||
|
|
||||||
module Stripe
|
module Stripe
|
||||||
class StripeClientTest < Test::Unit::TestCase
|
class StripeClientTest < Test::Unit::TestCase
|
||||||
|
context "initializing a StripeClient" do
|
||||||
|
should "allow a String to be passed in order to set the api key" do
|
||||||
|
assert_equal StripeClient.new("test_123").config.api_key, "test_123"
|
||||||
|
end
|
||||||
|
|
||||||
|
should "allow for overrides via a Hash" do
|
||||||
|
config = { api_key: "test_123", open_timeout: 100 }
|
||||||
|
client = StripeClient.new(config)
|
||||||
|
|
||||||
|
assert_equal client.config.api_key, "test_123"
|
||||||
|
assert_equal client.config.open_timeout, 100
|
||||||
|
end
|
||||||
|
|
||||||
|
should "support deprecated ConnectionManager objects" do
|
||||||
|
assert StripeClient.new(Stripe::ConnectionManager.new).config.is_a?(Stripe::StripeConfiguration)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context ".active_client" do
|
context ".active_client" do
|
||||||
should "be .default_client outside of #request" do
|
should "be .default_client outside of #request" do
|
||||||
assert_equal StripeClient.default_client, StripeClient.active_client
|
assert_equal StripeClient.default_client, StripeClient.active_client
|
||||||
@ -82,8 +100,64 @@ module Stripe
|
|||||||
assert_equal 1, StripeClient.maybe_gc_connection_managers
|
assert_equal 1, StripeClient.maybe_gc_connection_managers
|
||||||
|
|
||||||
# And as an additional check, the connection manager of the current
|
# And as an additional check, the connection manager of the current
|
||||||
# thread context should have been set to `nil` as it was GCed.
|
# thread context should have been removed as it was GCed.
|
||||||
assert_nil StripeClient.current_thread_context.default_connection_manager
|
assert_equal({}, StripeClient.current_thread_context.default_connection_managers)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "only garbage collect when all connection managers for a thread are expired" do
|
||||||
|
stub_request(:post, "#{Stripe.api_base}/v1/path")
|
||||||
|
.to_return(body: JSON.generate(object: "account"))
|
||||||
|
|
||||||
|
# Make sure we start with a blank slate (state may have been left in
|
||||||
|
# place by other tests).
|
||||||
|
StripeClient.clear_all_connection_managers
|
||||||
|
|
||||||
|
# Establish a base time.
|
||||||
|
t = 0.0
|
||||||
|
|
||||||
|
# And pretend that `StripeClient` was just initialized for the first
|
||||||
|
# time. (Don't access instance variables like this, but it's tricky to
|
||||||
|
# test properly otherwise.)
|
||||||
|
StripeClient.instance_variable_set(:@last_connection_manager_gc, t)
|
||||||
|
|
||||||
|
#
|
||||||
|
# t
|
||||||
|
#
|
||||||
|
Util.stubs(:monotonic_time).returns(t)
|
||||||
|
|
||||||
|
# Execute an initial request to ensure that a connection manager was
|
||||||
|
# created.
|
||||||
|
client = StripeClient.new
|
||||||
|
client.execute_request(:post, "/v1/path")
|
||||||
|
|
||||||
|
# Create a new client with a unique config to make sure the thread has two
|
||||||
|
# connection managers
|
||||||
|
active_client = StripeClient.new(max_network_retries: 10)
|
||||||
|
active_client.execute_request(:post, "/v1/path")
|
||||||
|
|
||||||
|
assert_equal 2, StripeClient.current_thread_context.default_connection_managers.keys.count
|
||||||
|
assert_equal nil, StripeClient.maybe_gc_connection_managers
|
||||||
|
|
||||||
|
# t + StripeClient::CONNECTION_MANAGER_GC_LAST_USED_EXPIRY + 1
|
||||||
|
#
|
||||||
|
# Move us far enough into the future that we're passed the horizons for
|
||||||
|
# both a GC run as well as well as the expiry age of a connection
|
||||||
|
# manager. That means the GC will run and collect the connection
|
||||||
|
# manager that we created above.
|
||||||
|
#
|
||||||
|
Util.stubs(:monotonic_time).returns(t + StripeClient::CONNECTION_MANAGER_GC_LAST_USED_EXPIRY + 1)
|
||||||
|
|
||||||
|
# Manually set the active_client's last_used time into the future to prevent GC.
|
||||||
|
StripeClient.default_connection_manager(active_client.config)
|
||||||
|
.instance_variable_set(:@last_used, Util.monotonic_time + 1)
|
||||||
|
|
||||||
|
assert_equal 0, StripeClient.maybe_gc_connection_managers
|
||||||
|
|
||||||
|
# Move time into the future past the last GC round
|
||||||
|
current_time = Util.monotonic_time
|
||||||
|
Util.stubs(:monotonic_time).returns(current_time * 2)
|
||||||
|
|
||||||
|
assert_equal 1, StripeClient.maybe_gc_connection_managers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -160,11 +234,26 @@ module Stripe
|
|||||||
thread.join
|
thread.join
|
||||||
refute_equal StripeClient.default_connection_manager, other_thread_manager
|
refute_equal StripeClient.default_connection_manager, other_thread_manager
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "create a separate connection manager per configuration" do
|
||||||
|
config = Stripe::StripeConfiguration.setup { |c| c.open_timeout = 100 }
|
||||||
|
connection_manager_one = StripeClient.default_connection_manager
|
||||||
|
connection_manager_two = StripeClient.default_connection_manager(config)
|
||||||
|
|
||||||
|
assert_equal connection_manager_one.config.open_timeout, 30
|
||||||
|
assert_equal connection_manager_two.config.open_timeout, 100
|
||||||
|
end
|
||||||
|
|
||||||
|
should "create a single connection manager for identitical configurations" do
|
||||||
|
2.times { StripeClient.default_connection_manager(Stripe::StripeConfiguration.setup) }
|
||||||
|
|
||||||
|
assert_equal 1, StripeClient.instance_variable_get(:@thread_contexts_with_connection_managers).first.default_connection_managers.size
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context ".should_retry?" do
|
context ".should_retry?" do
|
||||||
setup do
|
setup do
|
||||||
Stripe.stubs(:max_network_retries).returns(2)
|
Stripe::StripeConfiguration.any_instance.stubs(:max_network_retries).returns(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "retry on Errno::ECONNREFUSED" do
|
should "retry on Errno::ECONNREFUSED" do
|
||||||
@ -275,7 +364,7 @@ module Stripe
|
|||||||
context ".sleep_time" do
|
context ".sleep_time" do
|
||||||
should "should grow exponentially" do
|
should "should grow exponentially" do
|
||||||
StripeClient.stubs(:rand).returns(1)
|
StripeClient.stubs(:rand).returns(1)
|
||||||
Stripe.stubs(:max_network_retry_delay).returns(999)
|
Stripe.configuration.stubs(:max_network_retry_delay).returns(999)
|
||||||
assert_equal(Stripe.initial_network_retry_delay, StripeClient.sleep_time(1))
|
assert_equal(Stripe.initial_network_retry_delay, StripeClient.sleep_time(1))
|
||||||
assert_equal(Stripe.initial_network_retry_delay * 2, StripeClient.sleep_time(2))
|
assert_equal(Stripe.initial_network_retry_delay * 2, StripeClient.sleep_time(2))
|
||||||
assert_equal(Stripe.initial_network_retry_delay * 4, StripeClient.sleep_time(3))
|
assert_equal(Stripe.initial_network_retry_delay * 4, StripeClient.sleep_time(3))
|
||||||
@ -284,8 +373,8 @@ module Stripe
|
|||||||
|
|
||||||
should "enforce the max_network_retry_delay" do
|
should "enforce the max_network_retry_delay" do
|
||||||
StripeClient.stubs(:rand).returns(1)
|
StripeClient.stubs(:rand).returns(1)
|
||||||
Stripe.stubs(:initial_network_retry_delay).returns(1)
|
Stripe.configuration.stubs(:initial_network_retry_delay).returns(1)
|
||||||
Stripe.stubs(:max_network_retry_delay).returns(2)
|
Stripe.configuration.stubs(:max_network_retry_delay).returns(2)
|
||||||
assert_equal(1, StripeClient.sleep_time(1))
|
assert_equal(1, StripeClient.sleep_time(1))
|
||||||
assert_equal(2, StripeClient.sleep_time(2))
|
assert_equal(2, StripeClient.sleep_time(2))
|
||||||
assert_equal(2, StripeClient.sleep_time(3))
|
assert_equal(2, StripeClient.sleep_time(3))
|
||||||
@ -295,8 +384,8 @@ module Stripe
|
|||||||
should "add some randomness" do
|
should "add some randomness" do
|
||||||
random_value = 0.8
|
random_value = 0.8
|
||||||
StripeClient.stubs(:rand).returns(random_value)
|
StripeClient.stubs(:rand).returns(random_value)
|
||||||
Stripe.stubs(:initial_network_retry_delay).returns(1)
|
Stripe.configuration.stubs(:initial_network_retry_delay).returns(1)
|
||||||
Stripe.stubs(:max_network_retry_delay).returns(8)
|
Stripe.configuration.stubs(:max_network_retry_delay).returns(8)
|
||||||
|
|
||||||
base_value = Stripe.initial_network_retry_delay * (0.5 * (1 + random_value))
|
base_value = Stripe.initial_network_retry_delay * (0.5 * (1 + random_value))
|
||||||
|
|
||||||
@ -309,6 +398,23 @@ module Stripe
|
|||||||
assert_equal(base_value * 4, StripeClient.sleep_time(3))
|
assert_equal(base_value * 4, StripeClient.sleep_time(3))
|
||||||
assert_equal(base_value * 8, StripeClient.sleep_time(4))
|
assert_equal(base_value * 8, StripeClient.sleep_time(4))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "permit passing in a configuration object" do
|
||||||
|
StripeClient.stubs(:rand).returns(1)
|
||||||
|
config = Stripe::StripeConfiguration.setup do |c|
|
||||||
|
c.initial_network_retry_delay = 1
|
||||||
|
c.max_network_retry_delay = 2
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set the global configuration to be different than the client
|
||||||
|
Stripe.configuration.stubs(:initial_network_retry_delay).returns(100)
|
||||||
|
Stripe.configuration.stubs(:max_network_retry_delay).returns(200)
|
||||||
|
|
||||||
|
assert_equal(1, StripeClient.sleep_time(1, config: config))
|
||||||
|
assert_equal(2, StripeClient.sleep_time(2, config: config))
|
||||||
|
assert_equal(2, StripeClient.sleep_time(3, config: config))
|
||||||
|
assert_equal(2, StripeClient.sleep_time(4, config: config))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "#execute_request" do
|
context "#execute_request" do
|
||||||
@ -342,6 +448,10 @@ module Stripe
|
|||||||
# switch over to rspec-mocks at some point, we can probably remove
|
# switch over to rspec-mocks at some point, we can probably remove
|
||||||
# this.
|
# this.
|
||||||
Util.stubs(:monotonic_time).returns(0.0)
|
Util.stubs(:monotonic_time).returns(0.0)
|
||||||
|
|
||||||
|
# Stub the Stripe configuration so that mocha matchers will succeed. Currently,
|
||||||
|
# mocha does not support using param matchers within hashes.
|
||||||
|
StripeClient.any_instance.stubs(:config).returns(Stripe.configuration)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "produce appropriate logging" do
|
should "produce appropriate logging" do
|
||||||
@ -353,11 +463,13 @@ module Stripe
|
|||||||
idempotency_key: "abc",
|
idempotency_key: "abc",
|
||||||
method: :post,
|
method: :post,
|
||||||
num_retries: 0,
|
num_retries: 0,
|
||||||
path: "/v1/account")
|
path: "/v1/account",
|
||||||
|
config: Stripe.configuration)
|
||||||
Util.expects(:log_debug).with("Request details",
|
Util.expects(:log_debug).with("Request details",
|
||||||
body: "",
|
body: "",
|
||||||
idempotency_key: "abc",
|
idempotency_key: "abc",
|
||||||
query: nil)
|
query: nil,
|
||||||
|
config: Stripe.configuration)
|
||||||
|
|
||||||
Util.expects(:log_info).with("Response from Stripe API",
|
Util.expects(:log_info).with("Response from Stripe API",
|
||||||
account: "acct_123",
|
account: "acct_123",
|
||||||
@ -367,15 +479,18 @@ module Stripe
|
|||||||
method: :post,
|
method: :post,
|
||||||
path: "/v1/account",
|
path: "/v1/account",
|
||||||
request_id: "req_123",
|
request_id: "req_123",
|
||||||
status: 200)
|
status: 200,
|
||||||
|
config: Stripe.configuration)
|
||||||
Util.expects(:log_debug).with("Response details",
|
Util.expects(:log_debug).with("Response details",
|
||||||
body: body,
|
body: body,
|
||||||
idempotency_key: "abc",
|
idempotency_key: "abc",
|
||||||
request_id: "req_123")
|
request_id: "req_123",
|
||||||
|
config: Stripe.configuration)
|
||||||
Util.expects(:log_debug).with("Dashboard link for request",
|
Util.expects(:log_debug).with("Dashboard link for request",
|
||||||
idempotency_key: "abc",
|
idempotency_key: "abc",
|
||||||
request_id: "req_123",
|
request_id: "req_123",
|
||||||
url: Util.request_id_dashboard_url("req_123", Stripe.api_key))
|
url: Util.request_id_dashboard_url("req_123", Stripe.api_key),
|
||||||
|
config: Stripe.configuration)
|
||||||
|
|
||||||
stub_request(:post, "#{Stripe.api_base}/v1/account")
|
stub_request(:post, "#{Stripe.api_base}/v1/account")
|
||||||
.to_return(
|
.to_return(
|
||||||
@ -404,7 +519,8 @@ module Stripe
|
|||||||
idempotency_key: nil,
|
idempotency_key: nil,
|
||||||
method: :post,
|
method: :post,
|
||||||
num_retries: 0,
|
num_retries: 0,
|
||||||
path: "/v1/account")
|
path: "/v1/account",
|
||||||
|
config: Stripe.configuration)
|
||||||
Util.expects(:log_info).with("Response from Stripe API",
|
Util.expects(:log_info).with("Response from Stripe API",
|
||||||
account: nil,
|
account: nil,
|
||||||
api_version: nil,
|
api_version: nil,
|
||||||
@ -413,7 +529,8 @@ module Stripe
|
|||||||
method: :post,
|
method: :post,
|
||||||
path: "/v1/account",
|
path: "/v1/account",
|
||||||
request_id: nil,
|
request_id: nil,
|
||||||
status: 500)
|
status: 500,
|
||||||
|
config: Stripe.configuration)
|
||||||
|
|
||||||
error = {
|
error = {
|
||||||
code: "code",
|
code: "code",
|
||||||
@ -428,7 +545,8 @@ module Stripe
|
|||||||
error_param: error[:param],
|
error_param: error[:param],
|
||||||
error_type: error[:type],
|
error_type: error[:type],
|
||||||
idempotency_key: nil,
|
idempotency_key: nil,
|
||||||
request_id: nil)
|
request_id: nil,
|
||||||
|
config: Stripe.configuration)
|
||||||
|
|
||||||
stub_request(:post, "#{Stripe.api_base}/v1/account")
|
stub_request(:post, "#{Stripe.api_base}/v1/account")
|
||||||
.to_return(
|
.to_return(
|
||||||
@ -449,7 +567,8 @@ module Stripe
|
|||||||
idempotency_key: nil,
|
idempotency_key: nil,
|
||||||
method: :post,
|
method: :post,
|
||||||
num_retries: 0,
|
num_retries: 0,
|
||||||
path: "/oauth/token")
|
path: "/oauth/token",
|
||||||
|
config: Stripe.configuration)
|
||||||
Util.expects(:log_info).with("Response from Stripe API",
|
Util.expects(:log_info).with("Response from Stripe API",
|
||||||
account: nil,
|
account: nil,
|
||||||
api_version: nil,
|
api_version: nil,
|
||||||
@ -458,14 +577,16 @@ module Stripe
|
|||||||
method: :post,
|
method: :post,
|
||||||
path: "/oauth/token",
|
path: "/oauth/token",
|
||||||
request_id: nil,
|
request_id: nil,
|
||||||
status: 400)
|
status: 400,
|
||||||
|
config: Stripe.configuration)
|
||||||
|
|
||||||
Util.expects(:log_error).with("Stripe OAuth error",
|
Util.expects(:log_error).with("Stripe OAuth error",
|
||||||
status: 400,
|
status: 400,
|
||||||
error_code: "invalid_request",
|
error_code: "invalid_request",
|
||||||
error_description: "No grant type specified",
|
error_description: "No grant type specified",
|
||||||
idempotency_key: nil,
|
idempotency_key: nil,
|
||||||
request_id: nil)
|
request_id: nil,
|
||||||
|
config: Stripe.configuration)
|
||||||
|
|
||||||
stub_request(:post, "#{Stripe.connect_base}/oauth/token")
|
stub_request(:post, "#{Stripe.connect_base}/oauth/token")
|
||||||
.to_return(body: JSON.generate(error: "invalid_request",
|
.to_return(body: JSON.generate(error: "invalid_request",
|
||||||
@ -788,7 +909,7 @@ module Stripe
|
|||||||
|
|
||||||
context "idempotency keys" do
|
context "idempotency keys" do
|
||||||
setup do
|
setup do
|
||||||
Stripe.stubs(:max_network_retries).returns(2)
|
Stripe::StripeConfiguration.any_instance.stubs(:max_network_retries).returns(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "not add an idempotency key to GET requests" do
|
should "not add an idempotency key to GET requests" do
|
||||||
@ -838,7 +959,7 @@ module Stripe
|
|||||||
|
|
||||||
context "retry logic" do
|
context "retry logic" do
|
||||||
setup do
|
setup do
|
||||||
Stripe.stubs(:max_network_retries).returns(2)
|
Stripe::StripeConfiguration.any_instance.stubs(:max_network_retries).returns(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "retry failed requests and raise if error persists" do
|
should "retry failed requests and raise if error persists" do
|
||||||
@ -870,6 +991,21 @@ module Stripe
|
|||||||
client = StripeClient.new
|
client = StripeClient.new
|
||||||
client.execute_request(:post, "/v1/charges")
|
client.execute_request(:post, "/v1/charges")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "pass the client configuration when retrying" do
|
||||||
|
StripeClient.expects(:sleep_time)
|
||||||
|
.with(any_of(1, 2),
|
||||||
|
has_entry(:config, kind_of(Stripe::StripeConfiguration)))
|
||||||
|
.at_least_once.returns(0)
|
||||||
|
|
||||||
|
stub_request(:post, "#{Stripe.api_base}/v1/charges")
|
||||||
|
.to_raise(Errno::ECONNREFUSED.new)
|
||||||
|
|
||||||
|
client = StripeClient.new
|
||||||
|
assert_raises Stripe::APIConnectionError do
|
||||||
|
client.execute_request(:post, "/v1/charges")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "params serialization" do
|
context "params serialization" do
|
||||||
@ -1079,7 +1215,7 @@ module Stripe
|
|||||||
context "#proxy" do
|
context "#proxy" do
|
||||||
should "run the request through the proxy" do
|
should "run the request through the proxy" do
|
||||||
begin
|
begin
|
||||||
StripeClient.current_thread_context.default_connection_manager = nil
|
StripeClient.clear_all_connection_managers
|
||||||
|
|
||||||
Stripe.proxy = "http://user:pass@localhost:8080"
|
Stripe.proxy = "http://user:pass@localhost:8080"
|
||||||
|
|
||||||
@ -1095,7 +1231,7 @@ module Stripe
|
|||||||
ensure
|
ensure
|
||||||
Stripe.proxy = nil
|
Stripe.proxy = nil
|
||||||
|
|
||||||
StripeClient.current_thread_context.default_connection_manager = nil
|
StripeClient.clear_all_connection_managers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -20,6 +20,7 @@ module Stripe
|
|||||||
assert_equal "https://api.stripe.com", config.api_base
|
assert_equal "https://api.stripe.com", config.api_base
|
||||||
assert_equal "https://connect.stripe.com", config.connect_base
|
assert_equal "https://connect.stripe.com", config.connect_base
|
||||||
assert_equal "https://files.stripe.com", config.uploads_base
|
assert_equal "https://files.stripe.com", config.uploads_base
|
||||||
|
assert_equal nil, config.api_version
|
||||||
end
|
end
|
||||||
|
|
||||||
should "allow for overrides when a block is passed" do
|
should "allow for overrides when a block is passed" do
|
||||||
@ -41,7 +42,7 @@ module Stripe
|
|||||||
c.open_timeout = 100
|
c.open_timeout = 100
|
||||||
end
|
end
|
||||||
|
|
||||||
duped_config = config.reverse_duplicate_merge(read_timeout: 500)
|
duped_config = config.reverse_duplicate_merge(read_timeout: 500, api_version: "2018-08-02")
|
||||||
|
|
||||||
assert_equal config.open_timeout, duped_config.open_timeout
|
assert_equal config.open_timeout, duped_config.open_timeout
|
||||||
assert_equal 500, duped_config.read_timeout
|
assert_equal 500, duped_config.read_timeout
|
||||||
@ -57,6 +58,24 @@ module Stripe
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "#max_network_retry_delay=" do
|
||||||
|
should "coerce the option into an integer" do
|
||||||
|
config = Stripe::StripeConfiguration.setup
|
||||||
|
|
||||||
|
config.max_network_retry_delay = "10"
|
||||||
|
assert_equal 10, config.max_network_retry_delay
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#initial_network_retry_delay=" do
|
||||||
|
should "coerce the option into an integer" do
|
||||||
|
config = Stripe::StripeConfiguration.setup
|
||||||
|
|
||||||
|
config.initial_network_retry_delay = "10"
|
||||||
|
assert_equal 10, config.initial_network_retry_delay
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "#log_level=" do
|
context "#log_level=" do
|
||||||
should "be backwards compatible with old values" do
|
should "be backwards compatible with old values" do
|
||||||
config = Stripe::StripeConfiguration.setup
|
config = Stripe::StripeConfiguration.setup
|
||||||
@ -127,5 +146,14 @@ module Stripe
|
|||||||
config.verify_ssl_certs = false
|
config.verify_ssl_certs = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "#key" do
|
||||||
|
should "generate the same key when values are identicial" do
|
||||||
|
assert_equal StripeConfiguration.setup.key, StripeConfiguration.setup.key
|
||||||
|
|
||||||
|
custom_config = StripeConfiguration.setup { |c| c.open_timeout = 1000 }
|
||||||
|
refute_equal StripeConfiguration.setup.key, custom_config.key
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -114,6 +114,11 @@ class StripeTest < Test::Unit::TestCase
|
|||||||
assert_equal "https://other.stripe.com", Stripe.api_base
|
assert_equal "https://other.stripe.com", Stripe.api_base
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "allow api_version to be configured" do
|
||||||
|
Stripe.api_version = "2018-02-28"
|
||||||
|
assert_equal "2018-02-28", Stripe.api_version
|
||||||
|
end
|
||||||
|
|
||||||
should "allow connect_base to be configured" do
|
should "allow connect_base to be configured" do
|
||||||
Stripe.connect_base = "https://other.stripe.com"
|
Stripe.connect_base = "https://other.stripe.com"
|
||||||
assert_equal "https://other.stripe.com", Stripe.connect_base
|
assert_equal "https://other.stripe.com", Stripe.connect_base
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user