shopify_api 4.9.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/.travis.yml +0 -4
  4. data/CHANGELOG +25 -0
  5. data/README.md +36 -14
  6. data/lib/shopify_api/limits.rb +1 -2
  7. data/lib/shopify_api/resources/abandoned_checkout.rb +7 -0
  8. data/lib/shopify_api/resources/access_scope.rb +5 -0
  9. data/lib/shopify_api/resources/api_permission.rb +9 -0
  10. data/lib/shopify_api/resources/asset.rb +8 -8
  11. data/lib/shopify_api/resources/billing_address.rb +1 -1
  12. data/lib/shopify_api/resources/checkout.rb +24 -1
  13. data/lib/shopify_api/resources/custom_collection.rb +3 -3
  14. data/lib/shopify_api/resources/{customer_invite_message.rb → customer_invite.rb} +0 -0
  15. data/lib/shopify_api/resources/graphql.rb +22 -0
  16. data/lib/shopify_api/resources/image.rb +2 -2
  17. data/lib/shopify_api/resources/inventory_item.rb +6 -0
  18. data/lib/shopify_api/resources/inventory_level.rb +55 -0
  19. data/lib/shopify_api/resources/line_item.rb +9 -1
  20. data/lib/shopify_api/resources/location.rb +4 -0
  21. data/lib/shopify_api/resources/o_auth.rb +8 -0
  22. data/lib/shopify_api/resources/order.rb +7 -2
  23. data/lib/shopify_api/resources/payment.rb +7 -0
  24. data/lib/shopify_api/resources/ping.rb +3 -0
  25. data/lib/shopify_api/resources/ping/conversation.rb +18 -0
  26. data/lib/shopify_api/resources/ping/message.rb +9 -0
  27. data/lib/shopify_api/resources/product.rb +4 -4
  28. data/lib/shopify_api/resources/shipping_line.rb +1 -1
  29. data/lib/shopify_api/resources/shipping_rate.rb +7 -0
  30. data/lib/shopify_api/resources/shop.rb +4 -4
  31. data/lib/shopify_api/session.rb +1 -1
  32. data/lib/shopify_api/version.rb +1 -1
  33. data/shopify_api.gemspec +2 -1
  34. data/test/abandoned_checkouts_test.rb +29 -0
  35. data/test/api_permission_test.rb +9 -0
  36. data/test/checkouts_test.rb +67 -4
  37. data/test/detailed_log_subscriber_test.rb +3 -2
  38. data/test/fixtures/abandoned_checkout.json +184 -0
  39. data/test/fixtures/abandoned_checkouts.json +186 -0
  40. data/test/fixtures/checkout.json +160 -0
  41. data/test/fixtures/checkouts.json +25 -49
  42. data/test/fixtures/inventory_level.json +7 -0
  43. data/test/fixtures/inventory_levels.json +24 -0
  44. data/test/fixtures/order_with_properties.json +373 -0
  45. data/test/fixtures/payment.json +7 -0
  46. data/test/fixtures/payments.json +9 -0
  47. data/test/fixtures/ping/conversation.json +1 -0
  48. data/test/fixtures/ping/message.json +1 -0
  49. data/test/fixtures/shipping_rates.json +12 -0
  50. data/test/inventory_level_test.rb +59 -0
  51. data/test/location_test.rb +14 -0
  52. data/test/order_test.rb +13 -1
  53. data/test/payment_test.rb +19 -0
  54. data/test/ping/conversation_test.rb +39 -0
  55. data/test/session_test.rb +11 -11
  56. data/test/shipping_rate_test.rb +17 -0
  57. data/test/test_helper.rb +7 -5
  58. data/test/variant_test.rb +4 -1
  59. metadata +49 -10
  60. data/lib/shopify_api/resources/discount.rb +0 -11
  61. data/test/discount_test.rb +0 -52
  62. data/test/fixtures/discount.json +0 -17
  63. data/test/fixtures/discount_disabled.json +0 -17
  64. data/test/fixtures/discounts.json +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fff80db1065be0d32a1762d81992769fc9cde8b6
