Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make request ID and response information available on all API calls #492

Closed
wants to merge 6 commits into from

Conversation

brandur
Copy link
Contributor

@brandur brandur commented Jan 13, 2017

It's currently possible to access a Stripe request ID value from a StripeError object, but it's also quite useful to be able to access one from any API call for logging and other purposes. This patch adds a response access to APIResource, ListObject, and StripeError which includes response information including the request ID.

So for any API call it's now possible to do this:

charge = Charge.create(...)
puts "request ID = #{charge.response.request_id}"

charges = Charge.list
puts "request ID = #{charges.response.request_id}"

Or on error, the recommended approach is to now access #request_id in the same way:

begin
  charge = Charge.create(...)
rescue Stripe::StripeError => e
  puts "request ID = #{e.response.request_id}"
end

Only top level resources are given a response value, so we get a nil back for this operation:

charge = Charge.create(...)
charge.customer.response.request_id # nil

Fixes #484.

r? @dpetrovics-stripe This one is pretty big, but would you mind reviewing? We could also kick it to someone else on the team if you're pretty swamped right now. Thanks!

def self.card_error(error, resp, error_obj)
CardError.new(error[:message], error[:param], error[:code],
resp.code, resp.body, error_obj, resp.headers)
end
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These helper methods to create errors are a layer of obfuscation and don't add much, so I removed most of them (general_api_error survived because it's called more than once).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, +1

@@ -12,7 +12,7 @@ def request(method, url, params={}, opts={})
api_base = headers.delete(:api_base)
# Assume all remaining opts must be headers

response, opts[:api_key] = Stripe.request(method, url, api_key, params, headers, api_base)
resp, opts[:api_key] = Stripe.request(method, url, api_key, params, headers, api_base)
Copy link
Contributor Author

@brandur brandur Jan 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are nitpicky changes, but I tried to call all local variables containing a StripeResponse "resp" for global consistency.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

attr_reader :http_body
attr_reader :http_headers
attr_reader :http_status
attr_reader :json_body # equivalent to #data
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking it might be a good idea to deprecate the use of these fields and make them pass throughs to response in the meantime, but I deferred that for the time being.

#
# Note: We should try to move away from the very heavy constructors ordered
# parameters to each just setting accessor values directly or optional
# arguments.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to change this constructor to just take two arguments: (message, resp) for cleanliness, but I figure that people might be instantiating errors in their test suites (even though they're not technically part of the public API), so I figured I'd wait until the next major version increment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that makes sense to me. I would change the signature of this constructor at the same time as removing the duplicated fields above (and just use :response)

case resp
# * +response+ - An object containing information about the API response
# that produced the data which is hydrating the StripeObject.
def self.convert_to_stripe_object(data, opts, other_opts = {})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, this should be an optional argument (just have response instead of other_opts), but it breaks the 1.9 build. I wonder if we should just rip off that band-aid.

@@ -2,6 +2,13 @@ module Stripe
class APIResource < StripeObject
include Stripe::APIOperations::Request

# Response contains a structure that has some basic information about the

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a structure can you say StripeResponse?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! Done.

# StripeResponse encapsulates some vitals of a response that came back from
# the Stripe API.
class StripeResponse
# The data contained by the HTTP body of the response deserialized from

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the great comments on all these!

subscription
end

def delete_discount
_, opts = request(:delete, discount_url)
self.response, opts = request(:delete, discount_url)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this looks to be unused so you could just leave as _.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this one is a little confusing, but we're actually setting self.response which is an instance-level variable. So after you've deleted the discount object, you can still get the latest response information on the resource.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, duh!

@@ -3,12 +3,24 @@ module Stripe
# errors derive.
class StripeError < StandardError
attr_reader :message
attr_reader :http_status

# Response contains a structure that has some basic information about the

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might reference StripeResponse here again by name just for clarity

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. Done.

@dpetrovics-stripe
Copy link

Thanks for this, @brandur! The change seems pretty clean to me-- it makes sense to have this available whenever a request is made. Just some really minor comments, otherwise:

LGTM

@brandur
Copy link
Contributor Author

brandur commented Jan 17, 2017

Thanks for this, @brandur! The change seems pretty clean to me-- it makes sense to have this available whenever a request is made. Just some really minor comments, otherwise:

Thanks! And thank you for taking a look at this stuff too.

@brandur
Copy link
Contributor Author

brandur commented Jan 17, 2017

Wow, after thinking about it for the weekend, I think we can do better than this interface, and possibly also address #313 at the same time. I'm going to build one more prototype before pulling this in.

@brandur
Copy link
Contributor Author

brandur commented Jan 20, 2017

Closing in favor of #498. Most of the commits here are there as well.

@brandur brandur closed this Jan 20, 2017
@brandur brandur deleted the brandur-accessible-request-id branch January 20, 2017 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants