Skip to content

Commit

Permalink
REST resource API rate limit information
Browse files Browse the repository at this point in the history
When using rest resources there was no way to access the rate limit info

This was because we do not allow direct access to the headers

Add relevant rate limit info to rest resource as instance vars
  • Loading branch information
lizkenyon committed Dec 15, 2023
1 parent 342547f commit 715c792
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 0 deletions.
27 changes: 27 additions & 0 deletions lib/shopify_api/clients/http_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ class HttpResponse
sig { returns(T.nilable(String)) }
attr_reader :prev_page_info, :next_page_info

sig { returns(T.nilable(T::Hash[String, Integer])) }
attr_reader :api_call_limit

sig { returns(T.nilable(Float)) }
attr_reader :retry_request_after

sig do
params(
code: Integer,
Expand All @@ -33,6 +39,11 @@ def initialize(code:, headers:, body:)
@prev_page_info = T.let(nil, T.nilable(String))
@next_page_info = T.let(nil, T.nilable(String))
@prev_page_info, @next_page_info = parse_link_header

@api_call_limit = T.let(nil, T.nilable(T::Hash[String, Integer]))
@retry_request_after = T.let(nil, T.nilable(Float))
@api_call_limit = parse_api_call_limit_header
@retry_request_after = parse_retry_header
end

sig { returns(T::Boolean) }
Expand Down Expand Up @@ -61,6 +72,22 @@ def parse_link_header

[page_info["previous"], page_info["next"]]
end

sig { returns(T.nilable(Float)) }
def parse_retry_header
return nil if @headers["retry-after"].nil?

T.must(@headers["retry-after"])[0].to_f
end

sig { returns(T.nilable(T::Hash[String, Integer])) }
def parse_api_call_limit_header
rate_limit_info = headers["x-shopify-shop-api-call-limit"]&.first
return if rate_limit_info.nil?

request_count, bucket_size = rate_limit_info.split("/").map(&:to_i)
{ request_count: request_count, bucket_size: bucket_size }
end
end
end
end
13 changes: 13 additions & 0 deletions lib/shopify_api/rest/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ def base_find(session: nil, ids: {}, params: {})
instance_variable_get(:"@prev_page_info").value = response.prev_page_info
instance_variable_get(:"@next_page_info").value = response.next_page_info

instance_variable_get(:"@retry_request_after").value = response.retry_request_after
instance_variable_get(:"@api_call_limit").value = response.api_call_limit

create_instances_from_response(response: response, session: T.must(session))
end

Expand Down Expand Up @@ -122,6 +125,16 @@ def next_page?
!instance_variable_get(:"@next_page_info").value.nil?
end

sig { returns T.nilable(Float) }
def retry_request_after
instance_variable_get(:"@retry_request_after").value
end

sig { returns T.nilable(T::Hash[String, Integer]) }
def api_call_limit
instance_variable_get(:"@api_call_limit").value
end

sig { params(attribute: Symbol).returns(T::Boolean) }
def has_many?(attribute)
@has_many.include?(attribute)
Expand Down
15 changes: 15 additions & 0 deletions test/clients/base_rest_resource_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,21 @@ def test_pagination
refute(TestHelpers::FakeResource.next_page?)
end

def test_api_limit_headers
body = { fake_resources: [] }.to_json

stub_request(:get, "#{@prefix}/fake_resources.json")
.to_return(body: body, headers: {
"X-Shopify-Shop-Api-Call-Limit" => "40/40",
"Retry-After" => "2.0",
})

TestHelpers::FakeResource.all(session: @session)
assert(TestHelpers::FakeResource.retry_request_after, 2.0)
assert(TestHelpers::FakeResource.api_call_limit[:request_count], 40)
assert(TestHelpers::FakeResource.api_call_limit[:bucket_size], 40)
end

def test_pagination_is_thread_safe
response_body = { fake_resources: [] }.to_json
request_made = false
Expand Down
11 changes: 11 additions & 0 deletions test/clients/http_response_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ def test_next_and_prev
assert_equal("page-info", response.prev_page_info)
assert_equal("other-page-info", response.next_page_info)
end

def test_retry_request_after
response = ShopifyAPI::Clients::HttpResponse.new(code: 200, headers: { "retry-after" => ["2.0"] }, body: "")
assert_equal(2.0, response.retry_request_after)
end

def test_api_call_limit
response = ShopifyAPI::Clients::HttpResponse.new(code: 200, headers: { "x-shopify-shop-api-call-limit" => ["1/40"] }, body: "")
assert_equal(1, response.api_call_limit[:request_count])
assert_equal(40, response.api_call_limit[:bucket_size])
end
end
end
end
2 changes: 2 additions & 0 deletions test/test_helpers/fake_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class FakeResource < ShopifyAPI::Rest::Base

@prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

@read_only_attributes = T.let([:unsaveable_attribute], T::Array[Symbol])

Expand Down
2 changes: 2 additions & 0 deletions test/test_helpers/fake_resource_with_custom_prefix.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class FakeResourceWithCustomPrefix < ShopifyAPI::Rest::Base

@prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

@has_one = T.let({}, T::Hash[Symbol, Class])
@has_many = T.let({}, T::Hash[Symbol, Class])
Expand Down

0 comments on commit 715c792

Please sign in to comment.