shopify_api 4.9.0 → 5.0.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 (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
@@ -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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyAPI
4
+ class Payment < Base
5
+ self.prefix = '/admin/checkouts/:checkout_id/'
6
+ end
7
+ end
@@ -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
@@ -13,19 +13,19 @@ module ShopifyAPI
13
13
  format % prices.min
14
14
  end
15
15
  end
16
-
16
+
17
17
  def collections
18
18
  CustomCollection.find(:all, :params => {:product_id => self.id})
19
19
  end
20
-
20
+
21
21
  def smart_collections
22
22
  SmartCollection.find(:all, :params => {:product_id => self.id})
23
23
  end
24
-
24
+
25
25
  def add_to_collection(collection)
26
26
  collection.add_product(self)
27
27
  end
28
-
28
+
29
29
  def remove_from_collection(collection)
30
30
  collection.remove_product(self)
31
31
  end
@@ -1,4 +1,4 @@
1
1
  module ShopifyAPI
2
2
  class ShippingLine < Base
3
- end
3
+ end
4
4
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyAPI
4
+ class ShippingRate < Base
5
+ self.prefix = '/admin/checkouts/:checkout_id/'
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  module ShopifyAPI
2
- # Shop object. Use Shop.current to receive
2
+ # Shop object. Use Shop.current to receive
3
3
  # the shop.
4
4
  class Shop < Base
5
5
  def self.current(options={})
@@ -11,13 +11,13 @@ module ShopifyAPI
11
11
  end
12
12
 
13
13
  def add_metafield(metafield)
14
- raise ArgumentError, "You can only add metafields to resource that has been saved" if new?
14
+ raise ArgumentError, "You can only add metafields to resource that has been saved" if new?
15
15
  metafield.save
16
16
  metafield
17
17
  end
18
-
18
+
19
19
  def events
20
20
  Event.find(:all)
21
21
  end
22
- end
22
+ end
23
23
  end
@@ -51,7 +51,7 @@ module ShopifyAPI
51
51
  end
52
52
 
53
53
  def validate_signature(params)
54
- params = params.with_indifferent_access
54
+ params = (params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params).with_indifferent_access
55
55
  return false unless signature = params[:hmac]
56
56
 
57
57
  calculated_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new(), secret, encoded_params_for_signature(params))
@@ -1,3 +1,3 @@
1
1
  module ShopifyAPI
2
- VERSION = "4.9.0"
2
+ VERSION = "5.0.0"
3
3
  end
data/shopify_api.gemspec CHANGED
@@ -23,10 +23,11 @@ Gem::Specification.new do |s|
23
23
  s.summary = %q{ShopifyAPI is a lightweight gem for accessing the Shopify admin REST web services}
24
24
  s.license = "MIT"
25
25
 
26
- s.required_ruby_version = ">= 2.0"
26
+ s.required_ruby_version = ">= 2.1"
27
27
 
28
28
  s.add_runtime_dependency("activeresource", ">= 3.0.0")
29
29
  s.add_runtime_dependency("rack")
30
+ s.add_runtime_dependency("graphql-client")
30
31
 
31
32
  s.add_development_dependency("mocha", ">= 0.9.8")
32
33
  s.add_development_dependency("fakeweb")
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ class AbandonedCheckoutsTest < Test::Unit::TestCase
5
+ def setup
6
+ super
7
+
8
+ @expected_checkouts = JSON.parse(load_fixture('abandoned_checkouts'))['checkouts']
9
+ @expected_checkout_id = JSON.parse(load_fixture('abandoned_checkout'))['checkout']['id']
10
+ end
11
+
12
+ test ":create creates a checkout" do
13
+ fake 'checkouts', method: :post, status: 201, body: load_fixture('abandoned_checkout')
14
+
15
+ checkout = ShopifyAPI::AbandonedCheckout.create
16
+
17
+ assert_equal @expected_checkout_id, checkout.id
18
+ assert_equal true, checkout.attributes.include?(:abandoned_checkout_url)
19
+ end
20
+
21
+ test "get all checkouts indexed by token" do
22
+ fake 'checkouts', method: :get, status: 200, body: load_fixture('abandoned_checkouts')
23
+
24
+ checkouts = ShopifyAPI::AbandonedCheckout.all
25
+
26
+ assert_equal @expected_checkout_id, checkouts.first.id
27
+ assert_equal @expected_checkouts.size, checkouts.size
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ class ApiPermissionTest < Test::Unit::TestCase
5
+ test "revoke access token" do
6
+ fake "api_permissions/current", method: :delete, status: 200, body: "{}"
7
+ assert ShopifyAPI::ApiPermission.destroy
8
+ end
9
+ end
@@ -1,9 +1,72 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  class CheckoutsTest < Test::Unit::TestCase
4
- test "get all should get all orders" do
5
- fake 'checkouts', :method => :get, :status => 200, :body => load_fixture('checkouts')
6
- checkout = ShopifyAPI::Checkout.all
7
- assert_equal 450789469, checkout.first.id
5
+ def setup
6
+ super
7
+
8
+ @expected_checkouts = JSON.parse(load_fixture('checkouts'))['checkouts']
9
+ @expected_checkout_id = JSON.parse(load_fixture('checkout'))['checkout']['token']
10
+ end
11
+
12
+ test ":create creates a checkout" do
13
+ fake 'checkouts', method: :post, status: 201, body: load_fixture('checkout')
14
+
15
+ checkout = ShopifyAPI::Checkout.create
16
+
17
+ assert_equal @expected_checkout_id, checkout.id
18
+ end
19
+
20
+ test "get all checkouts indexed by token" do
21
+ fake 'checkouts', method: :get, status: 200, body: load_fixture('checkouts')
22
+
23
+ checkouts = ShopifyAPI::Checkout.all
24
+
25
+ assert_equal @expected_checkout_id, checkouts.first.id
26
+ assert_equal @expected_checkouts.size, checkouts.size
27
+ end
28
+
29
+ test ":complete completes a checkout" do
30
+ fake "checkouts/#{@expected_checkout_id}", method: :get, status: 200, body: load_fixture('checkout')
31
+
32
+ checkout = ShopifyAPI::Checkout.find(@expected_checkout_id)
33
+
34
+ fake "checkouts/#{@expected_checkout_id}/complete", method: :post, status: 200, body: load_fixture('checkouts')
35
+
36
+ checkout.complete
37
+ end
38
+
39
+ test ":ready? returns true when status is 201" do
40
+ fake "checkouts/#{@expected_checkout_id}", method: :get, status: 201, body: load_fixture('checkout')
41
+ checkout = ShopifyAPI::Checkout.find(@expected_checkout_id)
42
+
43
+ assert_predicate checkout, :ready?
44
+ end
45
+
46
+ test ":ready? returns false when status is 202" do
47
+ fake "checkouts/#{@expected_checkout_id}", method: :get, status: 202, body: load_fixture('checkout')
48
+ checkout = ShopifyAPI::Checkout.find(@expected_checkout_id)
49
+
50
+ refute_predicate checkout, :ready?
51
+ end
52
+
53
+ test ":payments returns payments for a checkout" do
54
+ fake "checkouts/#{@expected_checkout_id}", method: :get, status: 200, body: load_fixture('checkout')
55
+ checkout = ShopifyAPI::Checkout.find(@expected_checkout_id)
56
+
57
+ fake "checkouts/#{@expected_checkout_id}/payments", method: :get, status: 202, body: load_fixture('payments')
58
+
59
+ assert_equal 10.00, checkout.payments.first.amount
60
+ end
61
+
62
+ test ":shipping_rates returns shipping rates for a checkout" do
63
+ fake "checkouts/#{@expected_checkout_id}", method: :get, status: 200, body: load_fixture('checkout')
64
+ checkout = ShopifyAPI::Checkout.find(@expected_checkout_id)
65
+
66
+ fake("checkouts/#{@expected_checkout_id}/shipping_rates",
67
+ method: :get,
68
+ status: 202,
69
+ body: load_fixture('shipping_rates'))
70
+ assert_equal "canada_post-INT.TP.BOGUS-4.00", checkout.shipping_rates.first.id
8
71
  end
9
72
  end
@@ -8,6 +8,7 @@ class LogSubscriberTest < Test::Unit::TestCase
8
8
  super
9
9
  @page = { :page => { :id => 1, :title => 'Shopify API' } }.to_json
