Skip to content

christocracy/shopify-api-limits

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

shopify-api-limits

My friend Dave (aka “hunkybill”) posted a problem to me one day about ShopifyAPI call limits, offering a case of beer if I could find a solution: forums.shopify.com/categories/9/posts/49003

So in the HTTP headers, the ShopifyAPI will return to you in each API request how many calls you’ve made, as well as the maximum number of calls available.

Problem

ActiveResource does not make it easy to read the HTTP response headers, since the method #request in ActiveResource::Connection does not save a reference to the HTTP response:

# Makes a request to the remote service.
def request(method, path, *arguments)
  result = ActiveSupport::Notifications.instrument("request.active_resource") do |payload|
    payload[:method]      = method
    payload[:request_uri] = "#{site.scheme}://#{site.host}:#{site.port}#{path}"
    payload[:result]      = http.send(method, path, *arguments)
  end
  handle_response(result) # <-- right here:  handle_response returns an instance of HTTPResponse but doesn't save a ref to it!
rescue Timeout::Error => e
  raise TimeoutError.new(e.message)
rescue OpenSSL::SSL::SSLError => e
  raise SSLError.new(e.message)
end

Solution

Hack ActiveResource::Connection to introduce a new attr_reader :response and capture the returned instance of HTTPResponse provided by net/http from the #handle_response method.

module ActiveResource
  class Connection
    # HACK:  Add an attr_reader for response
    attr_reader :response

    # capture the original #handle_response as an unbound method instead of using alias
    handle_response = self.instance_method(:handle_response)

    # re-implement #handle_response to capture the returned HTTPResponse to an instance var.
    define_method(:handle_response) do |response| 
      @response = handle_response.bind(self).call(response)
    end 
  end
end

Now it’s possible to access the HTTPResponse instance directly from ActiveResource, via:

foo = ActiveResource::Base.connection.response['http-header-param-foo']

Installation

gem "shopify_api"
gem "shopify-api-limits"

Usage

count_shop = ShopifyAPI.credit_used :shop
limit_shop = ShopifyAPI.credit_limit :shop

count_global = ShopifyAPI.credit_used :global
limit_global = ShopifyAPI.credit_limit :global

Generally, you shouldn’t need to use the methods above directly – rather, they’re used under-the-hood by the following helpful methods which don’t require a scope (:shop/:global): If the :global scope has fewer calls available than the :local scope, the methods will operate upon the :global scope; otherwise, values will be returned based upon the :shop scope.

unless ShopifyAPI.credit_maxed?
  #make a ShopifyAPI call
end

until ShopifyAPI.credit_maxed? || stop_condition
  # make some ShopifyAPI calls
end

while ShopifyAPI.credit_left || stop_condition
  # make some ShopifyAPI calls
end

A special bonus for retrieving large recordsets > 250 records

Shopify places a hard limit of 250 on the number of records returned in a single request. There are ways around this, including one listed here bit.ly/kgwCRc which involves manually calculating the total & number of pages then iterating to make several API calls. This gem encapsulates this behaviour allowing you to make what feels like a single call to the ShopifyAPI. Simply set :params => {:limit => false}. False as in, “no limit”

For example, imagine a store which has 251 orders and you want to fetch them all. Since the maximum number of records returned in a single request is 250, 2 requests must be made to the API in order to fetch all 251 records.

records = ShopifyAPI::Order.all(:params => {:limit => false})
puts records.count
=> 251

Without {:limit => false}, the normal behaviour will operate (ie: just one request with 250 records returned):

records = ShopifyAPI::Order.all(:params => {:limit => 250})
puts records.count
=> 250

If you don’t have enough API credits to perform the multiple requests to serve your desired recordset, a ShopifyAPI::Limits::Error will be raised:

puts ShopifyAPI::Order.count
=> 251

puts ShopifyAPI.credit_left
=> 1

begin
  rs = ShopifyAPI::Order.all(:params => {:limit => false})
rescue ShopifyAPI::Limits::Error
  puts "Uhoh...didn't have enough credits to do that.  Maybe you wanna' queue your task for a later date."
end

About

A simple Gem for reading shopify API call limits

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages