solidus_api 2.9.5 → 2.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -2
  3. data/app/controllers/spree/api/addresses_controller.rb +1 -1
  4. data/app/controllers/spree/api/base_controller.rb +10 -10
  5. data/app/controllers/spree/api/checkouts_controller.rb +21 -8
  6. data/app/controllers/spree/api/credit_cards_controller.rb +1 -1
  7. data/app/controllers/spree/api/images_controller.rb +1 -1
  8. data/app/controllers/spree/api/inventory_units_controller.rb +1 -1
  9. data/app/controllers/spree/api/option_types_controller.rb +1 -1
  10. data/app/controllers/spree/api/option_values_controller.rb +1 -1
  11. data/app/controllers/spree/api/orders_controller.rb +17 -6
  12. data/app/controllers/spree/api/payments_controller.rb +1 -1
  13. data/app/controllers/spree/api/product_properties_controller.rb +1 -1
  14. data/app/controllers/spree/api/properties_controller.rb +1 -1
  15. data/app/controllers/spree/api/resource_controller.rb +1 -1
  16. data/app/controllers/spree/api/return_authorizations_controller.rb +1 -1
  17. data/app/controllers/spree/api/shipments_controller.rb +1 -1
  18. data/app/controllers/spree/api/stock_items_controller.rb +1 -1
  19. data/app/controllers/spree/api/stock_locations_controller.rb +1 -1
  20. data/app/controllers/spree/api/stores_controller.rb +1 -1
  21. data/app/controllers/spree/api/taxonomies_controller.rb +4 -2
  22. data/app/controllers/spree/api/taxons_controller.rb +1 -1
  23. data/app/controllers/spree/api/variants_controller.rb +1 -1
  24. data/app/controllers/spree/api/zones_controller.rb +1 -1
  25. data/app/helpers/spree/api/api_helpers.rb +2 -2
  26. data/app/views/spree/api/images/_image.json.jbuilder +2 -2
  27. data/app/views/spree/api/orders/_order.json.jbuilder +1 -1
  28. data/app/views/spree/api/shared/_pagination.json.jbuilder +1 -1
  29. data/lib/spree/api.rb +0 -7
  30. data/lib/spree/api/config.rb +9 -0
  31. data/lib/spree/api/engine.rb +4 -4
  32. data/lib/spree/api/responders.rb +3 -2
  33. data/lib/spree/api/responders/{rabl_template.rb → jbuilder_template.rb} +3 -1
  34. data/{app/models → lib}/spree/api_configuration.rb +0 -0
  35. data/openapi/.stoplight.yml +7 -0
  36. data/openapi/api.oas2.yml +6108 -0
  37. data/openapi/authentication.md +25 -0
  38. data/openapi/checkout-flow.md +50 -0
  39. data/openapi/errors.md +3 -0
  40. data/openapi/lint.yml +1 -0
  41. data/openapi/main.hub.yml +65 -0
  42. data/openapi/pagination.md +7 -0
  43. data/openapi/theme.css +0 -0
  44. data/solidus_api.gemspec +1 -1
  45. data/spec/controllers/spree/api/resource_controller_spec.rb +4 -4
  46. data/spec/lib/spree_api_responders_spec.rb +10 -0
  47. data/spec/requests/api/address_books_spec.rb +2 -2
  48. data/spec/requests/{rabl_cache_spec.rb → jbuilder_cache_spec.rb} +2 -2
  49. data/spec/requests/spree/api/checkouts_controller_spec.rb +35 -7
  50. data/spec/requests/spree/api/classifications_controller_spec.rb +1 -1
  51. data/spec/requests/spree/api/option_types_controller_spec.rb +2 -2
  52. data/spec/requests/spree/api/option_values_controller_spec.rb +2 -2
  53. data/spec/requests/spree/api/orders_controller_spec.rb +38 -15
  54. data/spec/requests/spree/api/payments_controller_spec.rb +3 -3
  55. data/spec/requests/spree/api/products_controller_spec.rb +6 -6
  56. data/spec/requests/spree/api/stock_items_controller_spec.rb +5 -5
  57. data/spec/requests/spree/api/stock_locations_controller_spec.rb +4 -4
  58. data/spec/requests/spree/api/taxons_controller_spec.rb +2 -2
  59. metadata +21 -9
@@ -0,0 +1,25 @@
1
+ # Authentication
2
+
3
+ The Solidus API provides two means of authentication: one is through your Solidus user's API key, while the other is through an order's guest token.
4
+
5
+ ### API key
6
+
7
+ You can use your API key to access all resources in the API. The API key must be passed in the `Authorization` header in the following form:
8
+
9
+ ```
10
+ Authorization: Bearer API_KEY
11
+ ```
12
+
13
+ By default, API keys are only generated for admins, but you can easily customize Solidus to generate them for all users, which is useful for instance if you want users to be able to sign in and manage their profile via the API.
14
+
15
+ ### Order token
16
+
17
+ For allowing guests to manage their cart and place their order, you can use the order's guest token. This token is contained in the `guest_token` property of the order, and it allows you to perform certain checkout-related operations on the order such as managing line items, completing the checkout flow etc.
18
+
19
+ The order token must be passed in the `X-Spree-Order-Token` header in the following form:
20
+
21
+ ```
22
+ X-Spree-Order-Token: ORDER_TOKEN
23
+ ```
24
+
25
+ If you are already providing an API key, you don't need to also provide the order token (although you may do so).
@@ -0,0 +1,50 @@
1
+ # Checkout flow
2
+
3
+ Given the amount of endpoints at your disposal, it can be difficult to understand how to string the right API calls together to perform a full checkout flow. This document explains how to perform a full checkout flow.
4
+
5
+ ## 1. Create an order
6
+
7
+ The first step is to create an order via `POST /orders`. You should save the number of the order that is created as well as the guest token, in case you are not authenticating with an API key.
8
+
9
+ ## 2. Fill the cart
10
+
11
+ Once you have an order, you can begin filling your cart. Here are some endpoints you can use for that:
12
+
13
+ - `POST /checkouts/:order_number/line_items`
14
+ - `PATCH /checkouts/:order_number/line_items/:id`
15
+ - `DELETE /checkouts/:order_number/line_items/:id`
16
+ - `PUT /orders/:order_number/empty`
17
+
18
+ ## 3. (Optional) Apply a coupon code
19
+
20
+ You can also apply a coupon code on the order via `POST /orders/:order_number/coupon_codes`.
21
+
22
+ ## 4. Start the checkout flow
23
+
24
+ When you are ready to start the checkout flow, you can call `PUT /checkouts/:order_number/next` to transition the order from the `cart` to the `address` state.
25
+
26
+ ## 5. Enter billing and shipping addresses
27
+
28
+ To enter the billing and shipping addresses, use the `PATCH /checkouts/:order_number` endpoint.
29
+
30
+ Once again, call `PUT /checkouts/:order_number/next` to transition the order from the `address` to the `shipping` state.
31
+
32
+ ## 6. Select a shipping method
33
+
34
+ You can retrieve the available shipping methods, along with their rates, via `GET /shipments/:shipment_number/estimated_rates`. This allows you to let your user choose the shipping method they prefer.
35
+
36
+ When you want to select a shipping method, call `PUT /shipments/:shipment_number/select_shipping_method`.
37
+
38
+ Finally, call `PUT /checkouts/:order_number/next` to transition the order from the `shipping` to the `payment` state.
39
+
40
+ ## 7. Enter payment details
41
+
42
+ To create a payment, call `POST /orders/:order_number/payments`.
43
+
44
+ Now call `PUT /checkouts/:order_number/next` to transition the order from the `payment` to the `confirm` state.
45
+
46
+ ## 8. Complete the order
47
+
48
+ At this point, you should show the user a summary of their cart and ask them to confirm they want to place the order.
49
+
50
+ When they confirm, call `PUT /checkouts/:order_number/complete` to complete the checkout flow and place the order!
@@ -0,0 +1,3 @@
1
+ # Errors
2
+
3
+ Error responses for each endpoint are documented. When interacting with an endpoint, make sure to handle all possible errors appropriately.
@@ -0,0 +1 @@
1
+ rules: {}
@@ -0,0 +1,65 @@
1
+ title: Solidus API
2
+ logo: 'https://next.stoplight.io/images/mark-light-bg.png'
3
+ header:
4
+ nav:
5
+ left: []
6
+ right: []
7
+ pages:
8
+ /:
9
+ title: Welcome
10
+ data:
11
+ blocks:
12
+ - type: text
13
+ data: >-
14
+ # Solidus API
15
+
16
+
17
+ Welcome! This is the documentation for the
18
+ [Solidus](https://solidus.io) REST API.
19
+
20
+
21
+ This documentation refers to a stock installation of Solidus.
22
+ However, every store may customize their API in any number of ways,
23
+ so make sure to ensure the store you are working with conforms to
24
+ this documentation or refer to the store's own documentation for
25
+ interacting with the API.
26
+
27
+
28
+ Endpoints are grouped by the logical resource they interact with.
29
+ Note that some of the endpoints are duplicated, since the same
30
+ resource may be accessed at the root level or as the child of
31
+ another resource (e.g. you may access all variants or the variants
32
+ that belong to a specific product).
33
+ - type: text
34
+ data: ''
35
+ children:
36
+ - title: Authentication
37
+ route:
38
+ path: /authentication
39
+ data:
40
+ $ref: ./authentication.md
41
+ - title: Pagination
42
+ route:
43
+ path: /pagination
44
+ data:
45
+ $ref: ./pagination.md
46
+ - title: Errors
47
+ route:
48
+ path: /errors
49
+ data:
50
+ $ref: ./errors.md
51
+ - title: Checkout Flow
52
+ route:
53
+ path: /checkout-flow
54
+ data:
55
+ $ref: ./checkout-flow.md
56
+ config:
57
+ sidebar:
58
+ token: ''
59
+ - title: API Reference
60
+ config:
61
+ includeDownloadLink: true
62
+ route:
63
+ path: /api-reference
64
+ data:
65
+ $ref: ./api.oas2.yml
@@ -0,0 +1,7 @@
1
+ # Pagination
2
+
3
+ Most endpoints that return a collection are paginated. A paginated response contains metadata about the current page at the root level and the resources in the current page in a child key named after the resource (e.g. `orders`).
4
+
5
+ You can pass the `page` and `per_page` parameters to set the current page and the desired number of items per page. Note that the default and the maximum number of items per page is decided at the application level.
6
+
7
+ All pagination metadata is documented in the individual API endpoints, so take a look there if you're unsure what data you can expect.
File without changes
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
18
18
  gem.require_paths = ["lib"]
19
19
  gem.version = Spree.solidus_version
20
20
 
21
- gem.required_ruby_version = '>= 2.2.2'
21
+ gem.required_ruby_version = '>= 2.4.0'
22
22
  gem.required_rubygems_version = '>= 1.8.23'
23
23
 
24
24
  gem.add_dependency 'jbuilder', '~> 2.8'
@@ -25,10 +25,10 @@ module Spree
25
25
  end
26
26
 
27
27
  with_model 'Widget', scope: :all do
28
- table do |t|
29
- t.string :name
30
- t.integer :position
31
- t.timestamps null: false
28
+ table do |widget|
29
+ widget.string :name
30
+ widget.integer :position
31
+ widget.timestamps null: false
32
32
  end
33
33
 
34
34
  model do
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe "Spree Api Responders" do
6
+ it "RablTemplate is deprecated Use JbuilderTemplate" do
7
+ warning_message = /DEPRECATION WARNING: RablTemplate is deprecated! Use JbuilderTemplate instead/
8
+ expect{ Spree::Api::Responders::RablTemplate.methods }.to output(warning_message).to_stderr
9
+ end
10
+ end
@@ -80,7 +80,7 @@ module Spree
80
80
  user_address = UserAddress.last
81
81
 
82
82
  expect(response.status).to eq(200)
83
- update_target_ids = JSON.parse(response.body).select { |a| a['update_target'] }.map { |a| a['id'] }
83
+ update_target_ids = JSON.parse(response.body).select { |target| target['update_target'] }.map { |location| location['id'] }
84
84
  expect(update_target_ids).to eq([user_address.address_id])
85
85
  end
86
86
  end
@@ -97,7 +97,7 @@ module Spree
97
97
  }.to_not change { UserAddress.count }