10
10
  @ua_header = "\"User-Agent\"=>\"ShopifyAPI/#{ShopifyAPI::VERSION} ActiveResource/#{ActiveResource::VERSION::STRING} Ruby/#{RUBY_VERSION}\""
11
+ @ver_header = "\"X-Shopify-Checkout-Version\"=>\"2016-09-06\""
11
12
 
12
13
  ShopifyAPI::Base.clear_session
13
14
  ShopifyAPI::Base.site = "https://this-is-my-test-shop.myshopify.com/admin"
@@ -28,7 +29,7 @@ class LogSubscriberTest < Test::Unit::TestCase
28
29
  assert_equal 4, @logger.logged(:info).size
29
30
  assert_equal "GET https://this-is-my-test-shop.myshopify.com:443/admin/pages/1.json", @logger.logged(:info)[0]
30
31
  assert_match /\-\-\> 200/, @logger.logged(:info)[1]
31
- assert_equal "Headers: {\"Accept\"=>\"application/json\", #{@ua_header}}", @logger.logged(:info)[2]
32
+ assert_equal "Headers: {\"Accept\"=>\"application/json\", #{@ua_header}, #{@ver_header}}", @logger.logged(:info)[2]
32
33
  assert_match /Response:\n\{\"page\"\:\{((\"id\"\:1)|(\"title\"\:\"Shopify API\")),((\"id\"\:1)|(\"title\"\:\"Shopify API\"))\}\}/, @logger.logged(:info)[3]
33
34
 
34
35
  end
@@ -43,7 +44,7 @@ class LogSubscriberTest < Test::Unit::TestCase
43
44
  assert_equal 4, @logger.logged(:info).size
44
45
  assert_equal "GET https://this-is-my-test-shop.myshopify.com:443/admin/pages/2.json", @logger.logged(:info)[0]
45
46
  assert_match /\-\-\> 404/, @logger.logged(:info)[1]
46
- assert_equal "Headers: {\"Accept\"=>\"application/json\", #{@ua_header}}", @logger.logged(:info)[2]
47
+ assert_equal "Headers: {\"Accept\"=>\"application/json\", #{@ua_header}, #{@ver_header}}", @logger.logged(:info)[2]
47
48
  assert_equal "Response:", @logger.logged(:info)[3]
48
49
  end
49
50
  end
@@ -0,0 +1,184 @@
1
+ {
2
+ "checkout": {
3
+ "buyer_accepts_marketing": false,
4
+ "cart_token": "68778783ad298f1c80c3bafcddeea02f",
5
+ "closed_at": null,
6
+ "completed_at": null,
7
+ "created_at": "2012-10-12T07:05:27-04:00",
8
+ "currency": "USD",
9
+ "email": "bob.norman@hostmail.com",
10
+ "gateway": null,
11
+ "id": 450789469,
12
+ "landing_site": null,
13
+ "note": null,
14
+ "referring_site": null,
15
+ "shipping_lines": [
16
+ {
17
+ "title": "Free Shipping",
18
+ "price": "0.00",
19
+ "code": "Free Shipping",
20
+ "source": "shopify"
21
+ }
22
+ ],
23
+ "source": null,
24
+ "source_identifier": null,
25
+ "source_name": "web",
26
+ "source_url": null,
27
+ "subtotal_price": "398.00",
28
+ "taxes_included": false,
29
+ "token": "2a1ace52255252df566af0faaedfbfa7",
30
+ "total_discounts": "0.00",
31
+ "total_line_items_price": "398.00",
32
+ "total_price": "409.94",
33
+ "total_tax": "11.94",
34
+ "total_weight": 400,
35
+ "updated_at": "2012-10-12T07:05:27-04:00",
36
+ "line_items": [
37
+ {
38
+ "applied_discounts": [
39
+
40
+ ],
41
+ "compare_at_price": null,
42
+ "fulfillment_service": "manual",
43
+ "gift_card": false,
44
+ "grams": 200,
45
+ "id": 49148385,
46
+ "line_price": "199.00",
47
+ "price": "199.00",
48
+ "product_id": 632910392,
49
+ "properties": null,
50
+ "quantity": 1,
51
+ "requires_shipping": true,
52
+ "sku": "IPOD2008RED",
53
+ "tax_lines": [
54
+
55
+ ],
56
+ "taxable": true,
57
+ "title": "IPod Nano - 8GB",
58
+ "variant_id": 49148385,
59
+ "variant_title": "Red",
60
+ "vendor": "Apple"
61
+ },
62
+ {
63
+ "applied_discounts": [
64
+
65
+ ],
66
+ "compare_at_price": null,
67
+ "fulfillment_service": "manual",
68
+ "gift_card": false,
69
+ "grams": 200,
70
+ "id": 808950810,
71
+ "line_price": "199.00",
72
+ "price": "199.00",
73
+ "product_id": 632910392,
74
+ "properties": null,
75
+ "quantity": 1,
76
+ "requires_shipping": true,
77
+ "sku": "IPOD2008PINK",
78
+ "tax_lines": [
79
+
80
+ ],
81
+ "taxable": true,
82
+ "title": "IPod Nano - 8GB",
83
+ "variant_id": 808950810,
84
+ "variant_title": "Pink",
85
+ "vendor": "Apple"
86
+ }
87
+ ],
88
+ "name": "#450789469",
89
+ "note_attributes": [
90
+ {
91
+ "name": "custom engraving",
92
+ "value": "Happy Birthday"
93
+ },
94
+ {
95
+ "name": "colour",
96
+ "value": "green"
97
+ }
98
+ ],
99
+ "discount_codes": [
100
+ {
101
+ "code": "TENOFF",
102
+ "amount": "10.00"
103
+ }
104
+ ],
105
+ "abandoned_checkout_url": "https://checkout.local/orders/690933842/2a1ace52255252df566af0faaedfbfa7?recovered=1",
106
+ "tax_lines": [
107
+ {
108
+ "price": "11.94",
109
+ "rate": 0.06,
110
+ "title": "State Tax"
111
+ }
112
+ ],
113
+ "billing_address": {
114
+ "address1": "Chestnut Street 92",
115
+ "address2": "",
116
+ "city": "Louisville",
117
+ "company": null,
118
+ "country": "United States",
119
+ "first_name": "Bob",
120
+ "last_name": "Norman",
121
+ "latitude": "45.41634",
122
+ "longitude": "-75.6868",
123
+ "phone": "555-625-1199",
124
+ "province": "Kentucky",
125
+ "zip": "40202",
126
+ "name": "Bob Norman",
127
+ "country_code": "US",
128
+ "province_code": "KY"
129
+ },
130
+ "shipping_address": {
131
+ "address1": "Chestnut Street 92",
132
+ "address2": "",
133
+ "city": "Louisville",
134
+ "company": null,
135
+ "country": "United States",
136
+ "first_name": "Bob",
137
+ "last_name": "Norman",
138
+ "latitude": "45.41634",
139
+ "longitude": "-75.6868",
140
+ "phone": "555-625-1199",
141
+ "province": "Kentucky",
142
+ "zip": "40202",
143
+ "name": "Bob Norman",
144
+ "country_code": "US",
145
+ "province_code": "KY"
146
+ },
147
+ "customer": {
148
+ "accepts_marketing": false,
149
+ "created_at": "2014-03-07T16:12:37-05:00",
150
+ "email": "bob.norman@hostmail.com",
151
+ "first_name": "Bob",
152
+ "id": 207119551,
153
+ "last_name": "Norman",
154
+ "last_order_id": null,
155
+ "multipass_identifier": null,
156
+ "note": null,
157
+ "orders_count": 0,
158
+ "state": "disabled",
159
+ "total_spent": "0.00",
160
+ "updated_at": "2014-03-07T16:12:37-05:00",
161
+ "verified_email": true,
162
+ "tags": "",
163
+ "last_order_name": null,
164
+ "default_address": {
165
+ "address1": "Chestnut Street 92",
166
+ "address2": "",
167
+ "city": "Louisville",
168
+ "company": null,
169
+ "country": "United States",
170
+ "first_name": null,
171
+ "id": 207119551,
172
+ "last_name": null,
173
+ "phone": "555-625-1199",
174
+ "province": "Kentucky",
175
+ "zip": "40202",
176
+ "name": null,
177
+ "province_code": "KY",
178
+ "country_code": "US",
179
+ "country_name": "United States",
180
+ "default": true
181
+ }
182
+ }
183
+ }
184
+ }