shopify-client 0.0.2 → 0.0.7
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/README.md +7 -5
- data/lib/shopify-client/client.rb +2 -0
- data/lib/shopify-client/config_error.rb +5 -0
- data/lib/shopify-client/create_all_webhooks.rb +43 -9
- data/lib/shopify-client/create_webhook.rb +5 -3
- data/lib/shopify-client/delete_all_webhooks.rb +32 -10
- data/lib/shopify-client/delete_webhook.rb +1 -3
- data/lib/shopify-client/response.rb +11 -0
- data/lib/shopify-client/version.rb +1 -1
- data/lib/shopify-client/webhook_list.rb +1 -1
- data/lib/shopify-client.rb +3 -3
- metadata +34 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 990510a605ed8b6b76a68ec7e958cf9a778760a809e5cbb902bfeb3cd450a916
|
4
|
+
data.tar.gz: b81c5340259127a52174116b9762e8040474a9ee66e3ade025a83e30658aee27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16346f143eefe2971c7bb3f7bbda0260b31808ea2e3b5593bce111d017cf062fd05a5c9176d959c1048c00e73869ffb7c92092864c7b45715f80a389156c786e
|
7
|
+
data.tar.gz: 320f639c45e575de5aad81f25ce473e463ed1072f25a9ee405b724199494a4deadc58399ee7426930c1be2db632c25d21fc40e0f3ecdf6bf84c601050ca4ae89
|
data/README.md
CHANGED
@@ -46,7 +46,7 @@ Setup
|
|
46
46
|
config.api_version = '...' # e.g. '2021-04'
|
47
47
|
config.cache_ttl = 3600
|
48
48
|
config.redirect_uri = '...' # for OAuth
|
49
|
-
config.logger = Logger.new(
|
49
|
+
config.logger = Logger.new($stdout) # defaults to a null logger
|
50
50
|
config.scope = '...'
|
51
51
|
config.shared_secret = '...'
|
52
52
|
config.webhook_uri = '...'
|
@@ -80,7 +80,7 @@ Calling the API
|
|
80
80
|
|
81
81
|
Request logging is disabled by default. To enable it:
|
82
82
|
|
83
|
-
ShopifyClient.config.logger = Logger.new(
|
83
|
+
ShopifyClient.config.logger = Logger.new($stdout)
|
84
84
|
|
85
85
|
Request throttling is enabled by default. If you're using Redis, throttling will
|
86
86
|
automatically make use of it; otherwise, throttling will only be maintained
|
@@ -93,7 +93,7 @@ The gem wraps Shopify's bulk query API by writing the result to a temporary file
|
|
93
93
|
and yielding an enumerator which itself streams each line of the result to limit
|
94
94
|
memory usage.
|
95
95
|
|
96
|
-
client.
|
96
|
+
client.graphql_bulk(%(
|
97
97
|
{
|
98
98
|
products {
|
99
99
|
edges {
|
@@ -300,7 +300,7 @@ Find a single result:
|
|
300
300
|
|
301
301
|
Iterate over results (automatic pagination):
|
302
302
|
|
303
|
-
order_repo.all.each do |order|
|
303
|
+
order_repo.all(client).each do |order|
|
304
304
|
# ...
|
305
305
|
end
|
306
306
|
|
@@ -350,10 +350,12 @@ Testing
|
|
350
350
|
### Integration tests
|
351
351
|
|
352
352
|
The integration tests require a private app with the scope `write_products`.
|
353
|
-
Create a .env file specifying the test shop
|
353
|
+
Create a .env file specifying the test shop, private app password, and a valid
|
354
|
+
webhook URI:
|
354
355
|
|
355
356
|
TEST_SHOP='test-shop.myshopify.com'
|
356
357
|
TEST_PASSWORD='shppa_...'
|
358
|
+
TEST_WEBHOOK_URI='https://.../webhooks'
|
357
359
|
|
358
360
|
Run the suite:
|
359
361
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'async/http/faraday'
|
3
4
|
require 'faraday'
|
4
5
|
require 'faraday_middleware'
|
5
6
|
|
@@ -18,6 +19,7 @@ module ShopifyClient
|
|
18
19
|
},
|
19
20
|
url: "https://#{myshopify_domain}/admin/api/#{ShopifyClient.config.api_version}",
|
20
21
|
) do |conn|
|
22
|
+
conn.adapter :async_http
|
21
23
|
# Request throttling to avoid API rate limit.
|
22
24
|
conn.use default_throttling_strategy
|
23
25
|
# Retry for 429, too many requests.
|
@@ -6,18 +6,52 @@ module ShopifyClient
|
|
6
6
|
#
|
7
7
|
# @param client [Client]
|
8
8
|
#
|
9
|
-
# @return [Array<
|
9
|
+
# @return [Array<String>] GraphQL IDs
|
10
10
|
def call(client)
|
11
|
-
|
11
|
+
raise ConfigError, 'webhook_uri is not set' unless ShopifyClient.config.webhook_uri
|
12
12
|
|
13
|
-
ShopifyClient.webhooks.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
webhooks_with_index = ShopifyClient.webhooks.each_with_index
|
14
|
+
|
15
|
+
return [] unless webhooks_with_index.any?
|
16
|
+
|
17
|
+
client.graphql(%(
|
18
|
+
mutation webhookSubscriptionCreate(
|
19
|
+
#{webhooks_with_index.map { |_, i| %(
|
20
|
+
$topic#{i}: WebhookSubscriptionTopic!
|
21
|
+
$webhookSubscription#{i}: WebhookSubscriptionInput!
|
22
|
+
)}.join("\n")}
|
23
|
+
) {
|
24
|
+
#{webhooks_with_index.map { |_, i| %(
|
25
|
+
webhookSubscriptionCreate#{i}: webhookSubscriptionCreate(
|
26
|
+
topic: $topic#{i}
|
27
|
+
webhookSubscription: $webhookSubscription#{i}
|
28
|
+
) {
|
29
|
+
userErrors {
|
30
|
+
field
|
31
|
+
message
|
32
|
+
}
|
33
|
+
webhookSubscription {
|
34
|
+
id
|
35
|
+
}
|
36
|
+
}
|
37
|
+
)}.join("\n")}
|
38
|
+
}
|
39
|
+
), webhooks_with_index.each_with_object({}) do |((topic, options), i), variables|
|
40
|
+
variables["topic#{i}"] = topic_to_graphql(topic)
|
41
|
+
variables["webhookSubscription#{i}"] = {}.tap do |subscription|
|
42
|
+
subscription['callbackUrl'] = ShopifyClient.config.webhook_uri
|
43
|
+
subscription['includeFields'] = options[:fields] unless options[:fields].empty?
|
19
44
|
end
|
20
|
-
end.map
|
45
|
+
end).data['data'].map do |_, mutation|
|
46
|
+
mutation['webhookSubscription']['id']
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param topic [String]
|
51
|
+
#
|
52
|
+
# @return [String]
|
53
|
+
private def topic_to_graphql(topic)
|
54
|
+
topic.upcase.sub('/', '_')
|
21
55
|
end
|
22
56
|
end
|
23
57
|
end
|
@@ -7,11 +7,13 @@ module ShopifyClient
|
|
7
7
|
# @option webhook [String] :topic
|
8
8
|
# @option webhook [Array<String>] :fields
|
9
9
|
#
|
10
|
-
# @return [
|
10
|
+
# @return [Integer] ID
|
11
11
|
def call(client, webhook)
|
12
|
-
|
12
|
+
raise ConfigError, 'webhook_uri is not set' unless ShopifyClient.config.webhook_uri
|
13
|
+
|
14
|
+
client.post('webhooks', webhook: webhook.merge(
|
13
15
|
address: ShopifyClient.config.webhook_uri,
|
14
|
-
))
|
16
|
+
)).data['webhook']['id']
|
15
17
|
rescue Response::Error => e
|
16
18
|
raise e unless e.response.errors.message?([
|
17
19
|
/has already been taken/,
|
@@ -5,18 +5,40 @@ module ShopifyClient
|
|
5
5
|
# Delete any existing webhooks.
|
6
6
|
#
|
7
7
|
# @param client [Client]
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
# @param ids [Array<Integer>, nil] GraphQL IDs
|
9
|
+
def call(client, ids: nil)
|
10
|
+
ids ||= client.graphql(%({
|
11
|
+
webhookSubscriptions(first: 100) {
|
12
|
+
edges {
|
13
|
+
node {
|
14
|
+
id
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
})).data['data']['webhookSubscriptions']['edges'].map do |edge|
|
19
|
+
edge['node']['id']
|
20
|
+
end
|
12
21
|
|
13
|
-
|
22
|
+
return if ids.empty?
|
14
23
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
24
|
+
client.graphql(%(
|
25
|
+
mutation webhookSubscriptionDelete(
|
26
|
+
#{ids.each_with_index.map { |_, i| %(
|
27
|
+
$id#{i}: ID!
|
28
|
+
)}.join("\n")}
|
29
|
+
) {
|
30
|
+
#{ids.each_with_index.map { |_, i| %(
|
31
|
+
webhookSubscriptionDelete#{i}: webhookSubscriptionDelete(id: $id#{i}) {
|
32
|
+
userErrors {
|
33
|
+
field
|
34
|
+
message
|
35
|
+
}
|
36
|
+
}
|
37
|
+
)}.join("\n")}
|
38
|
+
}
|
39
|
+
), ids.each_with_index.each_with_object({}) do |(id, i), variables|
|
40
|
+
variables["id#{i}"] = id
|
41
|
+
end)
|
20
42
|
end
|
21
43
|
end
|
22
44
|
end
|
@@ -72,6 +72,15 @@ module ShopifyClient
|
|
72
72
|
end
|
73
73
|
when 423
|
74
74
|
raise ShopError.new(request, self), 'Shop is locked'
|
75
|
+
when 430
|
76
|
+
# NOTE: This is an unofficial code used by Shopify. See:
|
77
|
+
#
|
78
|
+
# https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#Unofficial_codes
|
79
|
+
#
|
80
|
+
# It's undocumented unfortunately, but seems to be like a 429 response,
|
81
|
+
# except where the app is making too many API calls (rather than hitting
|
82
|
+
# the per store rate limit).
|
83
|
+
raise TooManyRequestsError.new(request, self), 'Too many requests'
|
75
84
|
when 400..499
|
76
85
|
raise ClientError.new(request, self)
|
77
86
|
when 500..599
|
@@ -183,6 +192,8 @@ module ShopifyClient
|
|
183
192
|
InvalidAccessTokenError = Class.new(ClientError)
|
184
193
|
# The shop is frozen/locked/unavailable.
|
185
194
|
ShopError = Class.new(ClientError)
|
195
|
+
# The app is making too many requests to the API.
|
196
|
+
TooManyRequestsError = Class.new(ClientError)
|
186
197
|
|
187
198
|
# The GraphQL API always responds with a status code of 200.
|
188
199
|
GraphQLClientError = Class.new(ClientError) do
|
@@ -12,7 +12,7 @@ module ShopifyClient
|
|
12
12
|
# @param topic [String]
|
13
13
|
# @param handler [#call]
|
14
14
|
# @param fields [Array<String>] e.g. %w[id tags]
|
15
|
-
def register(topic, handler = nil, fields:
|
15
|
+
def register(topic, handler = nil, fields: [], &block)
|
16
16
|
raise ArgumentError unless nil ^ handler ^ block
|
17
17
|
|
18
18
|
handler = block if block
|
data/lib/shopify-client.rb
CHANGED
@@ -17,9 +17,9 @@ module ShopifyClient
|
|
17
17
|
extend Dry::Configurable
|
18
18
|
|
19
19
|
setting :api_key
|
20
|
-
setting :api_version, '2021-04'
|
21
|
-
setting :cache_ttl, 3600
|
22
|
-
setting :logger, Logger.new(File::NULL).freeze
|
20
|
+
setting :api_version, default: '2021-04'
|
21
|
+
setting :cache_ttl, default: 3600
|
22
|
+
setting :logger, default: Logger.new(File::NULL).freeze
|
23
23
|
setting :oauth_redirect_uri
|
24
24
|
setting :oauth_scope
|
25
25
|
setting :shared_secret
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shopify-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kelsey Judson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dotenv
|
@@ -66,20 +66,48 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '2.7'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: async
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.29'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.29'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: async-http-faraday
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.11'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.11'
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
name: dry-configurable
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
72
100
|
requirements:
|
73
101
|
- - "~>"
|
74
102
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0.
|
103
|
+
version: '0.13'
|
76
104
|
type: :runtime
|
77
105
|
prerelease: false
|
78
106
|
version_requirements: !ruby/object:Gem::Requirement
|
79
107
|
requirements:
|
80
108
|
- - "~>"
|
81
109
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0.
|
110
|
+
version: '0.13'
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: faraday
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -153,6 +181,7 @@ files:
|
|
153
181
|
- lib/shopify-client/client.rb
|
154
182
|
- lib/shopify-client/client/logging.rb
|
155
183
|
- lib/shopify-client/client/normalise_path.rb
|
184
|
+
- lib/shopify-client/config_error.rb
|
156
185
|
- lib/shopify-client/cookieless/check_header.rb
|
157
186
|
- lib/shopify-client/cookieless/decode_session_token.rb
|
158
187
|
- lib/shopify-client/cookieless/middleware.rb
|
@@ -199,7 +228,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
199
228
|
- !ruby/object:Gem::Version
|
200
229
|
version: '0'
|
201
230
|
requirements: []
|
202
|
-
rubygems_version: 3.2.
|
231
|
+
rubygems_version: 3.2.22
|
203
232
|
signing_key:
|
204
233
|
specification_version: 4
|
205
234
|
summary: Shopify client library
|