Skip to content

Commit

Permalink
Merge pull request #1257 from Shopify/liz/rest-resource-rate-limit
Browse files Browse the repository at this point in the history
REST resource API rate limit information
  • Loading branch information
lizkenyon authored Jan 9, 2024
2 parents 342547f + 7242b26 commit 6ef42c0
Show file tree
Hide file tree
Showing 1,065 changed files with 44,700 additions and 5,713 deletions.
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
10 changes: 7 additions & 3 deletions lib/shopify_api/rest/resources/2022_04/apple_pay_certificate.rb
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

0 comments on commit 6ef42c0

Please sign in to comment.