4
- data.tar.gz: 2a00b35496dcffdb72d0788c5b4bd18b90bee947
3
+ metadata.gz: 55a5fb52fc6aa176717a301e7a52d015b54a7128
4
+ data.tar.gz: 7318df185fd7cfe2272a3b5078900beea9831eb4
5
5
  SHA512:
6
- metadata.gz: 4d53f0ae3e229f88800e277b83c4434228b1118f39627794b62868b37a4236deb626c295ee01557e10867b7070dd227a0fc1d1429201ecff203aabb2cb80ae24
7
- data.tar.gz: 259547ebef03ae4f739ea2e7489ce0f01b8d03726037d8648c69321d8f640847872ee7b8ca27ed6c0c27ff7e4f529a04fdf35947a7b7a927a06aa05d9be5fa37
6
+ metadata.gz: 8b0eeef0481961159424da00239ac3f9bfbc4709ccc169f592bdd4a385c15ea27a1efe2cd083737c5a36a13d2be8a6687cd5b44b4ad0d69903a1cbcf34440414
7
+ data.tar.gz: 2b0c63f6db528a3c721c2ddf9e30ed5e79976e8570c0ec7a9f03bfde173c90230e5af3979b16f7a49a001ab9ce90ad4d68920d03e291487340215506c9d70653
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ inherit_from:
2
+ - https://shopify.github.io/ruby-style-guide/rubocop.yml
3
+
4
+ AllCops:
5
+ TargetRubyVersion: 2.4
6
+
7
+ Rails:
8
+ Enabled: false
data/.travis.yml CHANGED
@@ -2,7 +2,6 @@ language: ruby
2
2
  sudo: false
3
3
 
4
4
  rvm:
5
- - '2.0'
6
5
  - 2.3.1
7
6
  - 2.4.0-preview1
8
7
 
@@ -15,9 +14,6 @@ gemfile:
15
14
  - Gemfile_ar_master
16
15
 
17
16
  matrix:
18
- exclude:
19
- - rvm: '2.0'
20
- gemfile: Gemfile_ar_master
21
17
  fast_finish: true
22
18
  allow_failures:
23
19
  - rvm: 2.4.0-preview1
data/CHANGELOG CHANGED
@@ -1,3 +1,28 @@
1
+ == Version 5.0.0
2
+
3
+ * Added `ShopifyAPI::AbandonedCheckout`
4
+ * Added support for X-Shopify-Checkout-Version header on `ShopifyAPI::Checkout`
5
+ * Added `ShopifyAPI::ShippingRate`
6
+ * Added support for Checkout::complete endpoint
7
+
8
+ == Version 4.12.0
9
+
10
+ * Added support for the GraphQL API
11
+
12
+ == Version 4.11.0
13
+
14
+ * Added `ShopifyAPI::InventoryItem`
15
+ * Added `ShopifyAPI::InventoryLevel`
16
+ * Added `#inventory_levels` method to `ShopifyAPI::Location`
17
+
18
+ == Version 4.10.0
19
+
20
+ * Added `ShopifyAPI::AccessScope`
21
+
22
+ == Version 4.9.1
23
+
24
+ * Fix a bug with custom properties for orders
25
+
1
26
  == Version 4.9.0
2
27
 
3
28
  * Added `ShopifyAPI::PriceRule`
data/README.md CHANGED
@@ -10,6 +10,13 @@ The Shopify API gem allows Ruby developers to programmatically access the admin
10
10
 
11
11
  The API is implemented as JSON over HTTP using all four verbs (GET/POST/PUT/DELETE). Each resource, like Order, Product, or Collection, has its own URL and is manipulated in isolation. In other words, we’ve tried to make the API follow the REST principles as much as possible.
12
12
 