98
98
 
99
99
  expect(response.status).to eq(200)
100
- update_target_ids = JSON.parse(response.body).select { |a| a['update_target'] }.map { |a| a['id'] }
100
+ update_target_ids = JSON.parse(response.body).select { |target| target['update_target'] }.map { |location| location['id'] }
101
101
  expect(update_target_ids).to eq([address.id])
102
102
  end
103
103
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe "Rabl Cache", type: :request, caching: true do
5
+ describe "Jbuilder Cache", type: :request, caching: true do
6
6
  let!(:user) { create(:admin_user) }
7
7
 
8
8
  before do
@@ -11,7 +11,7 @@ describe "Rabl Cache", type: :request, caching: true do
11
11
  expect(Spree::Product.count).to eq(1)
12
12
  end
13
13
 
14
- it "doesn't create a cache key collision for models with different rabl templates" do
14
+ it "doesn't create a cache key collision for models with different jbuilder templates" do
15
15
  get "/api/variants", params: { token: user.spree_api_key }
16
16
  expect(response.status).to eq(200)
17
17
 
@@ -132,9 +132,9 @@ module Spree
132
132
  put spree.api_checkout_path(order.to_param), params: { order_token: order.guest_token, order: { shipments_attributes: { "0" => { selected_shipping_rate_id: shipping_rate.id, id: shipment.id } } } }
