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.
@@ -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 = ::GraphQL::Client.load_schema(schema_file.to_s)
60
- client = ::GraphQL::Client.new(schema: schema, execute: HTTPClient.new(api_version)).tap do |c|
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
@@ -25,7 +25,7 @@ module ShopifyAPI
25
25
  url = parts[0][/<(.*)>/, 1]
26
26
  rel = parts[1][/rel="(.*)"/, 1]&.to_sym
27
27
 
28
- url = URI.parse(url)
28
+ url = URI.parse(url).request_uri
29
29
  LinkHeader.new(url, rel)
30
30
  end
31
31
  end
@@ -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.parser.escape options[#{key}].to_s}" }
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
- # InventroyLevels are a second class resource made up of a Where and a What
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(options = {})
7
- if options.present?
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
@@ -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:, &block)
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:, extra: {})
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
@@ -1,3 +1,3 @@
1
1
  module ShopifyAPI
2
- VERSION = "9.1.0"
2
+ VERSION = "9.2.0"
3
3
  end
@@ -0,0 +1,7 @@
1
+ class VerifyDocs
2
+ def self.call
3
+ readme_content = File.read("README.md").lines[2..-1]
4
+ docs_content = File.read("docs/index.md").lines[4..-1]
5
+ readme_content == docs_content
6
+ end
7
+ end
@@ -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}
@@ -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
@@ -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)
@@ -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
@@ -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 api version" do
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.site to original value" do
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