shopify_api 9.1.0 → 9.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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