133
133
  expect(response.status).to eq(200)
134
134
  # Find the correct shipment...
135
- json_shipment = json_response['shipments'].detect { |s| s["id"] == shipment.id }
135
+ json_shipment = json_response['shipments'].detect { |value| value["id"] == shipment.id }
136
136
  # Find the correct shipping rate for that shipment...
137
- json_shipping_rate = json_shipment['shipping_rates'].detect { |sr| sr["id"] == shipping_rate.id }
137
+ json_shipping_rate = json_shipment['shipping_rates'].detect { |value| value["id"] == shipping_rate.id }
138
138
  # ... And finally ensure that it's selected
139
139
  expect(json_shipping_rate['selected']).to be true
140
140
  # Order should automatically transfer to payment because all criteria are met
@@ -172,6 +172,7 @@ module Spree
172
172
  end
173
173
 
174
174
  describe 'setting the payment amount' do
175
+ let(:order) { create(:order_with_line_items, state: :payment) }
175
176
  let(:params) do
176
177
  {
177
178
  order_token: order.guest_token,
@@ -322,17 +323,44 @@ module Spree
322
323
  end
323
324
  end
324
325
 
326
+ it "cannot update attributes of another step" do
327
+ order.update_column(:state, "payment")
328
+
329
+ params = {
330
+ order_token: order.guest_token,
331
+ order: {
332
+ payments_attributes: [
333
+ {
334
+ payment_method_id: @payment_method.id.to_s,
335
+ source_attributes: attributes_for(:credit_card)
336
+ }
337
+ ],
338
+ ship_address_attributes: {
339
+ zipcode: 'MALICIOUS ZIPCODE'
340
+ }
341
+ }
342
+ }
343
+ expect do
344
+ put spree.api_checkout_path(order), params: params
345
+ end.not_to change { order.reload.ship_address.zipcode }
346
+ expect(response.status).to eq(200)
347
+ end
348
+
325
349
  it "returns the order if the order is already complete" do
326
350
  order.update_columns(completed_at: Time.current, state: 'complete')
327
351
  put spree.api_checkout_path(order.to_param), params: { order_token: order.guest_token }
328
352
  assert_unauthorized!
329
353
  end
330
354
 
331
- # Regression test for https://github.com/spree/spree/issues/3784
332
- it "can update the special instructions for an order" do
333
- instructions = "Don't drop it. (Please)"
334
- put spree.api_checkout_path(order.to_param), params: { order_token: order.guest_token, order: { special_instructions: instructions } }
335
- expect(json_response['special_instructions']).to eql(instructions)
355
+ context "in delivery state" do
356
+ let(:order) { create(:order_with_line_items, state: :delivery) }
357
+
358
+ # Regression test for https://github.com/spree/spree/issues/3784
359
+ it "can update the special instructions for an order" do
360
+ instructions = "Don't drop it. (Please)"
361
+ put spree.api_checkout_path(order.to_param), params: { order_token: order.guest_token, order: { special_instructions: instructions } }
362
+ expect(json_response['special_instructions']).to eql(instructions)
363
+ end
336
364
  end
337
365
 
338
366
  context "as an admin" do
@@ -39,7 +39,7 @@ module Spree
39
39
  end
40
40
 
41
41
  it "should touch the taxon" do
42
- taxon.update_attributes(updated_at: Time.current - 10.seconds)
42
+ taxon.update(updated_at: Time.current - 10.seconds)
43
43
  taxon_last_updated_at = taxon.updated_at
44
44
  put spree.api_classifications_path, params: { taxon_id: taxon.id, product_id: last_product.id, position: 0 }
45
45
  taxon.reload
@@ -34,9 +34,9 @@ module Spree
34
34
  end
35
35
 
36
36
  it "can retrieve a list of specific option types" do
37
- option_type_1 = create(:option_type)
37
+ option_type_one = create(:option_type)
38
38
  create(:option_type)
39
- get spree.api_option_types_path, params: { ids: "#{option_type.id},#{option_type_1.id}" }
39
+ get spree.api_option_types_path, params: { ids: "#{option_type.id},#{option_type_one.id}" }
40
40
  expect(json_response.count).to eq(2)
41
41
 
42
42
  check_option_values(json_response.first["option_values"])
@@ -48,9 +48,9 @@ module Spree
48
48
  end
49
49
 
50
50
  it "can retrieve a list of option types" do
51
- option_value_1 = create(:option_value, option_type: option_type)
51
+ option_value_one = create(:option_value, option_type: option_type)
52
52
  create(:option_value, option_type: option_type)
53
- get spree.api_option_values_path, params: { ids: [option_value.id, option_value_1.id] }
53
+ get spree.api_option_values_path, params: { ids: [option_value.id, option_value_one.id] }
54
54
  expect(json_response.count).to eq(2)
55
55
  end
56
56
 
@@ -156,6 +156,7 @@ module Spree
156
156
  end
157
157
 
158
158
  context 'creating payment' do
159
+ let!(:order) { create(:order_with_line_items) }
159
160
  let(:order_params) { super().merge(payments_attributes: [{ payment_method_id: payment_method.id }]) }
160
161
 
161
162
  context "with allowed payment method" do
@@ -166,6 +167,28 @@ module Spree
166
167
  subject
167
168
  }.to change { Spree::Payment.count }.by(1)
