Class: ShopifyAPI::GraphQL::Tiny

Inherits:
Object
  • Object
show all
Defined in:
lib/shopify_api/graphql/tiny.rb,
lib/shopify_api/graphql/tiny/version.rb

Overview

Lightweight, no-nonsense, Shopify GraphQL Admin API client with built-in pagination and retry

Defined Under Namespace

Classes: GraphQLError, HTTPError

Constant Summary collapse

Error =
Class.new(StandardError)
ConnectionError =
Class.new(Error)
ERROR_CODE_THROTTLED =
"THROTTLED"
ERROR_CODE_TIMEOUT =
"TIMEOUT"
ERROR_CODE_SERVER_ERROR =
"INTERNAL_SERVER_ERROR"
RateLimitError =
Class.new(GraphQLError)
USER_AGENT =
"ShopifyAPI::GraphQL::Tiny v#{VERSION} (Ruby v#{RUBY_VERSION})"
SHOPIFY_DOMAIN =
".myshopify.com"
ACCESS_TOKEN_HEADER =
"X-Shopify-Access-Token"
QUERY_COST_HEADER =
"X-GraphQL-Cost-Include-Fields"
DEFAULT_HEADERS =
{ "Content-Type" => "application/json" }.freeze
DEFAULT_BACKOFF_OPTIONS =
{
  :base_delay => 0.5,
  :jitter => true,
  :max_attempts => 10,
  :max_delay => 60,
  :multiplier => 2.0
}
DEFAULT_RETRY_ERRORS =
[
  "5XX",
  ERROR_CODE_SERVER_ERROR,
  ERROR_CODE_TIMEOUT,
  *NetHttpTimeoutErrors.all
]
ENDPOINT =

Note that we omit the "/" after API for the case where there's no version.

"https://%s/admin/api%s/graphql.json"
VERSION =
"1.0.1"

Instance Method Summary collapse

Constructor Details

#initialize(shop, token, options = nil) ⇒ Tiny

Create a new GraphQL client

Arguments

[shop (String)] Shopify domain to make requests against [token (String)] Shopify Admin API GraphQL token [options (Hash)] Client options. Optional.

Options

[:retry (Boolean|Array)] If false disable retries or an Array of errors to retry. Can be HTTP status codes, GraphQL errors, or exception classes. [:version (String)] Shopify API version to use. Defaults to the latest version. [:max_attempts (Integer)] Maximum number of retry attempts across all errors. Defaults to 10 [:base_delay (Float)] Exponential backoff base delay. Defaults to 0.5 [:jitter (Boolean)] Exponential backoff jitter (random delay added to backoff). Defaults to true [:multiplier (Float)] Exponential backoff multiplier. Defaults to 2.0 [:debug (Boolean|IO)] Output the HTTP request/response to STDERR or to its value if it's an IO. Defaults to false.

Errors

ArgumentError if no shop or token is provided.

Raises:

  • (ArgumentError)


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/shopify_api/graphql/tiny.rb', line 93

def initialize(shop, token, options = nil)
  raise ArgumentError, "shop required" unless shop
  raise ArgumentError, "token required" unless token

  @domain = shopify_domain(shop)
  @options = options || {}

  @headers = DEFAULT_HEADERS.dup
  @headers[ACCESS_TOKEN_HEADER] = token
  @headers[QUERY_COST_HEADER] = "true" unless @options[:retry] == false

  @endpoint = URI(sprintf(ENDPOINT, @domain, !@options[:version].to_s.strip.empty? ? "/#{@options[:version]}" : ""))
  @backoff_options = DEFAULT_BACKOFF_OPTIONS.merge(@options.slice(*DEFAULT_BACKOFF_OPTIONS.keys))

  if @options[:debug]
    @debug = @options[:debug].is_a?(IO) ? @options[:debug] : $stderr
  end

  case @options[:retry]
  when false
    @retryable = []
  when Array
    @retryable = @options[:retry]
  else
    @retryable = DEFAULT_RETRY_ERRORS
  end
end

Instance Method Details

#execute(q, variables = nil) ⇒ Object

Execute a GraphQL query or mutation

Arguments

[q (String)] Query or mutation to execute [variables (Hash)] Optional. Variable to pass to the query or mutation given by q

Errors

ArgumentError, ConnectionError, HTTPError, RateLimitError, GraphQLError

Outside of ArgumentError these are raised after exhausing the configured retry.

  • An ShopifyAPI::GraphQL::Tiny::HTTPError is raised of the response does not have 200 status code
  • A ShopifyAPI::GraphQL::Tiny::RateLimitError is raised if rate-limited and retries are disabled or if still rate-limited after the configured number of retry attempts
  • A ShopifyAPI::GraphQL::Tiny::GraphQLError is raised if the response contains an errors property that is not a rate-limit error

Returns

[Hash] The GraphQL response. Unmodified.

Raises:

  • (ArgumentError)


145
146
147
148
149
150
151
# File 'lib/shopify_api/graphql/tiny.rb', line 145

def execute(q, variables = nil)
  raise ArgumentError, "query required" if q.nil? || q.to_s.strip.empty?

  @request_attempts = 0

  make_request(q, variables)
end

#paginate(*options) ⇒ Object

Create a pager to execute a paginated query:

pager = gql.paginate # This is the same as gql.paginate(:after) pager.execute(query, :id => id) do |page| page.dig("data", "product", "title") end

The block is called for each page.

Using pagination requires you to include the PageInfo object in your queries and wrap them in a function that accepts a page/cursor argument. See the README for more information.

Arguments

[direction (Symbol)] The direction to paginate, either :after or :before. Optional, defaults to :after: [options (Hash)] Pagination options. Optional.

Options

[:after (Array|Proc)] The location of PageInfo block.

                  An +Array+ will be passed directly to <code>Hash#dig</code>. A +TypeError+ resulting
                  from the +#dig+ call will be raised as an +ArgumentError+.

                  The <code>"data"</code> and <code>"pageInfo"</code> keys are automatically added if not provided.

                  A +Proc+ must accept the GraphQL response +Hash+ as its argument and must return the
                  +pageInfo+ block to use for pagination.

[:before (Array|Proc)] See the :after option [:variable (String)] Name of the GraphQL variable to use as the "page" argument. Defaults to "before" or "after", depending on the pagination direction.

Errors

ArgumentError



195
196
197
# File 'lib/shopify_api/graphql/tiny.rb', line 195

def paginate(*options)
  Pager.new(self, options)
end