lib/stripe/util.rb in stripe-12.7.0.pre.beta.2 vs lib/stripe/util.rb in stripe-13.0.0

- old
+ new

@@ -2,31 +2,10 @@ require "cgi" module Stripe module Util - # Options that a user is allowed to specify. - OPTS_USER_SPECIFIED = Set[ - :api_key, - :authenticator, - :idempotency_key, - :stripe_account, - :stripe_version - ].freeze - - # Options that should be copyable from one StripeObject to another - # including options that may be internal. - OPTS_COPYABLE = ( - OPTS_USER_SPECIFIED + Set[:api_base] - ).freeze - - # Options that should be persisted between API requests. This includes - # client, which is an object containing an HTTP client to reuse. - OPTS_PERSISTABLE = ( - OPTS_USER_SPECIFIED + Set[:client] - Set[:idempotency_key] - ).freeze - def self.objects_to_ids(obj) case obj when APIResource obj.id when Hash @@ -42,10 +21,18 @@ def self.object_classes @object_classes ||= Stripe::ObjectTypes.object_names_to_classes end + def self.v2_object_classes + @v2_object_classes ||= Stripe::ObjectTypes.v2_object_names_to_classes + end + + def self.thin_event_classes + @thin_event_classes ||= Stripe::EventTypes.thin_event_names_to_classes + end + def self.object_name_matches_class?(object_name, klass) Util.object_classes[object_name] == klass end # Adds a custom method to a resource class. This is used to add support for @@ -60,10 +47,11 @@ # # 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? 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." @@ -82,18 +70,17 @@ url = "#{target.resource_url}/" \ "#{CGI.escape(id)}/" \ "#{CGI.escape(http_path)}" - resp, opts = resource.execute_resource_request( + resource.execute_resource_request( http_verb, url, + :api, params, opts ) - - Util.convert_to_stripe_object_with_params(resp.data, params, opts, resp) end end # Converts a hash of fields or an array of hashes into a +StripeObject+ or # array of +StripeObject+s. These new objects will be created as a concrete @@ -107,12 +94,12 @@ # * +data+ - Hash of fields and values to be converted into a StripeObject. # * +params+ - Params for +StripeObject+ like filters used in search that # will be reused on subsequent API calls. # * +opts+ - Options for +StripeObject+ like an API key that will be reused # on subsequent API calls. - def self.convert_to_stripe_object(data, opts = {}) - convert_to_stripe_object_with_params(data, {}, opts) + def self.convert_to_stripe_object(data, opts = {}, api_mode: :v1, requestor: nil) + convert_to_stripe_object_with_params(data, {}, opts, api_mode: api_mode, requestor: requestor) end # Converts a hash of fields or an array of hashes into a +StripeObject+ or # array of +StripeObject+s. These new objects will be created as a concrete # type as dictated by their `object` field (e.g. an `object` value of @@ -123,23 +110,52 @@ # ==== Attributes # # * +data+ - Hash of fields and values to be converted into a StripeObject. # * +opts+ - Options for +StripeObject+ like an API key that will be reused # on subsequent API calls. - def self.convert_to_stripe_object_with_params(data, params, opts = {}, last_response = nil) + # * +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. + def self.convert_to_stripe_object_with_params( + data, + params, + opts = {}, + last_response = nil, + api_mode: :v1, + requestor: nil + ) opts = normalize_opts(opts) case data when Array - data.map { |i| convert_to_stripe_object(i, opts) } + data.map { |i| convert_to_stripe_object(i, opts, api_mode: api_mode) } when Hash + # TODO: This is a terrible hack. + # Waiting on https://jira.corp.stripe.com/browse/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"] - obj = object_classes.fetch(object_name, StripeObject) - .construct_from(data, opts, last_response) + object_class = if api_mode == :v2 + if object_name == "v2.core.event" && thin_event_classes.key?(object_type) + thin_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 @@ -200,12 +216,12 @@ # 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. # `&`). - def self.encode_parameters(params) - Util.flatten_params(params) + def self.encode_parameters(params, api_mode) + Util.flatten_params(params, api_mode) .map { |k, v| "#{url_encode(k)}=#{url_encode(v)}" }.join("&") end # 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 @@ -216,38 +232,42 @@ # characters back to their literals. This is fine by the server, and # makes these parameter strings easier to read. gsub("%5B", "[").gsub("%5D", "]") end - def self.flatten_params(params, parent_key = nil) + 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, calculated_key) + result += flatten_params(value, api_mode, calculated_key) elsif value.is_a?(Array) - result += flatten_params_array(value, calculated_key) + result += flatten_params_array(value, api_mode, calculated_key) else result << [calculated_key, value] end end result end - def self.flatten_params_array(value, calculated_key) + 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, "#{calculated_key}[#{i}]") + result += flatten_params(elem, api_mode, "#{calculated_key}[#{i}]") elsif elem.is_a?(Array) - result += flatten_params_array(elem, calculated_key) + result += flatten_params_array(elem, calculated_key, api_mode) else - result << ["#{calculated_key}[#{i}]", elem] + result << if api_mode == :v2 + [calculated_key, elem] + else + ["#{calculated_key}[#{i}]", elem] + end end end result end @@ -278,17 +298,11 @@ 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 + check_api_key!(opts.fetch(:api_key)) if opts.key?(:api_key) # 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" @@ -337,9 +351,18 @@ l = str_a.unpack "C#{str_a.bytesize}" res = 0 str_b.each_byte { |byte| res |= byte ^ l.shift } res.zero? + end + + # Returns either v1 or v2 as api_mode based on the given path + def self.get_api_mode(path) + if path.start_with?("/v2/") + :v2 + else + :v1 + end end # # private #