shopify_api 9.1.0 → 9.2.0
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 +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +12 -6
- data/CHANGELOG.md +8 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +4 -0
- data/README.md +1 -1
- data/Rakefile +8 -1
- data/SECURITY.md +59 -0
- data/docs/_config.yml +1 -0
- data/docs/_includes/footer.html +28 -0
- data/docs/_includes/head.html +28 -0
- data/docs/_layouts/index.html +57 -0
- data/docs/graphql.md +46 -1
- data/docs/index.md +639 -0
- data/lib/shopify_api/graphql.rb +20 -2
- data/lib/shopify_api/pagination_link_headers.rb +1 -1
- data/lib/shopify_api/resources/base.rb +1 -2
- data/lib/shopify_api/resources/inventory_level.rb +1 -1
- data/lib/shopify_api/resources/product.rb +21 -0
- data/lib/shopify_api/resources/smart_collection.rb +2 -6
- data/lib/shopify_api/resources/variant.rb +37 -0
- data/lib/shopify_api/session.rb +7 -2
- data/lib/shopify_api/version.rb +1 -1
- data/lib/verify_docs.rb +7 -0
- data/shopify_api.gemspec +0 -1
- data/test/base_test.rb +16 -0
- data/test/graphql_test.rb +32 -0
- data/test/product_test.rb +39 -0
- data/test/session_test.rb +44 -2
- data/test/smart_collection_test.rb +0 -25
- data/test/variant_test.rb +77 -19
- metadata +10 -5
- data/bin/shopify +0 -3
data/lib/shopify_api/graphql.rb
CHANGED
@@ -5,6 +5,8 @@ require 'shopify_api/graphql/http_client'
|
|
5
5
|
module ShopifyAPI
|
6
6
|
module GraphQL
|
7
7
|
DEFAULT_SCHEMA_LOCATION_PATH = Pathname('shopify_graphql_schemas')
|
8
|
+
DEFAULT_EXECUTION_ADAPTER = HTTPClient
|
9
|
+
DEFAULT_GRAPHQL_CLIENT = ::GraphQL::Client
|
8
10
|
|
9
11
|
InvalidSchema = Class.new(StandardError)
|
10
12
|
InvalidClient = Class.new(StandardError)
|
@@ -56,8 +58,8 @@ module ShopifyAPI
|
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
59
|
-
schema =
|
60
|
-
client =
|
61
|
+
schema = graphql_client.load_schema(schema_file.to_s)
|
62
|
+
client = graphql_client.new(schema: schema, execute: execution_adapter.new(api_version)).tap do |c|
|
61
63
|
c.allow_dynamic_queries = true
|
62
64
|
end
|
63
65
|
|
@@ -73,6 +75,22 @@ module ShopifyAPI
|
|
73
75
|
@schema_location = Pathname(path)
|
74
76
|
end
|
75
77
|
|
78
|
+
def execution_adapter
|
79
|
+
@execution_adapter || DEFAULT_EXECUTION_ADAPTER
|
80
|
+
end
|
81
|
+
|
82
|
+
def execution_adapter=(executor)
|
83
|
+
@execution_adapter = executor
|
84
|
+
end
|
85
|
+
|
86
|
+
def graphql_client
|
87
|
+
@graphql_client || DEFAULT_GRAPHQL_CLIENT
|
88
|
+
end
|
89
|
+
|
90
|
+
def graphql_client=(client)
|
91
|
+
@graphql_client = client
|
92
|
+
end
|
93
|
+
|
76
94
|
private
|
77
95
|
|
78
96
|
def initialize_client_cache
|
@@ -53,7 +53,6 @@ module ShopifyAPI
|
|
53
53
|
self.site = nil
|
54
54
|
self.password = nil
|
55
55
|
self.user = nil
|
56
|
-
self.api_version = nil
|
57
56
|
self.headers.delete('X-Shopify-Access-Token')
|
58
57
|
end
|
59
58
|
|
@@ -88,7 +87,7 @@ module ShopifyAPI
|
|
88
87
|
def resource_prefix=(value)
|
89
88
|
@prefix_parameters = nil
|
90
89
|
|
91
|
-
resource_prefix_call = value.gsub(/:\w+/) { |key| "\#{URI.
|
90
|
+
resource_prefix_call = value.gsub(/:\w+/) { |key| "\#{URI::DEFAULT_PARSER.escape options[#{key}].to_s}" }
|
92
91
|
|
93
92
|
silence_warnings do
|
94
93
|
# Redefine the new methods.
|
@@ -5,7 +5,7 @@ module ShopifyAPI
|
|
5
5
|
|
6
6
|
# The default path structure in ActiveResource for delete would result in:
|
7
7
|
# /admin/api/<version>/inventory_levels/#{ inventory_level.id }.json?#{ params }, but since
|
8
|
-
#
|
8
|
+
# InventoryLevels are a second class resource made up of a Where and a What
|
9
9
|
# (Location and InventoryItem), it does not have a resource ID. Here we
|
10
10
|
# redefine element_path to remove the id so HTTP DELETE requests go to
|
11
11
|
# /admin/api/<version>/inventory_levels.json?#{ params } instead.
|
@@ -15,6 +15,15 @@ module ShopifyAPI
|
|
15
15
|
format % prices.min
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
def total_inventory=(new_value)
|
20
|
+
raise_deprecated_inventory_call('total_inventory') unless allow_inventory_params?
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def serializable_hash(options = {})
|
25
|
+
allow_inventory_params? ? super(options) : super(options).except('total_inventory')
|
26
|
+
end
|
18
27
|
|
19
28
|
def collections
|
20
29
|
CustomCollection.find(:all, :params => {:product_id => self.id})
|
@@ -31,5 +40,17 @@ module ShopifyAPI
|
|
31
40
|
def remove_from_collection(collection)
|
32
41
|
collection.remove_product(self)
|
33
42
|
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def raise_deprecated_inventory_call(parameter)
|
47
|
+
raise(ShopifyAPI::ValidationException,
|
48
|
+
"'#{parameter}' is deprecated - see https://help.shopify.com/en/api/guides/inventory-migration-guide",
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def allow_inventory_params?
|
53
|
+
Base.api_version < ApiVersion.find_version('2019-10')
|
54
|
+
end
|
34
55
|
end
|
35
56
|
end
|
@@ -3,12 +3,8 @@ module ShopifyAPI
|
|
3
3
|
include Events
|
4
4
|
include Metafields
|
5
5
|
|
6
|
-
def products
|
7
|
-
|
8
|
-
Product.find(:all, from: "#{self.class.prefix}smart_collections/#{id}/products.json", params: options)
|
9
|
-
else
|
10
|
-
Product.find(:all, params: { collection_id: id })
|
11
|
-
end
|
6
|
+
def products
|
7
|
+
Product.find(:all, params: { collection_id: id })
|
12
8
|
end
|
13
9
|
|
14
10
|
def order(options={})
|
@@ -4,5 +4,42 @@ module ShopifyAPI
|
|
4
4
|
include DisablePrefixCheck
|
5
5
|
|
6
6
|
conditional_prefix :product
|
7
|
+
|
8
|
+
def inventory_quantity_adjustment=(new_value)
|
9
|
+
raise_deprecated_inventory_call('inventory_quantity_adjustment') unless allow_inventory_params?
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def inventory_quantity=(new_value)
|
14
|
+
raise_deprecated_inventory_call('inventory_quantity') unless allow_inventory_params?
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def old_inventory_quantity=(new_value)
|
19
|
+
raise_deprecated_inventory_call('old_inventory_quantity') unless allow_inventory_params?
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def serializable_hash(options = {})
|
24
|
+
if allow_inventory_params?
|
25
|
+
super(options)
|
26
|
+
else
|
27
|
+
super(options).tap do |resource|
|
28
|
+
(resource['variant'] || resource).except!('inventory_quantity', 'old_inventory_quantity')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def raise_deprecated_inventory_call(parameter)
|
36
|
+
raise(ShopifyAPI::ValidationException,
|
37
|
+
"'#{parameter}' is deprecated - see https://help.shopify.com/en/api/guides/inventory-migration-guide",
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def allow_inventory_params?
|
42
|
+
Base.api_version < ApiVersion.find_version('2019-10')
|
43
|
+
end
|
7
44
|
end
|
8
45
|
end
|
data/lib/shopify_api/session.rb
CHANGED
@@ -20,7 +20,7 @@ module ShopifyAPI
|
|
20
20
|
params.each { |k,value| public_send("#{k}=", value) }
|
21
21
|
end
|
22
22
|
|
23
|
-
def temp(domain:, token:, api_version
|
23
|
+
def temp(domain:, token:, api_version: ShopifyAPI::Base.api_version, &block)
|
24
24
|
session = new(domain: domain, token: token, api_version: api_version)
|
25
25
|
|
26
26
|
with_session(session, &block)
|
@@ -28,12 +28,17 @@ module ShopifyAPI
|
|
28
28
|
|
29
29
|
def with_session(session, &_block)
|
30
30
|
original_session = extract_current_session
|
31
|
+
original_user = ShopifyAPI::Base.user
|
32
|
+
original_password = ShopifyAPI::Base.password
|
31
33
|
|
32
34
|
begin
|
35
|
+
ShopifyAPI::Base.clear_session
|
33
36
|
ShopifyAPI::Base.activate_session(session)
|
34
37
|
yield
|
35
38
|
ensure
|
36
39
|
ShopifyAPI::Base.activate_session(original_session)
|
40
|
+
ShopifyAPI::Base.user = original_user
|
41
|
+
ShopifyAPI::Base.password = original_password
|
37
42
|
end
|
38
43
|
end
|
39
44
|
|
@@ -84,7 +89,7 @@ module ShopifyAPI
|
|
84
89
|
end
|
85
90
|
end
|
86
91
|
|
87
|
-
def initialize(domain:, token:, api_version
|
92
|
+
def initialize(domain:, token:, api_version: ShopifyAPI::Base.api_version, extra: {})
|
88
93
|
self.domain = self.class.prepare_domain(domain)
|
89
94
|
self.api_version = api_version
|
90
95
|
self.token = token
|
data/lib/shopify_api/version.rb
CHANGED
data/lib/verify_docs.rb
ADDED
data/shopify_api.gemspec
CHANGED
@@ -19,7 +19,6 @@ Gem::Specification.new do |s|
|
|
19
19
|
]
|
20
20
|
s.files = `git ls-files`.split("\n")
|
21
21
|
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
22
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
22
|
|
24
23
|
s.rdoc_options = ["--charset=UTF-8"]
|
25
24
|
s.summary = %q{ShopifyAPI is a lightweight gem for accessing the Shopify admin REST web services}
|
data/test/base_test.rb
CHANGED
@@ -38,6 +38,22 @@ class BaseTest < Test::Unit::TestCase
|
|
38
38
|
assert_nil ShopifyAPI::Base.site
|
39
39
|
end
|
40
40
|
|
41
|
+
# test to check session reset api version remains the same after session reset
|
42
|
+
test '#clear_session should not change the api_version' do
|
43
|
+
ShopifyAPI::Base.site = "https://zoo:lion@www.zoo.com"
|
44
|
+
|
45
|
+
|
46
|
+
assert_equal "zoo", ShopifyAPI::Base.user
|
47
|
+
assert_equal "lion", ShopifyAPI::Base.password
|
48
|
+
|
49
|
+
ShopifyAPI::Base.clear_session
|
50
|
+
|
51
|
+
assert_nil ShopifyAPI::Base.user
|
52
|
+
assert_nil ShopifyAPI::Base.password
|
53
|
+
assert_nil ShopifyAPI::Base.site
|
54
|
+
assert_equal ShopifyAPI::Base.api_version,@session1.api_version
|
55
|
+
end
|
56
|
+
|
41
57
|
test '#clear_session should clear site and headers from Base' do
|
42
58
|
ShopifyAPI::Base.activate_session @session1
|
43
59
|
ShopifyAPI::Base.clear_session
|
data/test/graphql_test.rb
CHANGED
@@ -143,6 +143,38 @@ class GraphQLTest < Test::Unit::TestCase
|
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
146
|
+
test '#client creates execution adapter based off configured class' do
|
147
|
+
class SuperDuperExecutionAdapter < ShopifyAPI::GraphQL::HTTPClient
|
148
|
+
end
|
149
|
+
|
150
|
+
ShopifyAPI::GraphQL.execution_adapter = SuperDuperExecutionAdapter
|
151
|
+
version_fixtures('unstable') do |dir|
|
152
|
+
ShopifyAPI::Base.api_version = 'unstable'
|
153
|
+
|
154
|
+
ShopifyAPI::GraphQL.initialize_clients
|
155
|
+
assert_instance_of SuperDuperExecutionAdapter, ShopifyAPI::GraphQL.client('unstable').execute
|
156
|
+
end
|
157
|
+
|
158
|
+
ShopifyAPI::GraphQL.execution_adapter = nil
|
159
|
+
end
|
160
|
+
|
161
|
+
test '#client creates client based off configured class' do
|
162
|
+
class SuperDuperClient < ::GraphQL::Client
|
163
|
+
end
|
164
|
+
|
165
|
+
ShopifyAPI::GraphQL.graphql_client = SuperDuperClient
|
166
|
+
version_fixtures('unstable') do |dir|
|
167
|
+
ShopifyAPI::Base.api_version = 'unstable'
|
168
|
+
|
169
|
+
ShopifyAPI::GraphQL.initialize_clients
|
170
|
+
|
171
|
+
assert_instance_of SuperDuperClient, ShopifyAPI::GraphQL.client('unstable')
|
172
|
+
end
|
173
|
+
|
174
|
+
ShopifyAPI::GraphQL.clear_clients
|
175
|
+
ShopifyAPI::GraphQL.graphql_client = nil
|
176
|
+
end
|
177
|
+
|
146
178
|
private
|
147
179
|
|
148
180
|
def version_fixtures(*versions)
|
data/test/product_test.rb
CHANGED
@@ -57,4 +57,43 @@ class ProductTest < Test::Unit::TestCase
|
|
57
57
|
|
58
58
|
assert_equal('100.00 - 199.00', @product.price_range)
|
59
59
|
end
|
60
|
+
|
61
|
+
def test_deprecated_variant_inventory_fields_are_included_in_2019_07
|
62
|
+
ShopifyAPI::Base.api_version = '2019-07'
|
63
|
+
variant = @product.variants.first
|
64
|
+
assert variant.as_json.include?('inventory_quantity')
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_deprecated_variant_inventory_fields_are_removed_in_2020_01
|
68
|
+
ShopifyAPI::Base.api_version = '2020-01'
|
69
|
+
variant = @product.variants.first
|
70
|
+
refute variant.as_json.include?('inventory_quantity')
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_deprecated_inventory_fields_are_removed_in_2020_01
|
74
|
+
ShopifyAPI::Base.api_version = '2020-01'
|
75
|
+
refute @product.as_json.include?('total_inventory')
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_setting_product_total_inventory_passes_in_api_before_2019_10
|
79
|
+
ShopifyAPI::Base.api_version = '2019-07'
|
80
|
+
fake("products/632910392", {:body => load_fixture('product')})
|
81
|
+
@product.total_inventory = 8
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_setting_product_total_inventory_fails_in_2019_10_api
|
85
|
+
ShopifyAPI::Base.api_version = '2019-10'
|
86
|
+
fake("products/632910392", {:body => load_fixture('product')})
|
87
|
+
assert_raises(ShopifyAPI::ValidationException) do
|
88
|
+
@product.total_inventory = 8
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_setting_product_total_inventory_fails_in_the_unstable_api
|
93
|
+
ShopifyAPI::Base.api_version = :unstable
|
94
|
+
fake("products/632910392", {:body => load_fixture('product')})
|
95
|
+
assert_raises(ShopifyAPI::ValidationException) do
|
96
|
+
@product.total_inventory = 8
|
97
|
+
end
|
98
|
+
end
|
60
99
|
end
|
data/test/session_test.rb
CHANGED
@@ -18,7 +18,7 @@ class SessionTest < Test::Unit::TestCase
|
|
18
18
|
assert_not session.valid?
|
19
19
|
end
|
20
20
|
|
21
|
-
test "not be valid without an
|
21
|
+
test "not be valid without an API version" do
|
22
22
|
session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: "any-token", api_version: nil)
|
23
23
|
assert_not session.valid?
|
24
24
|
|
@@ -26,6 +26,19 @@ class SessionTest < Test::Unit::TestCase
|
|
26
26
|
assert_not session.valid?
|
27
27
|
end
|
28
28
|
|
29
|
+
test "default to base API version" do
|
30
|
+
session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: "any-token")
|
31
|
+
assert session.valid?
|
32
|
+
assert_equal session.api_version, ShopifyAPI::Base.api_version
|
33
|
+
end
|
34
|
+
|
35
|
+
test "can override the base API version" do
|
36
|
+
different_api_version = '2020-01'
|
37
|
+
session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: "any-token", api_version: different_api_version)
|
38
|
+
assert session.valid?
|
39
|
+
assert_equal session.api_version, ShopifyAPI::ApiVersion.find_version(different_api_version)
|
40
|
+
end
|
41
|
+
|
29
42
|
test "be valid with any token, any url and version" do
|
30
43
|
session = ShopifyAPI::Session.new(
|
31
44
|
domain: "testshop.myshopify.com",
|
@@ -78,13 +91,17 @@ class SessionTest < Test::Unit::TestCase
|
|
78
91
|
assert_equal "My test secret", ShopifyAPI::Session.secret
|
79
92
|
end
|
80
93
|
|
81
|
-
test "#temp reset ShopifyAPI::Base
|
94
|
+
test "#temp reset ShopifyAPI::Base values to original value" do
|
82
95
|
session1 = ShopifyAPI::Session.new(domain: 'fakeshop.myshopify.com', token: 'token1', api_version: '2019-01')
|
96
|
+
ShopifyAPI::Base.user = 'foo'
|
97
|
+
ShopifyAPI::Base.password = 'bar'
|
83
98
|
ShopifyAPI::Base.activate_session(session1)
|
84
99
|
|
85
100
|
ShopifyAPI::Session.temp(domain: "testshop.myshopify.com", token: "any-token", api_version: :unstable) do
|
86
101
|
@assigned_site = ShopifyAPI::Base.site
|
87
102
|
@assigned_version = ShopifyAPI::Base.api_version
|
103
|
+
@assigned_user = ShopifyAPI::Base.user
|
104
|
+
@assigned_password = ShopifyAPI::Base.password
|
88
105
|
end
|
89
106
|
|
90
107
|
assert_equal('https://testshop.myshopify.com', @assigned_site.to_s)
|
@@ -92,6 +109,31 @@ class SessionTest < Test::Unit::TestCase
|
|
92
109
|
|
93
110
|
assert_equal(ShopifyAPI::ApiVersion.new(handle: :unstable), @assigned_version)
|
94
111
|
assert_equal(ShopifyAPI::ApiVersion.new(handle: '2019-01'), ShopifyAPI::Base.api_version)
|
112
|
+
|
113
|
+
assert_nil(@assigned_user)
|
114
|
+
assert_equal('foo', ShopifyAPI::Base.user)
|
115
|
+
|
116
|
+
assert_nil(@assigned_password)
|
117
|
+
assert_equal('bar', ShopifyAPI::Base.password)
|
118
|
+
end
|
119
|
+
|
120
|
+
test "#temp does not use basic auth values from Base.site" do
|
121
|
+
ShopifyAPI::Base.site = 'https://user:pass@fakeshop.myshopify.com'
|
122
|
+
|
123
|
+
ShopifyAPI::Session.temp(domain: "testshop.myshopify.com", token: "any-token", api_version: :unstable) do
|
124
|
+
@assigned_site = ShopifyAPI::Base.site
|
125
|
+
@assigned_user = ShopifyAPI::Base.user
|
126
|
+
@assigned_password = ShopifyAPI::Base.password
|
127
|
+
end
|
128
|
+
|
129
|
+
assert_equal('https://testshop.myshopify.com', @assigned_site.to_s)
|
130
|
+
assert_equal('https://fakeshop.myshopify.com', ShopifyAPI::Base.site.to_s)
|
131
|
+
|
132
|
+
assert_nil(@assigned_user)
|
133
|
+
assert_equal('user', ShopifyAPI::Base.user)
|
134
|
+
|
135
|
+
assert_nil(@assigned_password)
|
136
|
+
assert_equal('pass', ShopifyAPI::Base.password)
|
95
137
|
end
|
96
138
|
|
97
139
|
test "#with_session activates the session for the duration of the block" do
|
@@ -7,29 +7,4 @@ class SmartCollectionTest < Test::Unit::TestCase
|
|
7
7
|
smart_collection = ShopifyAPI::SmartCollection.create(:title => "Macbooks", :rules => rules)
|
8
8
|
assert_equal 1063001432, smart_collection.id
|
9
9
|
end
|
10
|
-
|
11
|
-
test "Smart Collection get products gets all products in a smart collection" do
|
12
|
-
fake "smart_collections/1063001432", method: :get, status: 200, body: load_fixture('smart_collection')
|
13
|
-
smart_collection = ShopifyAPI::SmartCollection.find(1063001432)
|
14
|
-
|
15
|
-
fake "products.json?collection_id=1063001432",
|
16
|
-
method: :get,
|
17
|
-
status: 200,
|
18
|
-
body:
|
19
|
-
load_fixture('smart_collection_products'),
|
20
|
-
extension: false
|
21
|
-
assert_equal [632910392, 921728736], smart_collection.products.map(&:id)
|
22
|
-
end
|
23
|
-
|
24
|
-
test "Smart Collection get products with only_sorted=only_manual gets only manually sorted products" do
|
25
|
-
fake "smart_collections/1063001432", method: :get, status: 200, body: load_fixture('smart_collection')
|
26
|
-
smart_collection = ShopifyAPI::SmartCollection.find(1063001432)
|
27
|
-
|
28
|
-
fake "smart_collections/1063001432/products.json?only_sorted=only_manual",
|
29
|
-
method: :get,
|
30
|
-
status: 200,
|
31
|
-
body: load_fixture('smart_collection_products'),
|
32
|
-
extension: false
|
33
|
-
assert_equal [632910392, 921728736], smart_collection.products(only_sorted: "only_manual").map(&:id)
|
34
|
-
end
|
35
10
|
end
|