shopify_api 4.9.0 → 4.13.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/.travis.yml +0 -4
  4. data/CHANGELOG +18 -0
  5. data/README.md +28 -12
  6. data/lib/shopify_api/limits.rb +1 -2
  7. data/lib/shopify_api/resources/access_scope.rb +5 -0
  8. data/lib/shopify_api/resources/api_permission.rb +9 -0
  9. data/lib/shopify_api/resources/asset.rb +8 -8
  10. data/lib/shopify_api/resources/billing_address.rb +1 -1
  11. data/lib/shopify_api/resources/custom_collection.rb +3 -3
  12. data/lib/shopify_api/resources/{customer_invite_message.rb → customer_invite.rb} +0 -0
  13. data/lib/shopify_api/resources/graphql.rb +22 -0
  14. data/lib/shopify_api/resources/image.rb +2 -2
  15. data/lib/shopify_api/resources/inventory_item.rb +6 -0
  16. data/lib/shopify_api/resources/inventory_level.rb +55 -0
  17. data/lib/shopify_api/resources/line_item.rb +9 -1
  18. data/lib/shopify_api/resources/location.rb +4 -0
  19. data/lib/shopify_api/resources/o_auth.rb +8 -0
  20. data/lib/shopify_api/resources/order.rb +7 -2
  21. data/lib/shopify_api/resources/ping.rb +3 -0
  22. data/lib/shopify_api/resources/ping/conversation.rb +18 -0
  23. data/lib/shopify_api/resources/ping/message.rb +9 -0
  24. data/lib/shopify_api/resources/product.rb +4 -4
  25. data/lib/shopify_api/resources/shipping_line.rb +1 -1
  26. data/lib/shopify_api/resources/shop.rb +4 -4
  27. data/lib/shopify_api/version.rb +1 -1
  28. data/shopify_api.gemspec +2 -1
  29. data/test/api_permission_test.rb +9 -0
  30. data/test/fixtures/inventory_level.json +7 -0
  31. data/test/fixtures/inventory_levels.json +24 -0
  32. data/test/fixtures/order_with_properties.json +373 -0
  33. data/test/fixtures/ping/conversation.json +1 -0
  34. data/test/fixtures/ping/message.json +1 -0
  35. data/test/inventory_level_test.rb +59 -0
  36. data/test/location_test.rb +14 -0
  37. data/test/order_test.rb +13 -1
  38. data/test/ping/conversation_test.rb +39 -0
  39. data/test/test_helper.rb +7 -5
  40. data/test/variant_test.rb +4 -1
  41. metadata +37 -10
  42. data/lib/shopify_api/resources/discount.rb +0 -11
  43. data/test/discount_test.rb +0 -52
  44. data/test/fixtures/discount.json +0 -17
  45. data/test/fixtures/discount_disabled.json +0 -17
  46. 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: ef057d0e70073f7c63a076b7f878c1c3afd062d0
4
+ data.tar.gz: 477cf304d0a68dc7e68cfac7e39c9af5617611c5
5
5
  SHA512:
6
- metadata.gz: 4d53f0ae3e229f88800e277b83c4434228b1118f39627794b62868b37a4236deb626c295ee01557e10867b7070dd227a0fc1d1429201ecff203aabb2cb80ae24
7
- data.tar.gz: 259547ebef03ae4f739ea2e7489ce0f01b8d03726037d8648c69321d8f640847872ee7b8ca27ed6c0c27ff7e4f529a04fdf35947a7b7a927a06aa05d9be5fa37
6
+ metadata.gz: c24fa3742f763e0f798a4e2d0eccd37ff2db06ba21e08e09d5853584550398db112354f39b9fd6edc55af2cc8e438f5d04b8e3ade7e704c81e2eda2a8236338f
7
+ data.tar.gz: 9886ecfff88b5b9e7dac1e1e1948fbe3ea3a0c69cbc9f13f601c413dead928bdfc68678f81d06ad74f277631f8048628252f390a37717bf17d9c1b6c4c3e016b
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,21 @@
1
+ == Version 4.12.0
2
+
3
+ * Added support for the GraphQL API
4
+
5
+ == Version 4.11.0
6
+
7
+ * Added `ShopifyAPI::InventoryItem`
8
+ * Added `ShopifyAPI::InventoryLevel`
9
+ * Added `#inventory_levels` method to `ShopifyAPI::Location`
10
+
11
+ == Version 4.10.0
12
+
13
+ * Added `ShopifyAPI::AccessScope`
14
+
15
+ == Version 4.9.1
16
+
17
+ * Fix a bug with custom properties for orders
18
+
1
19
  == Version 4.9.0