168
169
  end
170
+
171
+ context 'trying to change the address' do
172
+ let(:order_params) do
173
+ super().merge(
174
+ ship_address_attributes: {
175
+ zipcode: '90100'
176
+ }
177
+ )
178
+ end
179
+
180
+ it 'changes the address' do
181
+ expect {
182
+ subject
183
+ }.to change { order.reload.ship_address.zipcode }
184
+ end
185
+
186
+ it 'invalidates the shipments' do
187
+ expect {
188
+ subject
189
+ }.to change { order.reload.shipments }.to([])
190
+ end
191
+ end
169
192
  end
170
193
 
171
194
  context "with disallowed payment method" do
@@ -246,12 +269,12 @@ module Spree
246
269
  it "returns orders in reverse chronological order by completed_at" do
247
270
  order.update_columns completed_at: Time.current, created_at: 3.days.ago
248
271
 
249
- order2 = Order.create user: order.user, completed_at: Time.current - 1.day, created_at: 2.day.ago, store: store
250
- expect(order2.created_at).to be > order.created_at
251
- order3 = Order.create user: order.user, completed_at: nil, created_at: 1.day.ago, store: store
252
- expect(order3.created_at).to be > order2.created_at
253
- order4 = Order.create user: order.user, completed_at: nil, created_at: 0.days.ago, store: store
254
- expect(order4.created_at).to be > order3.created_at
272
+ order_two = Order.create user: order.user, completed_at: Time.current - 1.day, created_at: 2.days.ago, store: store
273
+ expect(order_two.created_at).to be > order.created_at
274
+ order_three = Order.create user: order.user, completed_at: nil, created_at: 1.day.ago, store: store
275
+ expect(order_three.created_at).to be > order_two.created_at
276
+ order_four = Order.create user: order.user, completed_at: nil, created_at: 0.days.ago, store: store
277
+ expect(order_four.created_at).to be > order_three.created_at
255
278
 
