Skip to content

Commit

Permalink
Handle rate limiting by sleeping required amount
Browse files Browse the repository at this point in the history
  • Loading branch information
kddnewton committed Jun 26, 2024
1 parent 10cf9d7 commit c01327f
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 23 deletions.
41 changes: 18 additions & 23 deletions lib/smart_todo/slack_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ def initialize(slack_token)
#
# @see https://api.slack.com/methods/users.lookupByEmail
def lookup_user_by_email(email)
headers = { "Content-Type" => "application/x-www-form-urlencoded" }

request(:get, "/api/users.lookupByEmail?email=#{CGI.escape(email)}", nil, headers)
headers = default_headers.merge("Content-Type" => "application/x-www-form-urlencoded")
request = Net::HTTP::Get.new("/api/users.lookupByEmail?email=#{CGI.escape(email)}", headers)
dispatch(request)
end

# Send a message to a Slack channel or to a user
Expand All @@ -55,7 +55,10 @@ def lookup_user_by_email(email)
#
# @see https://api.slack.com/methods/chat.postMessage
def post_message(channel, text)
request(:post, "/api/chat.postMessage", JSON.dump(channel: channel, text: text))
headers = default_headers
request = Net::HTTP::Post.new("/api/chat.postMessage", headers)
request.body = JSON.dump(channel: channel, text: text)
dispatch(request)
end

private
Expand All @@ -67,27 +70,19 @@ def post_message(channel, text)
#
# @raise [Net::HTTPError] in case the request to Slack failed
# @raise [SlackClient::Error] in case Slack returns a { ok: false } in the body
def request(method, endpoint, data = nil, headers = {})
response = case method
when :post, :patch
@client.public_send(method, endpoint, data, default_headers.merge(headers))
else
@client.public_send(method, endpoint, default_headers.merge(headers))
end
def dispatch(request, max_attempts = 5)
response = @client.request(request)
attempts = 1

slack_response!(response)
end
while response.is_a?(Net::HTTPTooManyRequests) && attempts < max_attempts
sleep([Integer(response["Retry-After"]), 600].min)
response = @client.request(request)
attempts += 1
end

# Check if the response to Slack was a 200 and the Slack API request was successful
#
# @param response [Net::HTTPResponse] a net Net::HTTPResponse subclass
# (Net::HTTPOK, Net::HTTPNotFound ...)
# @return [Hash]
#
# @raise [Net::HTTPError] in case the request to Slack failed
# @raise [SlackClient::Error] in case Slack returns a { ok: false } in the body
def slack_response!(response)
raise(Net::HTTPError.new("Request to slack failed", response)) unless response.code_type < Net::HTTPSuccess
unless response.code_type < Net::HTTPSuccess
raise(Net::HTTPError.new("Request to slack failed", response))
end

body = JSON.parse(response.body)

Expand Down
24 changes: 24 additions & 0 deletions test/smart_todo/slack_client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,29 @@ def test_succeeds_when_reponse_is_a_201

@client.lookup_user_by_email("[email protected]")
end

def test_handles_sleeping
counter = 0

stub_request(:get, /slack.com/).to_return do
counter += 1
if counter == 1
{ status: 429, headers: { "Retry-After" => "1" } }
else
{ status: 201, body: JSON.dump(ok: true) }
end
end

@client.lookup_user_by_email("[email protected]")
end

def test_fails_sleeping
stub_request(:get, /slack.com/)
.to_return(status: 429, headers: { "Retry-After" => "0" })

assert_raises(Net::HTTPError) do
@client.lookup_user_by_email("[email protected]")
end
end
end
end

0 comments on commit c01327f

Please sign in to comment.