shopify_api-graphql-tiny 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changes +8 -0
- data/README.md +40 -1
- data/lib/shopify_api/graphql/tiny/version.rb +1 -1
- data/lib/shopify_api/graphql/tiny.rb +65 -16
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 18495ff517670050b05536c1ef5fda702e689a4ffab229eba78fd2aba055dce5
|
|
4
|
+
data.tar.gz: c374cf2ae48b8ed7de70b5ec8501841593535ad21ef2d5591deb17c7dc96fdef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3388c846e2190e5996f780bf847d28f66c9336e0a96d53a8b6eed051fe8e38ffb18f3b32cc0abb15dfb15373d9f254eb6c8afbd2fbc050b3c19aea9c690db4de
|
|
7
|
+
data.tar.gz: e29a9dd41cd963bd96e8025ee31c5c4ec5b6b2726164655707c214fa73062f4e30e72545ea2df0c1b2ce54c90816c564776f509e4b9e3a51ef672290d35d2aec
|
data/Changes
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
2026-06-23 v1.0.2
|
|
2
|
+
--------------------
|
|
3
|
+
* Add :raise_on_warnings option to raise a WarningError when a response contains warnings
|
|
4
|
+
|
|
5
|
+
2026-01-24 v1.0.1
|
|
6
|
+
--------------------
|
|
7
|
+
* Add support for calling #paginate without a block
|
|
8
|
+
|
|
1
9
|
2026-01-20 v1.0.0
|
|
2
10
|
--------------------
|
|
3
11
|
* Add support for exponential backoff and remove ShopifyAPIRetry
|
data/README.md
CHANGED
|
@@ -97,7 +97,7 @@ These can be overridden globally (by assigning to the constant) or per instance:
|
|
|
97
97
|
gql = ShopifyAPI::GraphQL::Tiny.new(shop, token, :max_attempts => 20, :max_delay => 90)
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
`ShopifyAPI::GraphQL::Tiny::DEFAULT_RETRY_ERRORS` determines what is retried. It contains
|
|
100
|
+
`ShopifyAPI::GraphQL::Tiny::DEFAULT_RETRY_ERRORS` determines what is retried. It contains HTTP status codes, Shopify GraphQL errors codes, and exception classes.
|
|
101
101
|
By default it contains:
|
|
102
102
|
|
|
103
103
|
* `"5XX"` - Any HTTP 5XX status
|
|
@@ -160,6 +160,13 @@ pager.execute(query, :foo => 123) do |page|
|
|
|
160
160
|
end
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
+
If a block is not given an `Enumerator::Lazy` instance is returned that will fetch the next page upon each iteration:
|
|
164
|
+
|
|
165
|
+
```rb
|
|
166
|
+
results = pager.execute(query, :foo => 123)
|
|
167
|
+
results.each { |page| ... }
|
|
168
|
+
```
|
|
169
|
+
|
|
163
170
|
#### `after` Pagination
|
|
164
171
|
|
|
165
172
|
To use `after` pagination, i.e., to paginate forward, your query must:
|
|
@@ -259,6 +266,37 @@ pager.execute(query) { |page| }
|
|
|
259
266
|
|
|
260
267
|
The `"data"` and `"pageInfo"` keys are automatically added if not provided.
|
|
261
268
|
|
|
269
|
+
### Raising on Warnings
|
|
270
|
+
|
|
271
|
+
A successful GraphQL response can still contain warnings (for example an invalid search field reported under
|
|
272
|
+
`extensions.search`). By default these are ignored. Set `:raise_on_warnings` to `true` to raise a `WarningError`
|
|
273
|
+
(a subclass of `GraphQLError`) whenever the response contains warnings:
|
|
274
|
+
|
|
275
|
+
```rb
|
|
276
|
+
gql = ShopifyAPI::GraphQL::Tiny.new(shop, token, :raise_on_warnings => true)
|
|
277
|
+
|
|
278
|
+
begin
|
|
279
|
+
gql.execute(query)
|
|
280
|
+
rescue ShopifyAPI::GraphQL::Tiny::WarningError => e
|
|
281
|
+
warn e.message # The warnings, formatted
|
|
282
|
+
e.response # The full GraphQL response Hash
|
|
283
|
+
end
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Why Use This Instead of Shopify's API Client?
|
|
287
|
+
|
|
288
|
+
- Easy-to-use
|
|
289
|
+
- Built-in retry
|
|
290
|
+
- Built-in pagination
|
|
291
|
+
- Lightweight
|
|
292
|
+
|
|
293
|
+
Overall, Shopify's API client is bloated trash that will give you development headaches and long-term maintenance nightmares.
|
|
294
|
+
|
|
295
|
+
We used to use it, staring way back in 2015, but eventually had to pivot away from their Ruby libraries due to developer
|
|
296
|
+
frustration and high maintenance cost (and don't get us started on the ShopifyApp gem!@#).
|
|
297
|
+
|
|
298
|
+
For more information see: https://github.com/Shopify/shopify-api-ruby/issues/1181
|
|
299
|
+
|
|
262
300
|
## Testing
|
|
263
301
|
|
|
264
302
|
`cp env.template .env` and fill-in `.env` with the missing values. This requires a Shopify store.
|
|
@@ -271,6 +309,7 @@ bundle exec rake rate_limit SHOPIFY_DOMAIN=your-domain SHOPIFY_TOKEN=your-token
|
|
|
271
309
|
|
|
272
310
|
## See Also
|
|
273
311
|
|
|
312
|
+
- [`ShopifyAPI::GraphQL::Request`](https://github.com/ScreenStaring/shopify_api-graphql-request) - A higher-level wrapper around this class with improved exception handling and `:snake_case` hash key conversion
|
|
274
313
|
- [Shopify Dev Tools](https://github.com/ScreenStaring/shopify-dev-tools) - Command-line program to assist with the development and/or maintenance of Shopify apps and stores
|
|
275
314
|
- [Shopify ID Export](https://github.com/ScreenStaring/shopify_id_export/) - Dump Shopify product and variant IDs —along with other identifiers— to a CSV or JSON file
|
|
276
315
|
- [`TinyGID`](https://github.com/sshaw/tiny_gid/) - Build Global ID (gid://) URI strings from scalar values
|
|
@@ -29,6 +29,7 @@ module ShopifyAPI
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
RateLimitError = Class.new(GraphQLError)
|
|
32
|
+
WarningError = Class.new(GraphQLError)
|
|
32
33
|
|
|
33
34
|
class HTTPError < Error
|
|
34
35
|
attr_reader :code
|
|
@@ -84,6 +85,7 @@ module ShopifyAPI
|
|
|
84
85
|
# [:jitter (Boolean)] Exponential backoff jitter (random delay added to backoff). Defaults to +true+
|
|
85
86
|
# [:multiplier (Float)] Exponential backoff multiplier. Defaults to +2.0+
|
|
86
87
|
# [:debug (Boolean|IO)] Output the HTTP request/response to +STDERR+ or to its value if it's an +IO+. Defaults to +false+.
|
|
88
|
+
# [:raise_on_warnings (Boolean)] If +true+ raise a WarningError when the GraphQL response contains warnings. Defaults to +false+.
|
|
87
89
|
#
|
|
88
90
|
# === Errors
|
|
89
91
|
#
|
|
@@ -96,6 +98,7 @@ module ShopifyAPI
|
|
|
96
98
|
|
|
97
99
|
@domain = shopify_domain(shop)
|
|
98
100
|
@options = options || {}
|
|
101
|
+
@raise_on_warnings = @options[:raise_on_warnings]
|
|
99
102
|
|
|
100
103
|
@headers = DEFAULT_HEADERS.dup
|
|
101
104
|
@headers[ACCESS_TOKEN_HEADER] = token
|
|
@@ -137,6 +140,8 @@ module ShopifyAPI
|
|
|
137
140
|
# rate-limited after the configured number of retry attempts
|
|
138
141
|
# * A ShopifyAPI::GraphQL::Tiny::GraphQLError is raised if the response contains an +errors+ property that is
|
|
139
142
|
# not a rate-limit error
|
|
143
|
+
# * A ShopifyAPI::GraphQL::Tiny::WarningError is raised if the +:raise_on_warnings+ option is +true+ and the
|
|
144
|
+
# response contains warnings
|
|
140
145
|
#
|
|
141
146
|
# === Returns
|
|
142
147
|
#
|
|
@@ -158,7 +163,8 @@ module ShopifyAPI
|
|
|
158
163
|
# page.dig("data", "product", "title")
|
|
159
164
|
# end
|
|
160
165
|
#
|
|
161
|
-
# The block is called for each page.
|
|
166
|
+
# The block is called for each page. If a block is not provided returns an instance of Enumerator::Lazy that will fetch the next
|
|
167
|
+
# page on each iteration.
|
|
162
168
|
#
|
|
163
169
|
# Using pagination requires you to include the
|
|
164
170
|
# {PageInfo}[https://shopify.dev/api/admin-graphql/2022-10/objects/PageInfo]
|
|
@@ -188,9 +194,13 @@ module ShopifyAPI
|
|
|
188
194
|
# Defaults to <code>"before"</code> or <code>"after"</code>, depending on the pagination
|
|
189
195
|
# direction.
|
|
190
196
|
#
|
|
197
|
+
# === Returns
|
|
198
|
+
#
|
|
199
|
+
# +nil+ or `Enumerator::Lazy` if no block was provided
|
|
200
|
+
#
|
|
191
201
|
# === Errors
|
|
192
202
|
#
|
|
193
|
-
#
|
|
203
|
+
# See #execute
|
|
194
204
|
|
|
195
205
|
def paginate(*options)
|
|
196
206
|
Pager.new(self, options)
|
|
@@ -216,12 +226,20 @@ module ShopifyAPI
|
|
|
216
226
|
end
|
|
217
227
|
|
|
218
228
|
json = parse_json(response.body)
|
|
219
|
-
return json unless json.include?("errors")
|
|
220
229
|
|
|
221
|
-
|
|
230
|
+
if json.include?("errors")
|
|
231
|
+
return make_request(query, variables) if handle_graphql_error(json)
|
|
232
|
+
|
|
233
|
+
message = error_message(json["errors"])
|
|
234
|
+
raise GraphQLError.new("failed to execute query for #@domain: #{message}", json)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
if @raise_on_warnings
|
|
238
|
+
warnings = find_warnings(json)
|
|
239
|
+
raise WarningError.new("query for #@domain returned warnings: #{warning_message(warnings)}", json) unless warnings.empty?
|
|
240
|
+
end
|
|
222
241
|
|
|
223
|
-
|
|
224
|
-
raise GraphQLError.new("failed to execute query for #@domain: #{message}", json)
|
|
242
|
+
json
|
|
225
243
|
end
|
|
226
244
|
|
|
227
245
|
def post(query, variables = nil)
|
|
@@ -318,6 +336,34 @@ module ShopifyAPI
|
|
|
318
336
|
end.join(", ")
|
|
319
337
|
end
|
|
320
338
|
|
|
339
|
+
def find_warnings(data, prop = nil)
|
|
340
|
+
case data
|
|
341
|
+
when Hash
|
|
342
|
+
data.flat_map do |key, value|
|
|
343
|
+
if key == "warnings" && value.is_a?(Array)
|
|
344
|
+
value.map { |w| [prop, w] }
|
|
345
|
+
else
|
|
346
|
+
find_warnings(value, key)
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
when Array
|
|
350
|
+
data.flat_map { |value| find_warnings(value, prop) }
|
|
351
|
+
else
|
|
352
|
+
[]
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def warning_message(warnings)
|
|
357
|
+
warnings.map do |prop, warning|
|
|
358
|
+
next warning.to_s unless warning.is_a?(Hash)
|
|
359
|
+
|
|
360
|
+
field = Array(warning["field"]).join(".")
|
|
361
|
+
parts = [prop]
|
|
362
|
+
parts << field unless field.empty?
|
|
363
|
+
"#{parts.join(" ")}: #{warning["message"]}"
|
|
364
|
+
end.join("\n")
|
|
365
|
+
end
|
|
366
|
+
|
|
321
367
|
def request_attempts_remain?
|
|
322
368
|
@request_attempts < @backoff_options[:max_attempts]
|
|
323
369
|
end
|
|
@@ -348,19 +394,22 @@ module ShopifyAPI
|
|
|
348
394
|
variables ||= {}
|
|
349
395
|
pagination_finder = @options[@options[:direction]]
|
|
350
396
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
397
|
+
enumerator = Enumerator.new do |y|
|
|
398
|
+
loop do
|
|
399
|
+
page = @gql.execute(q, variables)
|
|
400
|
+
y << page
|
|
355
401
|
|
|
356
|
-
|
|
357
|
-
|
|
402
|
+
cursor = pagination_finder[page]
|
|
403
|
+
break unless cursor
|
|
358
404
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
405
|
+
variables[@options[:variable]] = cursor
|
|
406
|
+
end
|
|
407
|
+
end
|
|
362
408
|
|
|
363
|
-
|
|
409
|
+
if block_given?
|
|
410
|
+
enumerator.each { |page| yield page }
|
|
411
|
+
else
|
|
412
|
+
enumerator.lazy
|
|
364
413
|
end
|
|
365
414
|
end
|
|
366
415
|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: shopify_api-graphql-tiny
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Skye Shaw
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-06-23 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: net_http_timeout_errors
|