From 06dbb22ce13e890a00b86f4d8c209f64e7e971db Mon Sep 17 00:00:00 2001 From: David Clavijo Date: Mon, 3 Nov 2025 18:30:35 +0100 Subject: [PATCH] Injects rate_limit_details on RateLimitExceeded exception - Why? This would allow a user of the API to rescue for example in an ApplicationJob and retry when the time has passed, rather than sleeping. ``` def perform req.execute(uri, token: 'test-token') rescue Intercom::RateLimitExceeded => e self.class.perform_at(e.rate_limit_details[:reset_at]) end ``` - How? * Adds attr_accessor to RateLimitExceeded on errors.rb * Intercom::Request#execute rescue block injects the @rate_limit_details before raising the exception --- lib/intercom/errors.rb | 9 ++++++++- lib/intercom/request.rb | 1 + spec/unit/intercom/request_spec.rb | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/intercom/errors.rb b/lib/intercom/errors.rb index b3af0bd..d7056b6 100644 --- a/lib/intercom/errors.rb +++ b/lib/intercom/errors.rb @@ -64,7 +64,14 @@ class ResourceNotUniqueError < IntercomError; end class BadRequestError < IntercomError; end # Raised when you have exceeded the API rate limit - class RateLimitExceeded < IntercomError; end + class RateLimitExceeded < IntercomError + attr_accessor :rate_limit_details + + def initialize(message, context={}) + super + @rate_limit_details = context[:rate_limit_details] + end + end # Raised when some attribute of the response cannot be handled class UnexpectedResponseError < IntercomError; end diff --git a/lib/intercom/request.rb b/lib/intercom/request.rb index 4037993..8a35e99 100644 --- a/lib/intercom/request.rb +++ b/lib/intercom/request.rb @@ -80,6 +80,7 @@ def execute(target_base_url = nil, token:, read_timeout: 90, open_timeout: 30, a retry end else + e.rate_limit_details = @rate_limit_details raise e end rescue Timeout::Error diff --git a/spec/unit/intercom/request_spec.rb b/spec/unit/intercom/request_spec.rb index 6ec61cb..dbebb5c 100644 --- a/spec/unit/intercom/request_spec.rb +++ b/spec/unit/intercom/request_spec.rb @@ -118,6 +118,22 @@ def execute! expect { execute! }.must_raise(Intercom::GatewayTimeoutError) end + + describe 'when raises RateLimitExceeded' do + it 'sets on the exception the rate limit details from the response headers' do + stub_request(:any, uri).to_return( + status: [429, "Too Many Requests"], + headers: { + 'X-RateLimit-Limit' => '5000', + 'X-RateLimit-Remaining' => '0', + 'X-RateLimit-Reset' => Time.parse("February 25 2010").utc.to_i.to_s, + }, + ) + + error = expect { execute! }.must_raise(Intercom::RateLimitExceeded) + assert_equal({ limit: 5000, remaining: 0, reset_at: Time.parse("February 25 2010").utc}, error.rate_limit_details) + end + end end describe "application error handling" do