2
20
 
3
21
  * Added `ShopifyAPI::PriceRule`
data/README.md CHANGED
@@ -19,7 +19,7 @@ All API usage happens through Shopify applications, created by either shop owner
19
19
  * Shop owners can create applications for themselves through their own admin: https://docs.shopify.com/api/authentication/creating-a-private-app
20
20
  * Shopify Partners create applications through their admin: http://app.shopify.com/services/partners
21
21
 
22
- For more information and detailed documentation about the API visit http://api.shopify.com
22
+ For more information and detailed documentation about the API visit https://developers.shopify.com/
23
23
 
24
24
  #### Ruby version
25
25
 
@@ -39,15 +39,6 @@ Or install via [gem](http://rubygems.org/)
39
39
  gem install shopify_api
40
40
  ```
41
41
 
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
42
  ### Getting Started
52
43
 
53
44
  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 +221,30 @@ gem install shopify_cli
230
221
  shopify-cli help
231
222
  ```
232
223
 
224
+ ## GraphQL
225
+
226
+ This library also supports Shopify's new [GraphQL API](https://help.shopify.com/api/graphql-admin-api)
227
+ via a dependency on the [graphql-client](https://github.com/github/graphql-client) gem.
228
+ The authentication process (steps 1-5 under [Getting Started](#getting-started))
229
+ is identical. Once your session is activated, simply construct a new graphql
230
+ client and use `parse` and `query` as defined by
231
+ [graphql-client](https://github.com/github/graphql-client#defining-queries).
232
+
233
+ ```ruby
234
+ client = ShopifyAPI::GraphQL.new
235
+
236
+ SHOP_NAME_QUERY = client.parse <<-'GRAPHQL'
237
+ {
238
+ shop {
239
+ name
240
+ }
241
+ }
242
+ GRAPHQL
243
+
244
+ result = client.query(SHOP_NAME_QUERY)
245
+ result.data.shop.name
246
+ ```
247
+
233
248
  ## Threadsafety
234
249
 
235
250
  ActiveResource is threadsafe as of version 4.1 (which works with Rails 4.x and above).
@@ -241,12 +256,13 @@ If you were previously using Shopify's [activeresource fork](https://github.com/
241
256
  Download the source code and run:
242
257
 
243
258
  ```bash
244
- rake install
259
+ bundle install
260
+ bundle exec rake test
245
261
  ```
246
262
 
247
263
  ## Additional Resources
248
264
 
249
- API Docs: https://help.shopify.com/api/reference
265
+ API Reference: https://help.shopify.com/api/reference
250
266
 
251
267
  Ask questions on the forums: http://ecommerce.shopify.com/c/shopify-apis-and-technology
252
268
 
@@ -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,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
@@ -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
@@ -3,8 +3,13 @@ module ShopifyAPI
3
3
  include Events
4
4
  include Metafields
5
5
 
6
- def close; load_attributes_from_response(post(:close, {}, only_id)); end
7
- def open; load_attributes_from_response(post(:open, {}, only_id)); end
6
+ def close
7
+ load_attributes_from_response(post(:close, {}, only_id))
8
+ end
9
+
10
+ def open
11
+ load_attributes_from_response(post(:open, {}, only_id))
12
+ end
8
13
 
9
14
  def cancel(options = {})
10
15
  load_attributes_from_response(post(:cancel, {}, options.to_json))
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir.glob("#{File.dirname(__FILE__)}/ping/*").each { |file| require file }
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyAPI
4
+ module Ping
5
+ class Conversation < Base
6
+ self.prefix = "/admin/api/ping-api/v1/"
7
+
8
+ def send_message(message_attrs)
9
+ message = ShopifyAPI::Ping::Message.new(
10
+ message_attrs.merge(conversation_id: id)
11
+ )
12
+
13
+ message.save
14
+ message
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyAPI
4
+ module Ping
5
+ class Message < Base
6
+ self.prefix = "/admin/api/ping-api/v1/conversations/:conversation_id/"
7
+ end
8
+ end
9
+ end