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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 300adbcc594f4ad258ccf2bf74e81c0c6763c41f722a2f35b2bb7641c394d27b
4
- data.tar.gz: 776d8e1439d218c2c09e2dbf1b01a174569b203fd4df6f48c8ba22d042785d04
3
+ metadata.gz: 990510a605ed8b6b76a68ec7e958cf9a778760a809e5cbb902bfeb3cd450a916
4
+ data.tar.gz: b81c5340259127a52174116b9762e8040474a9ee66e3ade025a83e30658aee27
5
5
  SHA512:
6
- metadata.gz: b2b388ed76c1b812f3530e929b9eb33ef070001cdd056a7eb5dbc28b12d5678d02ef2c332f4bfece665eb1f838e383cf6c98189e3a30f6df0875b39c80012f63
7
- data.tar.gz: 55e85ebabd3e2a0186d532a216f9ab6653a88cf605cb46010e614dff729ed0ed65f0e57ec5f82d225c3d1e58bcada46ebfd5f16162264f3b9131b4b11a45c92d
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(STDOUT) # defaults to a null logger
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(STDOUT)
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.grahql_bulk(%(
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 and private app password:
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.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyClient
4
+ ConfigError = Class.new(Error)
5
+ end
@@ -6,18 +6,52 @@ module ShopifyClient
6
6
  #
7
7
  # @param client [Client]
8
8
  #
9
- # @return [Array<Hash>] response data
9
+ # @return [Array<String>] GraphQL IDs
10
10
  def call(client)
11
- create_webhook = CreateWebhook.new
11
+ raise ConfigError, 'webhook_uri is not set' unless ShopifyClient.config.webhook_uri
12
12
 
13
- ShopifyClient.webhooks.map do |topic|
14
- Thread.new do
15
- create_webhook.(client, {
16
- topic: topic,
17
- fields: topic[:fields],
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(&:value)
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 [Hash] response data
10
+ # @return [Integer] ID
11
11
  def call(client, webhook)
12
- client.post_json(credentials, 'webhooks', webhook: webhook.merge(
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
- # @return [Array<Hash>] response data
10
- def call(client)
11
- webhooks = client.get(credentials, 'webhooks')['webhooks']
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
- delete_webhook = DeleteWebhook.new
22
+ return if ids.empty?
14
23
 
15
- webhooks.map do |webhook|
16
- Thread.new do
17
- delete_webhook.(client, webhook['id'])
18
- end
19
- end.map(&:value)
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
@@ -4,10 +4,8 @@ module ShopifyClient
4
4
  class DeleteWebhook
5
5
  # @param client [Client]
6
6
  # @param id [Integer]
7
- #
8
- # @return [Hash] response data
9
7
  def call(client, id)
10
- client.delete(credentials, "webhooks/#{id}")
8
+ client.delete("webhooks/#{id}")
11
9
  end
12
10
  end
13
11
  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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShopifyClient
4
- VERSION = '0.0.2'
4
+ VERSION = '0.0.7'
5
5
  end
@@ -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: nil, &block)
15
+ def register(topic, handler = nil, fields: [], &block)
16
16
  raise ArgumentError unless nil ^ handler ^ block
17
17
 
18
18
  handler = block if block
@@ -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.2
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-07-05 00:00:00.000000000 Z
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.12'
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.12'
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.15
231
+ rubygems_version: 3.2.22
203
232
  signing_key:
204
233
  specification_version: 4
205
234
  summary: Shopify client library