shopify-client 0.0.1
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 +7 -0
- data/README.md +342 -0
- data/lib/shopify-client.rb +49 -0
- data/lib/shopify-client/authorise.rb +38 -0
- data/lib/shopify-client/bulk_request.rb +219 -0
- data/lib/shopify-client/cache/redis_store.rb +28 -0
- data/lib/shopify-client/cache/store.rb +67 -0
- data/lib/shopify-client/cache/thread_local_store.rb +47 -0
- data/lib/shopify-client/cached_request.rb +69 -0
- data/lib/shopify-client/client.rb +126 -0
- data/lib/shopify-client/client/logging.rb +38 -0
- data/lib/shopify-client/client/normalise_path.rb +14 -0
- data/lib/shopify-client/cookieless/check_header.rb +28 -0
- data/lib/shopify-client/cookieless/decode_session_token.rb +43 -0
- data/lib/shopify-client/cookieless/middleware.rb +39 -0
- data/lib/shopify-client/create_all_webhooks.rb +23 -0
- data/lib/shopify-client/create_webhook.rb +21 -0
- data/lib/shopify-client/delete_all_webhooks.rb +22 -0
- data/lib/shopify-client/delete_webhook.rb +13 -0
- data/lib/shopify-client/error.rb +9 -0
- data/lib/shopify-client/parse_link_header.rb +33 -0
- data/lib/shopify-client/request.rb +40 -0
- data/lib/shopify-client/resource/base.rb +46 -0
- data/lib/shopify-client/resource/create.rb +31 -0
- data/lib/shopify-client/resource/delete.rb +29 -0
- data/lib/shopify-client/resource/read.rb +80 -0
- data/lib/shopify-client/resource/update.rb +30 -0
- data/lib/shopify-client/response.rb +201 -0
- data/lib/shopify-client/response_errors.rb +59 -0
- data/lib/shopify-client/response_user_errors.rb +42 -0
- data/lib/shopify-client/struct.rb +10 -0
- data/lib/shopify-client/throttling/redis_strategy.rb +62 -0
- data/lib/shopify-client/throttling/strategy.rb +50 -0
- data/lib/shopify-client/throttling/thread_local_strategy.rb +29 -0
- data/lib/shopify-client/verify_request.rb +51 -0
- data/lib/shopify-client/verify_webhook.rb +24 -0
- data/lib/shopify-client/version.rb +5 -0
- data/lib/shopify-client/webhook.rb +32 -0
- data/lib/shopify-client/webhook_list.rb +50 -0
- metadata +206 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyClient
|
4
|
+
class ResponseErrors
|
5
|
+
class << self
|
6
|
+
# Certain error responses, such as 'Not Found', use a string rather than
|
7
|
+
# an object. For consistency, these messages are set under 'resource'.
|
8
|
+
#
|
9
|
+
# @param data [Hash] the complete response data
|
10
|
+
#
|
11
|
+
# @return [ResponseErrors]
|
12
|
+
def from_response_data(data)
|
13
|
+
errors = data['errors']
|
14
|
+
|
15
|
+
return new if errors.nil?
|
16
|
+
|
17
|
+
errors.is_a?(String) ? new('resource' => errors) : new(errors)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param errors [Hash]
|
22
|
+
def initialize(errors = {})
|
23
|
+
@errors = errors
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Array<String]
|
27
|
+
def messages
|
28
|
+
@errors.map do |field, message|
|
29
|
+
"#{message} [#{field}]"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# message_patterns [Array<Regexp, String>]
|
34
|
+
#
|
35
|
+
# @return [Boolean]
|
36
|
+
def message?(message_patterns)
|
37
|
+
message_patterns.any? do |pattern|
|
38
|
+
case pattern
|
39
|
+
when Regexp
|
40
|
+
messages.any? { |message| message.match?(pattern) }
|
41
|
+
when String
|
42
|
+
messages.include?(pattern)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
include Enumerable
|
48
|
+
|
49
|
+
# @see Hash#each
|
50
|
+
def each(...)
|
51
|
+
@errors.dup.each(...)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Hash]
|
55
|
+
def to_h
|
56
|
+
@errors.dup
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyClient
|
4
|
+
class ResponseUserErrors < ResponseErrors
|
5
|
+
class << self
|
6
|
+
# @param data [Hash] the complete response data
|
7
|
+
#
|
8
|
+
# @return [ResponseErrors]
|
9
|
+
def from_response_data(data)
|
10
|
+
errors = find_user_errors(data) || {}
|
11
|
+
|
12
|
+
return new if errors.empty?
|
13
|
+
|
14
|
+
new(errors.to_h do |error|
|
15
|
+
[
|
16
|
+
error['field'] ? error['field'].join('.') : '.',
|
17
|
+
error['message'],
|
18
|
+
]
|
19
|
+
end)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Find user errors recursively.
|
23
|
+
#
|
24
|
+
# @param data [Hash]
|
25
|
+
#
|
26
|
+
# @return [Hash, nil]
|
27
|
+
def find_errors(data)
|
28
|
+
data.each do |key, value|
|
29
|
+
return value if key == 'userErrors'
|
30
|
+
|
31
|
+
if value.is_a?(Hash)
|
32
|
+
errors = find_errors(value)
|
33
|
+
|
34
|
+
return errors if errors
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyClient
|
4
|
+
module Throttling
|
5
|
+
# Use Redis to maintain API call limit throttling across threads/processes.
|
6
|
+
#
|
7
|
+
# No delay for requests up to half of the call limit.
|
8
|
+
class RedisStrategy < Strategy
|
9
|
+
# @see Strategy#interval
|
10
|
+
def interval(interval_key)
|
11
|
+
num_requests, max_requests, header_timestamp = Redis.current.hmget(interval_key,
|
12
|
+
:num_requests,
|
13
|
+
:max_requests,
|
14
|
+
:header_timestamp,
|
15
|
+
).map(&:to_i)
|
16
|
+
|
17
|
+
num_requests = leak(num_requests, header_timestamp)
|
18
|
+
|
19
|
+
max_unthrottled_requests = max_requests / 2
|
20
|
+
|
21
|
+
if num_requests > num_unthrottled_requests
|
22
|
+
Rational((num_requests - num_unthrottled_requests) * leak_rate, 1000)
|
23
|
+
else
|
24
|
+
0
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @see Strategy#after_sleep
|
29
|
+
def after_sleep(env, interval_key)
|
30
|
+
header = env[:response_headers]['X-Shopify-Shop-Api-Call-Limit']
|
31
|
+
|
32
|
+
return if header.nil?
|
33
|
+
|
34
|
+
num_requests, max_requests = header.split('/')
|
35
|
+
|
36
|
+
Redis.current.mapped_hmset(interval_key,
|
37
|
+
num_requests: num_requests,
|
38
|
+
max_requests: max_requests,
|
39
|
+
header_timestamp: header_timestamp,
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Find the actual number of requests by subtracting requests leaked by the
|
44
|
+
# leaky bucket algorithm since the last header timestamp.
|
45
|
+
#
|
46
|
+
# @param num_requests [Integer]
|
47
|
+
# @param header_timestamp [Integer]
|
48
|
+
#
|
49
|
+
# @return [Integer]
|
50
|
+
def leak(num_requests, header_timestamp)
|
51
|
+
n = Rational(timestamp - header_timestamp, leak_rate).floor
|
52
|
+
|
53
|
+
n > num_requests ? 0 : num_requests - n
|
54
|
+
end
|
55
|
+
|
56
|
+
# Leak rate of the leaky bucket algorithm in milliseconds.
|
57
|
+
def leak_rate
|
58
|
+
500
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyClient
|
4
|
+
module Throttling
|
5
|
+
# @abstract
|
6
|
+
class Strategy < Faraday::Middleware
|
7
|
+
# @param env [Faraday::Env]
|
8
|
+
def on_request(env)
|
9
|
+
interval_key = build_interval_key(env)
|
10
|
+
|
11
|
+
sleep(interval(interval_key))
|
12
|
+
|
13
|
+
after_sleep(env, interval_key)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param env [Faraday::Env]
|
17
|
+
#
|
18
|
+
# @return [String]
|
19
|
+
def build_interval_key(env)
|
20
|
+
myshopify_domain = env.dig(:shopify, :myshopify_domain) || 'unknown'
|
21
|
+
|
22
|
+
format('shopify-client:throttling:%s', myshopify_domain)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Sleep interval in seconds.
|
26
|
+
#
|
27
|
+
# @param interval_key [String]
|
28
|
+
#
|
29
|
+
# @return [Numeric]
|
30
|
+
def interval(interval_key)
|
31
|
+
0
|
32
|
+
end
|
33
|
+
|
34
|
+
# Hook for setting the interval key.
|
35
|
+
#
|
36
|
+
# @param env [Faraday::Env]
|
37
|
+
# @param interval_key [String]
|
38
|
+
def after_sleep(env, interval_key)
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# Time in milliseconds since the UNIX epoch.
|
43
|
+
#
|
44
|
+
# @return [Integer]
|
45
|
+
def timestamp
|
46
|
+
(Time.now.to_f * 1000).to_i
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyClient
|
4
|
+
module Throttling
|
5
|
+
# Maintain API call limit throttling across a single thread.
|
6
|
+
class ThreadLocalStrategy < Strategy
|
7
|
+
# @see Strategy#interval
|
8
|
+
def interval(interval_key)
|
9
|
+
return 0 if Thread.current[interval_key].nil?
|
10
|
+
|
11
|
+
ms = (timestamp - Thread.current[interval_key])
|
12
|
+
|
13
|
+
ms < minimum_interval ? Rational(minimum_interval - ms, 1000) : 0
|
14
|
+
end
|
15
|
+
|
16
|
+
# @see Strategy#after_sleep
|
17
|
+
def after_sleep(env, interval_key)
|
18
|
+
Thread.current[interval_key] = timestamp
|
19
|
+
end
|
20
|
+
|
21
|
+
# Minimum time between requests in milliseconds.
|
22
|
+
#
|
23
|
+
# @return [Integer]
|
24
|
+
def minimum_interval
|
25
|
+
500
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module ShopifyClient
|
6
|
+
class VerifyRequest
|
7
|
+
Error = Class.new(Error)
|
8
|
+
|
9
|
+
# Verify that the request originated from Shopify.
|
10
|
+
#
|
11
|
+
# @param params [Hash] the request params
|
12
|
+
#
|
13
|
+
# @raise [Error] if signature is invalid
|
14
|
+
def call(params)
|
15
|
+
params = params.to_h.transform_keys(&:to_s)
|
16
|
+
digest = OpenSSL::Digest::SHA256.new
|
17
|
+
digest = OpenSSL::HMAC.hexdigest(digest, ShopifyClient.config.shared_secret, encoded_params(params))
|
18
|
+
|
19
|
+
raise Error, 'invalid signature' unless digest == params['hmac']
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param params [Hash]
|
23
|
+
#
|
24
|
+
# @return [String]
|
25
|
+
private def encoded_params(params)
|
26
|
+
params.reject do |k, _|
|
27
|
+
k == 'hmac'
|
28
|
+
end.map do |k, v|
|
29
|
+
[].tap do |param|
|
30
|
+
param << k.gsub(/./) { |c| encode_key(c) }
|
31
|
+
param << '='
|
32
|
+
param << v.gsub(/./) { |c| encode_val(c) }
|
33
|
+
end.join
|
34
|
+
end.join('&')
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param chr [String]
|
38
|
+
#
|
39
|
+
# @return [String]
|
40
|
+
private def encode_key(chr)
|
41
|
+
{'%' => '%25', '&' => '%26', '=' => '%3D'}[chr] || chr
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param chr [String]
|
45
|
+
#
|
46
|
+
# @return [String]
|
47
|
+
private def encode_val(chr)
|
48
|
+
{'%' => '%25', '&' => '%26'}[chr] || chr
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
require 'openssl'
|
5
|
+
|
6
|
+
module ShopifyClient
|
7
|
+
class VerifyWebhook
|
8
|
+
Error = Class.new(Error)
|
9
|
+
|
10
|
+
# Verify that the webhook request originated from Shopify.
|
11
|
+
#
|
12
|
+
# @param data [String] the signed request data
|
13
|
+
# @param hmac [String] the signature
|
14
|
+
#
|
15
|
+
# @raise [Error] if signature is invalid
|
16
|
+
def call(data, hmac)
|
17
|
+
digest = OpenSSL::Digest::SHA256.new
|
18
|
+
digest = OpenSSL::HMAC.digest(digest, ShopifyClient.config.shared_secret, data)
|
19
|
+
digest = Base64.encode64(digest).strip
|
20
|
+
|
21
|
+
raise Error, 'invalid signature' unless digest == hmac
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module ShopifyClient
|
6
|
+
# @!attribute [rw] myshopify_domain
|
7
|
+
# @return [String]
|
8
|
+
# @!attribute [rw] topic
|
9
|
+
# @return [String]
|
10
|
+
# @!attribute [rw] raw_data
|
11
|
+
# @return [String]
|
12
|
+
Webhook = Struct.new(:myshopify_domain, :topic, :raw_data) do
|
13
|
+
# @return [Hash]
|
14
|
+
def data
|
15
|
+
@data ||= JSON.parse(raw_data)
|
16
|
+
rescue JSON::ParserError
|
17
|
+
{}
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_method :to_h, :data
|
21
|
+
|
22
|
+
# @return [String]
|
23
|
+
def to_json(*args)
|
24
|
+
to_h.to_json(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String]
|
28
|
+
def inspect
|
29
|
+
"#<ShopifyClient::Webhook (#{myshopify_domain}, #{topic})>"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyClient
|
4
|
+
class WebhookList
|
5
|
+
def initialize
|
6
|
+
@webhooks = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
# Register a handler for a webhook topic. The callable handler should
|
10
|
+
# receive a single {Webhook} argument.
|
11
|
+
#
|
12
|
+
# @param topic [String]
|
13
|
+
# @param handler [#call]
|
14
|
+
# @param fields [Array<String>] e.g. %w[id tags]
|
15
|
+
def register(topic, handler = nil, fields: nil, &block)
|
16
|
+
raise ArgumentError unless nil ^ handler ^ block
|
17
|
+
|
18
|
+
handler = block if block
|
19
|
+
|
20
|
+
self[topic][:handlers] << handler
|
21
|
+
self[topic][:fields] |= fields # merge fields with previous fields
|
22
|
+
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Call each of the handlers registered for the given topic in turn.
|
27
|
+
#
|
28
|
+
# @param webhook [Webhook]
|
29
|
+
def delegate(webhook)
|
30
|
+
self[webhook.topic][:handlers].each do |handler|
|
31
|
+
handler.(webhook)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param topic [String]
|
36
|
+
def [](topic)
|
37
|
+
@webhooks[topic] ||= {
|
38
|
+
handlers: [],
|
39
|
+
fields: [],
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
include Enumerable
|
44
|
+
|
45
|
+
# @yield [Hash]
|
46
|
+
def each(&block)
|
47
|
+
@webhooks.each(&block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
metadata
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shopify-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kelsey Judson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-07-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dotenv
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '13.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '13.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.10'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.10'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: addressable
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.7'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.7'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: dry-configurable
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.12'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.12'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: faraday
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.4'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.4'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: faraday_middleware
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: jwt
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '2.2'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '2.2'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: zeitwerk
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '2.4'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '2.4'
|
139
|
+
description:
|
140
|
+
email: kelsey@kelseyjudson.dev
|
141
|
+
executables: []
|
142
|
+
extensions: []
|
143
|
+
extra_rdoc_files: []
|
144
|
+
files:
|
145
|
+
- README.md
|
146
|
+
- lib/shopify-client.rb
|
147
|
+
- lib/shopify-client/authorise.rb
|
148
|
+
- lib/shopify-client/bulk_request.rb
|
149
|
+
- lib/shopify-client/cache/redis_store.rb
|
150
|
+
- lib/shopify-client/cache/store.rb
|
151
|
+
- lib/shopify-client/cache/thread_local_store.rb
|
152
|
+
- lib/shopify-client/cached_request.rb
|
153
|
+
- lib/shopify-client/client.rb
|
154
|
+
- lib/shopify-client/client/logging.rb
|
155
|
+
- lib/shopify-client/client/normalise_path.rb
|
156
|
+
- lib/shopify-client/cookieless/check_header.rb
|
157
|
+
- lib/shopify-client/cookieless/decode_session_token.rb
|
158
|
+
- lib/shopify-client/cookieless/middleware.rb
|
159
|
+
- lib/shopify-client/create_all_webhooks.rb
|
160
|
+
- lib/shopify-client/create_webhook.rb
|
161
|
+
- lib/shopify-client/delete_all_webhooks.rb
|
162
|
+
- lib/shopify-client/delete_webhook.rb
|
163
|
+
- lib/shopify-client/error.rb
|
164
|
+
- lib/shopify-client/parse_link_header.rb
|
165
|
+
- lib/shopify-client/request.rb
|
166
|
+
- lib/shopify-client/resource/base.rb
|
167
|
+
- lib/shopify-client/resource/create.rb
|
168
|
+
- lib/shopify-client/resource/delete.rb
|
169
|
+
- lib/shopify-client/resource/read.rb
|
170
|
+
- lib/shopify-client/resource/update.rb
|
171
|
+
- lib/shopify-client/response.rb
|
172
|
+
- lib/shopify-client/response_errors.rb
|
173
|
+
- lib/shopify-client/response_user_errors.rb
|
174
|
+
- lib/shopify-client/struct.rb
|
175
|
+
- lib/shopify-client/throttling/redis_strategy.rb
|
176
|
+
- lib/shopify-client/throttling/strategy.rb
|
177
|
+
- lib/shopify-client/throttling/thread_local_strategy.rb
|
178
|
+
- lib/shopify-client/verify_request.rb
|
179
|
+
- lib/shopify-client/verify_webhook.rb
|
180
|
+
- lib/shopify-client/version.rb
|
181
|
+
- lib/shopify-client/webhook.rb
|
182
|
+
- lib/shopify-client/webhook_list.rb
|
183
|
+
homepage: https://gitea.judson.nz/shopify-apps/shopify-client
|
184
|
+
licenses:
|
185
|
+
- ISC
|
186
|
+
metadata: {}
|
187
|
+
post_install_message:
|
188
|
+
rdoc_options: []
|
189
|
+
require_paths:
|
190
|
+
- lib
|
191
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
|
+
requirements:
|
198
|
+
- - ">="
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0'
|
201
|
+
requirements: []
|
202
|
+
rubygems_version: 3.2.15
|
203
|
+
signing_key:
|
204
|
+
specification_version: 4
|
205
|
+
summary: Shopify client library
|
206
|
+
test_files: []
|