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
@@ -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
+ }