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

REST resource API rate limit information #1257

Merged
merged 9 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Note: For changes to the API, see https://shopify.dev/changelog?filter=api

## Unreleased
[#1210](https://github.com/Shopify/shopify-api-ruby/pull/1246) Add context option `response_as_struct` to allow GraphQL API responses to be accessed via dot notation.
[#1257](https://github.com/Shopify/shopify-api-ruby/pull/1257) Add `api_call_limit` and `retry_request_after` to REST resources to expose rate limit information.
[#1257](https://github.com/Shopify/shopify-api-ruby/pull/1257) Added support for the 2024-01 API version.

## 13.3.1

Expand Down
21 changes: 18 additions & 3 deletions docs/usage/rest.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Create an instance of [`ShopifyAPI::Clients::Rest::Admin`](https://github.com/Sh

Examples:
```ruby
# Create a default client with `ShopifyAPI::Context.api_version`
# Create a default client with `ShopifyAPI::Context.api_version`
# and the active session from `ShopifyAPI::Context.active_session`
client = ShopifyAPI::Clients::Rest::Admin.new

Expand All @@ -147,7 +147,7 @@ The `ShopifyAPI::Clients::Rest::Admin` client offers the 4 core request methods:

#### Input Parameters

Each method can take the parameters outlined in the table below.
Each method can take the parameters outlined in the table below.

| Parameter | Type | Required in Methods | Default Value | Notes |
| -------------- | -------------------------------------------------------- | :-----------------: | :-----------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Expand All @@ -161,7 +161,7 @@ Each method can take the parameters outlined in the table below.

#### Output
##### Success
If the request is successful these methods will all return a [`ShopifyAPI::Clients::HttpResponse`](https://github.com/Shopify/shopify-api-ruby/blob/main/lib/shopify_api/clients/http_response.rb) object, which has the following methods:
If the request is successful these methods will all return a [`ShopifyAPI::Clients::HttpResponse`](https://github.com/Shopify/shopify-api-ruby/blob/main/lib/shopify_api/clients/http_response.rb) object, which has the following methods:
| Methods | Type | Notes |
|---------|------|-------|
| `code` |`Integer`| HTTP Response code, e.g. `200`|
Expand Down Expand Up @@ -247,6 +247,21 @@ _For more information on the `products` endpoint, [check out our API reference g

_For more information on the `products` endpoint, [check out our API reference guide](https://shopify.dev/docs/api/admin-rest/latest/resources/product)._

#### Accessing Rate Limit information

The REST resources have `api_call_limit` and `retry_after` can be found on the Resource class. These values correspond to the `X-Shopify-Shop-Api-Call-Limit` and `Retry-After` [headers](https://shopify.dev/docs/api/usage/rate-limits#rest-admin-api-rate-limits) respectively.

```ruby
product = ShopifyAPI::Product.find(session: session, id: 12345)

# X-Shopify-Shop-Api-Call-Limit: 32/40
request_count = ShopifyAPI::Product.api_call_limit[:request_count] # 32
bucket_size = ShopifyAPI::Product.api_call_limit[:bucket_size] # 40

retry_after = ShopifyAPI::Product.retry_request_after

```

### Pagination

This library also supports cursor-based pagination for REST Admin API requests. [Learn more about REST request pagination](https://shopify.dev/docs/api/usage/pagination-rest).
Expand Down
3 changes: 2 additions & 1 deletion lib/shopify_api/admin_versions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module ShopifyAPI
module AdminVersions
SUPPORTED_ADMIN_VERSIONS = T.let([
"unstable",
"2024-01",
"2023-10",
"2023-07",
"2023-04",
Expand All @@ -15,7 +16,7 @@ module AdminVersions
"2022-01",
], T::Array[String])

LATEST_SUPPORTED_ADMIN_VERSION = T.let("2023-10", String)
LATEST_SUPPORTED_ADMIN_VERSION = T.let("2024-01", String)
end

SUPPORTED_ADMIN_VERSIONS = ShopifyAPI::AdminVersions::SUPPORTED_ADMIN_VERSIONS
Expand Down
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
10 changes: 7 additions & 3 deletions lib/shopify_api/rest/resources/2022_04/abandoned_checkout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ class AbandonedCheckout < 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)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session)).void }
def initialize(session: ShopifyAPI::Context.active_session)
super(session: session)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)

@abandoned_checkout_url = T.let(nil, T.nilable(String))
@billing_address = T.let(nil, T.nilable(T::Hash[T.untyped, T.untyped]))
Expand Down Expand Up @@ -55,6 +57,8 @@ def initialize(session: ShopifyAPI::Context.active_session)
@total_weight = T.let(nil, T.nilable(Integer))
@updated_at = T.let(nil, T.nilable(String))
@user_id = T.let(nil, T.nilable(Integer))

super(session: session, from_hash: from_hash)
end

@has_one = T.let({
Expand Down
10 changes: 7 additions & 3 deletions lib/shopify_api/rest/resources/2022_04/access_scope.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ class AccessScope < 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)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session)).void }
def initialize(session: ShopifyAPI::Context.active_session)
super(session: session)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)

@handle = T.let(nil, T.nilable(String))
@access_scopes = T.let(nil, T.nilable(T::Array[T.untyped]))

super(session: session, from_hash: from_hash)
end

@has_one = T.let({}, T::Hash[Symbol, Class])
Expand Down
10 changes: 7 additions & 3 deletions lib/shopify_api/rest/resources/2022_04/android_pay_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ class AndroidPayKey < 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)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session)).void }
def initialize(session: ShopifyAPI::Context.active_session)
super(session: session)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)

@id = T.let(nil, T.nilable(Integer))
@public_key = T.let(nil, T.nilable(String))

super(session: session, from_hash: from_hash)
end

@has_one = T.let({}, T::Hash[Symbol, Class])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ class ApplePayCertificate < 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)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session)).void }
def initialize(session: ShopifyAPI::Context.active_session)
super(session: session)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)

@id = T.let(nil, T.nilable(Integer))
@merchant_id = T.let(nil, T.nilable(String))
@status = T.let(nil, T.nilable(String))

super(session: session, from_hash: from_hash)
end

@has_one = T.let({}, T::Hash[Symbol, Class])
Expand Down
10 changes: 7 additions & 3 deletions lib/shopify_api/rest/resources/2022_04/application_charge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ class ApplicationCharge < 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)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session)).void }
def initialize(session: ShopifyAPI::Context.active_session)
super(session: session)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)

@confirmation_url = T.let(nil, T.nilable(String))
@created_at = T.let(nil, T.nilable(String))
Expand All @@ -26,6 +28,8 @@ def initialize(session: ShopifyAPI::Context.active_session)
@status = T.let(nil, T.nilable(String))
@test = T.let(nil, T.nilable(T::Boolean))
@updated_at = T.let(nil, T.nilable(String))

super(session: session, from_hash: from_hash)
end

@has_one = T.let({
Expand Down
10 changes: 7 additions & 3 deletions lib/shopify_api/rest/resources/2022_04/application_credit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@ class ApplicationCredit < 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)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session)).void }
def initialize(session: ShopifyAPI::Context.active_session)
super(session: session)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)

@amount = T.let(nil, T.nilable(String))
@currency = T.let(nil, T.nilable(Currency))
@description = T.let(nil, T.nilable(String))
@id = T.let(nil, T.nilable(Integer))
@test = T.let(nil, T.nilable(T::Boolean))

super(session: session, from_hash: from_hash)
end

@has_one = T.let({
Expand Down
10 changes: 7 additions & 3 deletions lib/shopify_api/rest/resources/2022_04/article.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ class Article < 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)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session)).void }
def initialize(session: ShopifyAPI::Context.active_session)
super(session: session)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)

@author = T.let(nil, T.nilable(String))
@blog_id = T.let(nil, T.nilable(Integer))
Expand All @@ -32,6 +34,8 @@ def initialize(session: ShopifyAPI::Context.active_session)
@title = T.let(nil, T.nilable(String))
@updated_at = T.let(nil, T.nilable(String))
@user_id = T.let(nil, T.nilable(Integer))

super(session: session, from_hash: from_hash)
end

@has_one = T.let({}, T::Hash[Symbol, Class])
Expand Down
10 changes: 7 additions & 3 deletions lib/shopify_api/rest/resources/2022_04/asset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ class Asset < 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)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session)).void }
def initialize(session: ShopifyAPI::Context.active_session)
super(session: session)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)

@attachment = T.let(nil, T.nilable(String))
@checksum = T.let(nil, T.nilable(String))
Expand All @@ -26,6 +28,8 @@ def initialize(session: ShopifyAPI::Context.active_session)
@theme_id = T.let(nil, T.nilable(Integer))
@updated_at = T.let(nil, T.nilable(String))
@value = T.let(nil, T.nilable(String))

super(session: session, from_hash: from_hash)
end

@has_one = T.let({}, T::Hash[Symbol, Class])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ class AssignedFulfillmentOrder < 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)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session)).void }
def initialize(session: ShopifyAPI::Context.active_session)
super(session: session)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)

@assigned_location_id = T.let(nil, T.nilable(Integer))
@destination = T.let(nil, T.nilable(T::Hash[T.untyped, T.untyped]))
Expand All @@ -24,6 +26,8 @@ def initialize(session: ShopifyAPI::Context.active_session)
@request_status = T.let(nil, T.nilable(String))
@shop_id = T.let(nil, T.nilable(Integer))
@status = T.let(nil, T.nilable(String))

super(session: session, from_hash: from_hash)
end

@has_one = T.let({}, T::Hash[Symbol, Class])
Expand Down Expand Up @@ -51,10 +55,12 @@ def initialize(session: ShopifyAPI::Context.active_session)

class << self
sig do
returns(String)
returns(T::Array[String])
end
def json_response_body_name()
"fulfillment_order"
def json_response_body_names()
[
"fulfillment_order"
]
end

sig do
Expand Down
9 changes: 6 additions & 3 deletions lib/shopify_api/rest/resources/2022_04/balance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ class Balance < 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)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session)).void }
def initialize(session: ShopifyAPI::Context.active_session)
super(session: session)
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)

sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)

super(session: session, from_hash: from_hash)
end

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