shopify-client 0.0.2 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|