Module: Stripe::Util
- Defined in:
- lib/stripe/util.rb
Constant Summary collapse
- LEGAL_FIRST_CHARACTER =
/[a-zA-Z_]/.freeze
- LEGAL_VARIABLE_CHARACTER =
/[a-zA-Z0-9_]/.freeze
Class Method Summary collapse
- .check_api_key!(key) ⇒ Object
- .check_string_argument!(key) ⇒ Object
-
.convert_to_stripe_object(data, opts = {}, api_mode: :v1, requestor: nil, klass: nil) ⇒ Object
Converts a hash of fields or an array of hashes into a
StripeObjector array of StripeObjects. -
.convert_to_stripe_object_with_params(data, params, opts = {}, last_response = nil, api_mode: :v1, requestor: nil, v2_deleted_object: false, klass: nil) ⇒ Object
Converts a hash of fields or an array of hashes into a
StripeObjector array of StripeObjects. -
.custom_method(resource, target, name, http_verb, http_path) ⇒ Object
Adds a custom method to a resource class.
-
.encode_parameters(params, api_mode) ⇒ Object
Encodes a hash of parameters in a way that’s suitable for use as query parameters in a URI or as form parameters in a request body.
- .event_notification_classes ⇒ Object
- .flatten_params(params, api_mode, parent_key = nil) ⇒ Object
- .flatten_params_array(value, api_mode, calculated_key) ⇒ Object
-
.get_api_mode(path) ⇒ Object
Returns either v1 or v2 as api_mode based on the given path.
- .log_debug(message, data = {}) ⇒ Object
- .log_error(message, data = {}) ⇒ Object
- .log_info(message, data = {}) ⇒ Object
-
.monotonic_time ⇒ Object
‘Time.now` can be unstable in cases like an administrator manually updating its value or a reconcilation via NTP.
-
.normalize_headers(headers) ⇒ Object
Normalizes header keys so that they’re all lower case and each hyphen-delimited section starts with a single capitalized letter.
- .normalize_id(id) ⇒ Object
-
.normalize_opts(opts) ⇒ Object
The secondary opts argument can either be a string or hash Turn this value into an api_key and a set of headers.
- .object_classes ⇒ Object
- .object_name_matches_class?(object_name, klass) ⇒ Boolean
- .objects_to_ids(obj, semantics) ⇒ Object
-
.request_id_dashboard_url(request_id, api_key) ⇒ Object
Generates a Dashboard link to inspect a request ID based off of a request ID value and an API key, which is used to attempt to extract whether the environment is livemode or testmode.
-
.secure_compare(str_a, str_b) ⇒ Object
Constant time string comparison to prevent timing attacks Code borrowed from ActiveSupport.
- .symbolize_names(object) ⇒ Object
-
.url_encode(key) ⇒ Object
Encodes a string in a way that makes it suitable for use in a set of query parameters in a URI or in a set of form parameters in a request body.
- .v2_event_classes ⇒ Object
- .v2_object_classes ⇒ Object
-
.valid_variable_name?(key) ⇒ Boolean
Return false for strings that are invalid variable names Does NOT expect there to be a preceding ‘@’ for instance variables.
Class Method Details
.check_api_key!(key) ⇒ Object
349 350 351 352 353 |
# File 'lib/stripe/util.rb', line 349 def self.check_api_key!(key) raise TypeError, "api_key must be a string" unless key.is_a?(String) key end |
.check_string_argument!(key) ⇒ Object
343 344 345 346 347 |
# File 'lib/stripe/util.rb', line 343 def self.check_string_argument!(key) raise TypeError, "argument must be a string" unless key.is_a?(String) key end |
.convert_to_stripe_object(data, opts = {}, api_mode: :v1, requestor: nil, klass: nil) ⇒ Object
Converts a hash of fields or an array of hashes into a StripeObject or array of StripeObjects. These new objects will be created as a concrete type as dictated by their ‘object` field (e.g. an `object` value of `charge` would create an instance of Charge), but if `object` is not present or of an unknown type, the newly created instance will fall back to being a StripeObject.
Attributes
-
data- Hash of fields and values to be converted into a StripeObject. -
params- Params forStripeObjectlike filters used in search that will be reused on subsequent API calls. -
opts- Options forStripeObjectlike an API key that will be reused on subsequent API calls.
112 113 114 |
# File 'lib/stripe/util.rb', line 112 def self.convert_to_stripe_object(data, opts = {}, api_mode: :v1, requestor: nil, klass: nil) convert_to_stripe_object_with_params(data, {}, opts, api_mode: api_mode, requestor: requestor, klass: klass) end |
.convert_to_stripe_object_with_params(data, params, opts = {}, last_response = nil, api_mode: :v1, requestor: nil, v2_deleted_object: false, klass: nil) ⇒ Object
Converts a hash of fields or an array of hashes into a StripeObject or array of StripeObjects. These new objects will be created as a concrete type as dictated by their ‘object` field (e.g. an `object` value of `charge` would create an instance of Charge), but if `object` is not present or of an unknown type, the newly created instance will fall back to being a StripeObject.
Attributes
-
data- Hash of fields and values to be converted into a StripeObject. -
opts- Options forStripeObjectlike an API key that will be reused on subsequent API calls. -
last_response- The raw response associated with the object. -
api_mode- The API mode to use when converting the object, either :v1 or :v2. -
requestor- The requestor to use when constructing the object. -
v2_deleted_object- If true, ignore the object tag for casting purposes -
klass- The class to use for inner types
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/stripe/util.rb', line 133 def self.convert_to_stripe_object_with_params( data, params, opts = {}, last_response = nil, api_mode: :v1, requestor: nil, v2_deleted_object: false, klass: nil ) opts = normalize_opts(opts) case data when Array data.map { |i| convert_to_stripe_object(i, opts, api_mode: api_mode, requestor: requestor, klass: klass) } when Hash # TODO: This is a terrible hack. # Waiting on https://go/j/API_SERVICES-3167 to add an object in v2 lists if api_mode == :v2 && data.include?(:data) && data.include?(:next_page_url) return V2::ListObject.construct_from(data, opts, last_response, api_mode, requestor) end # Try converting to a known object class. If none available, fall back # to generic StripeObject object_type = data[:type] || data["type"] object_name = data[:object] || data["object"] object_class = if klass klass elsif api_mode == :v2 if v2_deleted_object V2::DeletedObject elsif object_name == "v2.core.event" && v2_event_classes.key?(object_type) v2_event_classes.fetch(object_type) else v2_object_classes.fetch( object_name, StripeObject ) end else object_classes.fetch(object_name, StripeObject) end obj = object_class.construct_from(data, opts, last_response, api_mode, requestor) # set filters so that we can fetch the same limit, expansions, and # predicates when accessing the next and previous pages obj.filters = params.dup if obj && (obj.is_a?(SearchResultObject) || obj.is_a?(ListObject)) obj else data end end |
.custom_method(resource, target, name, http_verb, http_path) ⇒ Object
Adds a custom method to a resource class. This is used to add support for non-CRUDL API requests, e.g. capturing charges. custom_method takes the following parameters:
-
name: the name of the custom method to create (as a symbol)
-
http_verb: the HTTP verb for the API request (:get, :post, or :delete)
-
http_path: the path to append to the resource’s URL. If not provided,
the name is used as the path -
resource: the resource implementation class
-
target: the class that custom static method will be added to
For example, this call:
custom_method :capture, http_verb: post
adds a ‘capture` class method to the resource class that, when called, will send a POST request to `/v1/<object_name>/capture`. TODO: are we comfortable with deleting this?
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/stripe/util.rb', line 66 def self.custom_method(resource, target, name, http_verb, http_path) unless %i[get post delete].include?(http_verb) raise ArgumentError, "Invalid http_verb value: #{http_verb.inspect}. Should be one " \ "of :get, :post or :delete." end unless target.respond_to?(:resource_url) raise ArgumentError, "Invalid target value: #{target}. Target class should have a " \ "`resource_url` method." end http_path ||= name.to_s target.define_singleton_method(name) do |id, params = {}, opts = {}| unless id.is_a?(String) raise ArgumentError, "id should be a string representing the ID of an API resource" end url = "#{target.resource_url}/" \ "#{CGI.escape(id)}/" \ "#{CGI.escape(http_path)}" resource.execute_resource_request( http_verb, url, :api, params, opts ) end end |
.encode_parameters(params, api_mode) ⇒ Object
Encodes a hash of parameters in a way that’s suitable for use as query parameters in a URI or as form parameters in a request body. This mainly involves escaping special characters from parameter keys and values (e.g. ‘&`).
241 242 243 244 |
# File 'lib/stripe/util.rb', line 241 def self.encode_parameters(params, api_mode) Util.flatten_params(params, api_mode) .map { |k, v| "#{url_encode(k)}=#{url_encode(v)}" }.join("&") end |
.event_notification_classes ⇒ Object
43 44 45 |
# File 'lib/stripe/util.rb', line 43 def self.event_notification_classes @event_notification_classes ||= Stripe::EventTypes.event_notification_types_to_classes end |
.flatten_params(params, api_mode, parent_key = nil) ⇒ Object
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/stripe/util.rb', line 257 def self.flatten_params(params, api_mode, parent_key = nil) result = [] # do not sort the final output because arrays (and arrays of hashes # especially) can be order sensitive, but do sort incoming parameters params.each do |key, value| calculated_key = parent_key ? "#{parent_key}[#{key}]" : key.to_s if value.is_a?(Hash) result += flatten_params(value, api_mode, calculated_key) elsif value.is_a?(Array) result += flatten_params_array(value, api_mode, calculated_key) else result << [calculated_key, value] end end result end |
.flatten_params_array(value, api_mode, calculated_key) ⇒ Object
276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
# File 'lib/stripe/util.rb', line 276 def self.flatten_params_array(value, api_mode, calculated_key) result = [] value.each_with_index do |elem, i| if elem.is_a?(Hash) result += flatten_params(elem, api_mode, "#{calculated_key}[#{i}]") elsif elem.is_a?(Array) result += flatten_params_array(elem, api_mode, calculated_key) else # Always use indexed format for arrays result << ["#{calculated_key}[#{i}]", elem] end end result end |
.get_api_mode(path) ⇒ Object
Returns either v1 or v2 as api_mode based on the given path
390 391 392 393 394 395 396 |
# File 'lib/stripe/util.rb', line 390 def self.get_api_mode(path) if path.start_with?("/v2/") :v2 else :v1 end end |
.log_debug(message, data = {}) ⇒ Object
207 208 209 210 211 212 213 214 215 |
# File 'lib/stripe/util.rb', line 207 def self.log_debug(, data = {}) config = data.delete(:config) || Stripe.config logger = config.logger || Stripe.logger if !logger.nil? || (!config.log_level.nil? && config.log_level <= Stripe::LEVEL_DEBUG) log_internal(, data, color: :blue, level: Stripe::LEVEL_DEBUG, logger: Stripe.logger, out: $stdout) end end |
.log_error(message, data = {}) ⇒ Object
187 188 189 190 191 192 193 194 195 |
# File 'lib/stripe/util.rb', line 187 def self.log_error(, data = {}) config = data.delete(:config) || Stripe.config logger = config.logger || Stripe.logger if !logger.nil? || (!config.log_level.nil? && config.log_level <= Stripe::LEVEL_ERROR) log_internal(, data, color: :cyan, level: Stripe::LEVEL_ERROR, logger: Stripe.logger, out: $stderr) end end |
.log_info(message, data = {}) ⇒ Object
197 198 199 200 201 202 203 204 205 |
# File 'lib/stripe/util.rb', line 197 def self.log_info(, data = {}) config = data.delete(:config) || Stripe.config logger = config.logger || Stripe.logger if !logger.nil? || (!config.log_level.nil? && config.log_level <= Stripe::LEVEL_INFO) log_internal(, data, color: :cyan, level: Stripe::LEVEL_INFO, logger: Stripe.logger, out: $stdout) end end |
.monotonic_time ⇒ Object
‘Time.now` can be unstable in cases like an administrator manually updating its value or a reconcilation via NTP. For this reason, prefer the use of the system’s monotonic clock especially where comparing times to calculate an elapsed duration.
Shortcut for getting monotonic time, mostly for purposes of line length and test stubbing. Returns time in seconds since the event used for monotonic reference purposes by the platform (e.g. system boot time).
299 300 301 |
# File 'lib/stripe/util.rb', line 299 def self.monotonic_time Process.clock_gettime(Process::CLOCK_MONOTONIC) end |
.normalize_headers(headers) ⇒ Object
Normalizes header keys so that they’re all lower case and each hyphen-delimited section starts with a single capitalized letter. For example, ‘request-id` becomes `Request-Id`. This is useful for extracting certain key values when the user could have set them with a variety of diffent naming schemes.
360 361 362 363 364 365 366 367 |
# File 'lib/stripe/util.rb', line 360 def self.normalize_headers(headers) headers.each_with_object({}) do |(k, v), new_headers| k = k.to_s.tr("_", "-") if k.is_a?(Symbol) k = k.split("-").reject(&:empty?).map(&:capitalize).join("-") new_headers[k] = v end end |
.normalize_id(id) ⇒ Object
303 304 305 306 307 308 309 310 311 |
# File 'lib/stripe/util.rb', line 303 def self.normalize_id(id) if id.is_a?(Hash) # overloaded id params_hash = id.dup id = params_hash.delete(:id) else params_hash = {} end [id, params_hash] end |
.normalize_opts(opts) ⇒ Object
The secondary opts argument can either be a string or hash Turn this value into an api_key and a set of headers
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/stripe/util.rb', line 315 def self.normalize_opts(opts) case opts when String { api_key: opts } when Hash # If the user is using request signing for authentication, # no need to check the api_key per request. if !(opts.key?(:client) && opts.fetch(:client).config.authenticator) && opts.key?(:api_key) check_api_key!(opts.fetch(:api_key)) end # Explicitly use dup here instead of clone to avoid preserving freeze # state on input params. opts.dup else raise TypeError, "normalize_opts expects a string or a hash" end end |
.object_classes ⇒ Object
31 32 33 |
# File 'lib/stripe/util.rb', line 31 def self.object_classes @object_classes ||= Stripe::ObjectTypes.object_names_to_classes end |
.object_name_matches_class?(object_name, klass) ⇒ Boolean
47 48 49 |
# File 'lib/stripe/util.rb', line 47 def self.object_name_matches_class?(object_name, klass) Util.object_classes[object_name] == klass end |
.objects_to_ids(obj, semantics) ⇒ Object
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/stripe/util.rb', line 10 def self.objects_to_ids(obj, semantics) case obj when APIResource obj.id when Hash res = {} obj.each do |k, v| if !v.nil? res[k] = objects_to_ids(v, semantics) elsif semantics == :v2 res[k] = nil end end res when Array obj.map { |v| objects_to_ids(v, semantics) } else obj end end |
.request_id_dashboard_url(request_id, api_key) ⇒ Object
Generates a Dashboard link to inspect a request ID based off of a request ID value and an API key, which is used to attempt to extract whether the environment is livemode or testmode.
372 373 374 375 |
# File 'lib/stripe/util.rb', line 372 def self.request_id_dashboard_url(request_id, api_key) env = !api_key.nil? && api_key.start_with?("sk_live") ? "live" : "test" "https://dashboard.stripe.com/#{env}/logs/#{request_id}" end |
.secure_compare(str_a, str_b) ⇒ Object
Constant time string comparison to prevent timing attacks Code borrowed from ActiveSupport
379 380 381 382 383 384 385 386 387 |
# File 'lib/stripe/util.rb', line 379 def self.secure_compare(str_a, str_b) return false unless str_a.bytesize == str_b.bytesize l = str_a.unpack "C#{str_a.bytesize}" res = 0 str_b.each_byte { |byte| res |= byte ^ l.shift } res.zero? end |
.symbolize_names(object) ⇒ Object
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/stripe/util.rb', line 217 def self.symbolize_names(object) case object when Hash new_hash = {} object.each do |key, value| key = (begin key.to_sym rescue StandardError key end) || key new_hash[key] = symbolize_names(value) end new_hash when Array object.map { |value| symbolize_names(value) } else object end end |
.url_encode(key) ⇒ Object
Encodes a string in a way that makes it suitable for use in a set of query parameters in a URI or in a set of form parameters in a request body.
249 250 251 252 253 254 255 |
# File 'lib/stripe/util.rb', line 249 def self.url_encode(key) CGI.escape(key.to_s). # Don't use strict form encoding by changing the square bracket control # characters back to their literals. This is fine by the server, and # makes these parameter strings easier to read. gsub("%5B", "[").gsub("%5D", "]") end |
.v2_event_classes ⇒ Object
39 40 41 |
# File 'lib/stripe/util.rb', line 39 def self.v2_event_classes @v2_event_classes ||= Stripe::EventTypes.v2_event_types_to_classes end |
.v2_object_classes ⇒ Object
35 36 37 |
# File 'lib/stripe/util.rb', line 35 def self.v2_object_classes @v2_object_classes ||= Stripe::ObjectTypes.v2_object_names_to_classes end |
.valid_variable_name?(key) ⇒ Boolean
Return false for strings that are invalid variable names Does NOT expect there to be a preceding ‘@’ for instance variables
337 338 339 340 341 |
# File 'lib/stripe/util.rb', line 337 def self.valid_variable_name?(key) return false if key.empty? || key[0] !~ LEGAL_FIRST_CHARACTER key[1..].chars.all? { |char| char =~ LEGAL_VARIABLE_CHARACTER } end |