256
279
  get spree.api_my_orders_path, headers: { 'SERVER_NAME' => store.url }
257
280
  expect(response.status).to eq(200)
@@ -259,8 +282,8 @@ module Spree
259
282
  orders = json_response["orders"]
260
283
  expect(orders.length).to eq(4)
261
284
  expect(orders[0]["number"]).to eq(order.number)
262
- expect(orders[1]["number"]).to eq(order2.number)
263
- expect([orders[2]["number"], orders[3]["number"]]).to match_array([order3.number, order4.number])
285
+ expect(orders[1]["number"]).to eq(order_two.number)
286
+ expect([orders[2]["number"], orders[3]["number"]]).to match_array([order_three.number, order_four.number])
264
287
  end
265
288
  end
266
289
 
@@ -306,7 +329,7 @@ module Spree
306
329
 
307
330
  context 'when an item does not track inventory' do
308
331
  before do
309
- order.line_items.first.variant.update_attributes!(track_inventory: false)
332
+ order.line_items.first.variant.update!(track_inventory: false)
310
333
  end
311
334
 
312
335
  it 'contains stock information on variant' do
@@ -502,18 +525,18 @@ module Spree
502
525
  end
503
526
 
504
527
  it "adds an extra line item" do
505
- variant2 = create(:variant)
528
+ variant_two = create(:variant)
506
529
  put spree.api_order_path(order), params: { order: {
507
530
  line_items: {
508
531
  "0" => { id: line_item.id, quantity: 10 },
509
- "1" => { variant_id: variant2.id, quantity: 1 }
532
+ "1" => { variant_id: variant_two.id, quantity: 1 }
510
533
  }
511
534
  } }
512
535
 
513
536
  expect(response.status).to eq(200)
514
537
  expect(json_response['line_items'].count).to eq(2)
515
538
  expect(json_response['line_items'][0]['quantity']).to eq(10)
516
- expect(json_response['line_items'][1]['variant_id']).to eq(variant2.id)
539
+ expect(json_response['line_items'][1]['variant_id']).to eq(variant_two.id)
517
540
  expect(json_response['line_items'][1]['quantity']).to eq(1)
518
541
  end
519
542
 
@@ -547,7 +570,7 @@ module Spree
547
570
  end
548
571
 
549
572
  it "can add shipping address" do
550
- order.update_attributes!(ship_address_id: nil)
573
+ order.update!(ship_address_id: nil)
551
574
 
552
575
  expect {
553
576
  put spree.api_order_path(order), params: { order: { ship_address_attributes: shipping_address } }
@@ -555,7 +578,7 @@ module Spree
555
578
  end
556
579
 
557
580
  it "receives error message if trying to add shipping address with errors" do
558
- order.update_attributes!(ship_address_id: nil)
581
+ order.update!(ship_address_id: nil)
559
582
 
560
583
  shipping_address[:firstname] = ""
561
584
 
@@ -727,7 +750,7 @@ module Spree
727
750
 
728
751
  orders = json_response[:orders]
729
752
  expect(orders.count).to be >= 3
730
- expect(orders.map { |o| o[:id] }).to match_array Order.pluck(:id)
753
+ expect(orders.map { |order| order[:id] }).to match_array Order.pluck(:id)
731
754
  end
732
755
 
733
756
  after { ActionController::Base.perform_caching = false }