13
+ ## ⚠️ Breaking change notice for version 5.0.0 ⚠️
14
+ The [Abandoned Checkout API](https://help.shopify.com/en/api/reference/orders/abandoned_checkouts) is now accessed through the `ShopifyAPI::AbandonedCheckout` resource. If you were previously accessing the Abandoned Checkout API through the `ShopifyAPI::Checkout` resource, you will need to update your code after upgrading from version 4.x.x or earlier.
15
+
16
+ Going forward, the `ShopifyAPI::Checkout` resource is used to access the [Checkout API](https://help.shopify.com/en/api/reference/sales_channels/checkout), which can be used to create new checkouts.
17
+
18
+ For more details, [please see this issue](https://github.com/Shopify/shopify_api/issues/471).
19
+
13
20
  ## Usage
14
21
 
15
22
  ### Requirements
@@ -19,7 +26,7 @@ All API usage happens through Shopify applications, created by either shop owner
19
26
  * Shop owners can create applications for themselves through their own admin: https://docs.shopify.com/api/authentication/creating-a-private-app
20
27
  * Shopify Partners create applications through their admin: http://app.shopify.com/services/partners
21
28
 
22
- For more information and detailed documentation about the API visit http://api.shopify.com
29
+ For more information and detailed documentation about the API visit https://developers.shopify.com/
23
30
 
24
31
  #### Ruby version
25
32
 
@@ -39,15 +46,6 @@ Or install via [gem](http://rubygems.org/)
39
46
  gem install shopify_api
40
47
  ```
41
48
 
42
- #### Rails 5
43
-
44
- shopify_api is compatible with Rails 5 but since the latest ActiveResource release (4.1) is locked on Rails 4.x, you'll need to use the unreleased master version:
45
-
46
- ```ruby
47
- gem 'shopify_api'
48
- gem 'activeresource', github: 'rails/activeresource'
49
- ```
50
-
51
49
  ### Getting Started
52
50
 
53
51
  ShopifyAPI uses ActiveResource to communicate with the REST web service. ActiveResource has to be configured with a fully authorized URL of a particular store first. To obtain that URL you can follow these steps:
@@ -230,6 +228,30 @@ gem install shopify_cli
230
228
  shopify-cli help
231
229
  ```
232
230
 
231
+ ## GraphQL
232
+
233
+ This library also supports Shopify's new [GraphQL API](https://help.shopify.com/api/graphql-admin-api)
234
+ via a dependency on the [graphql-client](https://github.com/github/graphql-client) gem.
235
+ The authentication process (steps 1-5 under [Getting Started](#getting-started))
236
+ is identical. Once your session is activated, simply construct a new graphql
237
+ client and use `parse` and `query` as defined by
238
+ [graphql-client](https://github.com/github/graphql-client#defining-queries).
239
+
240
+ ```ruby
241
+ client = ShopifyAPI::GraphQL.new
242
+
243
+ SHOP_NAME_QUERY = client.parse <<-'GRAPHQL'
244
+ {
245
+ shop {
246
+ name
247
+ }
248
+ }
249
+ GRAPHQL
250
+
251
+ result = client.query(SHOP_NAME_QUERY)
252
+ result.data.shop.name
253
+ ```
254
+
233
255
  ## Threadsafety
234
256
 
235
257
  ActiveResource is threadsafe as of version 4.1 (which works with Rails 4.x and above).
@@ -241,14 +263,14 @@ If you were previously using Shopify's [activeresource fork](https://github.com/
241
263
  Download the source code and run:
242
264
 
243
265
  ```bash
244
- rake install
266
+ bundle install
267
+ bundle exec rake test
245
268
  ```
246
269
 
247
270
  ## Additional Resources
248
271
 
249
- API Docs: https://help.shopify.com/api/reference
250
-
251
- Ask questions on the forums: http://ecommerce.shopify.com/c/shopify-apis-and-technology
272
+ * [API Reference](https://help.shopify.com/api/reference)
273
+ * [Ask questions on the forums](http://ecommerce.shopify.com/c/shopify-apis-and-technology)
252
274
 
253
275
  ## Copyright
254
276
 
@@ -39,8 +39,7 @@ module ShopifyAPI
39
39
  # @return {Integer}
40
40
  #
41
41
  def credit_limit(scope=:shop)
42
- @api_credit_limit ||= {}
43
- @api_credit_limit[scope] ||= api_credit_limit_param(scope).pop.to_i - 1
42
+ api_credit_limit_param(scope).pop.to_i - 1
44
43
  end
45
44
  alias_method :call_limit, :credit_limit
46
45
 
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyAPI
4
+ class AbandonedCheckout < Base
5
+ self.element_name = "checkout"
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module ShopifyAPI
2
+ class AccessScope < Base
3
+ self.prefix = '/admin/oauth/'
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyAPI
4
+ class ApiPermission < Base
5
+ def self.destroy
6
+ delete(:current)
7
+ end
8
+ end
9
+ end
@@ -42,7 +42,7 @@ module ShopifyAPI
42
42
  prefix_options, query_options = split_options(prefix_options) if query_options.nil?
43
43
  "#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
44
44
  end
45
-
45
+
46
46
  # find an asset by key:
47
47
  # ShopifyAPI::Asset.find('layout/theme.liquid', :params => {:theme_id => 99})
48
48
  def self.find(*args)
@@ -57,7 +57,7 @@ module ShopifyAPI
57
57
  resource
58
58
  end
59
59
  end
60
-
60
+
61
61
  # For text assets, Shopify returns the data in the 'value' attribute.
62
62
  # For binary assets, the data is base-64-encoded and returned in the
63
63
  # 'attachment' attribute. This accessor returns the data in both cases.
@@ -65,28 +65,28 @@ module ShopifyAPI
65
65
  attributes['value'] ||
66
66
  (attributes['attachment'] ? Base64.decode64(attributes['attachment']) : nil)
67
67
  end
68
-
68
+
69
69
  def attach(data)
70
70
  self.attachment = Base64.encode64(data)
71
71
  end
72
-
72
+
73
73
  def destroy
74
74
  connection.delete(element_path(prefix_options.merge(:asset => {:key => key})), self.class.headers)
75
75
  end
76
-
76
+
77
77
  def new?
78
78
  false
79
79
  end
80
-
80
+
81
81
  def method_missing(method_symbol, *arguments) #:nodoc:
82
82
  if %w{value= attachment= src= source_key=}.include?(method_symbol)
83
83
  wipe_value_attributes
84
84
  end
85
85
  super
86
86
  end
87
-
87
+
88
88
  private
89
-
89
+
90
90
  def wipe_value_attributes
91
91
  %w{value attachment src source_key}.each do |attr|
92
92
  attributes.delete(attr)
@@ -1,4 +1,4 @@
1
1
  module ShopifyAPI
2
2
  class BillingAddress < Base
3
- end
3
+ end
4
4
  end
@@ -1,4 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ShopifyAPI
2
4
  class Checkout < Base
5
+ self.primary_key = :token
6
+ headers['X-Shopify-Checkout-Version'] = '2016-09-06'
7
+
8
+ def complete
9
+ post(:complete)
10
+ end
11
+
12
+ def ready?
13
+ return false unless persisted?
14
+
15
+ reload
16
+ [200, 201].include?(ShopifyAPI::Base.connection.response.code.to_i)
17
+ end
18
+
19
+ def payments
20
+ Payment.find(:all, params: { checkout_id: id })
21
+ end
22
+
23
+ def shipping_rates
24
+ ShippingRate.find(:all, params: { checkout_id: id })
25
+ end
3
26
  end
4
- end
27
+ end
@@ -6,14 +6,14 @@ module ShopifyAPI
6
6
  def products
7
7
  Product.find(:all, :params => {:collection_id => self.id})
8
8
  end
9
-
9
+
10
10
  def add_product(product)
11
11
  Collect.create(:collection_id => self.id, :product_id => product.id)
12
12
  end
13
-
13
+
14
14
  def remove_product(product)
15
15
  collect = Collect.find(:first, :params => {:collection_id => self.id, :product_id => product.id})
16
16
  collect.destroy if collect
17
17
  end
18
- end
18
+ end
19
19
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ require 'graphql/client'
3
+ require 'graphql/client/http'
4
+
5
+ module ShopifyAPI
6
+ # GraphQL API.
7
+ class GraphQL
8
+ def initialize
9
+ uri = Base.site.dup
10
+ uri.path = '/admin/api/graphql.json'
11
+ @http = ::GraphQL::Client::HTTP.new(uri.to_s) do
12
+ define_method(:headers) do |_context|
13
+ Base.headers
14
+ end
15
+ end
16
+ @schema = ::GraphQL::Client.load_schema(@http)
17
+ @client = ::GraphQL::Client.new(schema: @schema, execute: @http)
18
+ end
19
+
20
+ delegate :parse, :query, to: :@client
21
+ end
22
+ end
@@ -1,13 +1,13 @@
1
1
  module ShopifyAPI
2
2
  class Image < Base
3
3
  init_prefix :product
4
-
4
+
5
5
  # generate a method for each possible image variant
6
6
  [:pico, :icon, :thumb, :small, :compact, :medium, :large, :grande, :original].each do |m|
7
7
  reg_exp_match = "/\\1_#{m}.\\2"
8
8
  define_method(m) { src.gsub(/\/(.*)\.(\w{2,4})/, reg_exp_match) }
9
9
  end
10
-
10
+
11
11
  def attach_image(data, filename = nil)
12
12
  attributes['attachment'] = Base64.encode64(data)
13
13
  attributes['filename'] = filename unless filename.nil?
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyAPI
4
+ class InventoryItem < Base
5
+ end
6
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyAPI
4
+ class InventoryLevel < Base
5
+
6
+ # The default path structure in ActiveResource for delete would result in:
7
+ # /admin/inventory_levels/#{ inventory_level.id }.json?#{ params }, but since
8
+ # InventroyLevels are a second class resource made up of a Where and a What
9
+ # (Location and InventoryItem), it does not have a resource ID. Here we
10
+ # redefine element_path to remove the id so HTTP DELETE requests go to
11
+ # /admin/inventory_levels.json?#{ params } instead.
12
+ #
13
+ def self.element_path(prefix_options = {}, query_options = nil)
14
+ prefix_options, query_options = split_options(prefix_options) if query_options.nil?
15
+ "#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
16
+ end
17
+
18
+ def destroy
19
+ load_attributes_from_response(
20
+ self.class.delete('/', location_id: location_id, inventory_item_id: inventory_item_id)
21
+ )
22
+ end
23
+
24
+ def connect(relocate_if_necessary: nil)
25
+ body = { location_id: location_id, inventory_item_id: inventory_item_id }
26
+ body[:relocate_if_necessary] = relocate_if_necessary unless relocate_if_necessary.nil?
27
+ load_attributes_from_response(
28
+ self.class.post(:connect, {}, body.to_json)
29
+ )
30
+ end
31
+
32
+ def set(new_available, disconnect_if_necessary: nil)
33
+ body = {
34
+ location_id: location_id,
35
+ inventory_item_id: inventory_item_id,
36
+ available: new_available
37
+ }
38
+ body[:disconnect_if_necessary] = disconnect_if_necessary unless disconnect_if_necessary.nil?
39
+ load_attributes_from_response(
40
+ self.class.post(:set, {}, body.to_json)
41
+ )
42
+ end
43
+
44
+ def adjust(available_adjustment)
45
+ body = {
46
+ location_id: location_id,
47
+ inventory_item_id: inventory_item_id,
48
+ available_adjustment: available_adjustment
49
+ }
50
+ load_attributes_from_response(
51
+ self.class.post(:adjust, {}, body.to_json)
52
+ )
53
+ end
54
+ end
55
+ end
@@ -1,6 +1,14 @@
1
1
  module ShopifyAPI
2
- class LineItem < Base
2
+ class LineItem < Base
3
3
  class Property < Base
4
+ def initialize(*args)
5
+ attributes = args[0] || {}
6
+ persisted = args[1] || false
7
+ super
8
+ rescue NameError
9
+ attributes = attributes.to_hash
10
+ self
11
+ end
4
12
  end
5
13
  end
6
14
  end
@@ -1,4 +1,8 @@
1
1
  module ShopifyAPI
2
2
  class Location < Base
3
+
4
+ def inventory_levels
5
+ ShopifyAPI::InventoryLevel.find(:all, from: "/admin/locations/#{id}/inventory_levels.json")
6
+ end
3
7
  end
4
8
  end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This resource is deprecated and will be removed in a future version of this gem.
4
+ # Use ShopifyAPI::ApiPermission.destroy instead
5
+
1
6
  module ShopifyAPI
2
7
  class OAuth < Base
3
8
  self.collection_name = 'oauth'
4
9
 
5
10
  def self.revoke
11
+ warn '[DEPRECATED] ShopifyAPI::OAuth#revoke is deprecated and will be removed in a future version. ' \
12
+ 'Use ShopifyAPI::ApiPermission#destroy instead.'
13
+
6
14
  delete(:revoke)
7
15
  end
8
16
  end