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 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