shopify-client 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|