spree_api 2.0.3 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -2
  3. data/app/controllers/spree/api/base_controller.rb +2 -2
  4. data/app/controllers/spree/api/checkouts_controller.rb +16 -12
  5. data/app/controllers/spree/api/products_controller.rb +9 -4
  6. data/app/controllers/spree/api/return_authorizations_controller.rb +28 -0
  7. data/app/controllers/spree/api/shipments_controller.rb +1 -1
  8. data/app/controllers/spree/api/variants_controller.rb +1 -1
  9. data/app/helpers/spree/api/api_helpers.rb +2 -2
  10. data/app/models/spree/order_decorator.rb +18 -5
  11. data/app/views/spree/admin/users/_api_fields.html.erb +4 -4
  12. data/app/views/spree/api/variants/variant.v1.rabl +4 -0
  13. data/config/routes.rb +8 -5
  14. data/lib/spree/api/testing_support/setup.rb +1 -14
  15. data/spec/controllers/spree/api/base_controller_spec.rb +2 -2
  16. data/spec/controllers/spree/api/checkouts_controller_spec.rb +87 -34
  17. data/spec/controllers/spree/api/line_items_controller_spec.rb +1 -1
  18. data/spec/controllers/spree/api/option_types_controller_spec.rb +1 -1
  19. data/spec/controllers/spree/api/option_values_controller_spec.rb +1 -1
  20. data/spec/controllers/spree/api/orders_controller_spec.rb +74 -16
  21. data/spec/controllers/spree/api/payments_controller_spec.rb +5 -5
  22. data/spec/controllers/spree/api/products_controller_spec.rb +8 -7
  23. data/spec/controllers/spree/api/return_authorizations_controller_spec.rb +63 -1
  24. data/spec/controllers/spree/api/states_controller_spec.rb +1 -1
  25. data/spec/controllers/spree/api/variants_controller_spec.rb +2 -4
  26. data/spec/spec_helper.rb +23 -2
  27. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 99004d692b431aa93374f77fcafbbe18215d6f33
4
- data.tar.gz: 9fd49a4a1cb42b2389037efb43c67aed741b4806
3
+ metadata.gz: 9f5519b716277f189e3ca709d60342a6644f63e6
4
+ data.tar.gz: 5d8d83310106f05dee2dd8ed5a0f01aa66d1f6f1
5
5
  SHA512:
6
- metadata.gz: 2a920e5e0749c037063856d55ed8b4ba6d6290f5b9cada53f6195e965beda88f3878ae726c6a0531c0b43df31387c3a4b54c0def0d6ac02ceaa2ea4282e7e22b
7
- data.tar.gz: 400dba6876823fc0607b71ac643fc759760896d2a3f430570e133c101f5a867e4cf7eebc0b0e4a4af1bac7fadce7902b443e4ed13d21fdb067e3bf521f03891a
6
+ metadata.gz: 5f850cf66f2e7a8bd57940b7961f1e740be4a0d0aeabe87ffe5e45f0a3641c7a7c81ffa72301517b5ca07ac5761a3071cb09dabbe69652593656ad382a487367
7
+ data.tar.gz: 03ac9cf7f05c894ebab107a30c6f0d6613835e8c55a2407e4b62f30e2b257fbdcf38cd97842f72a8a10f9032068e252f4c10f8e1b024e852036acdc82621eefa
@@ -1,3 +1,31 @@
1
- ## Spree 2.0.1 (unreleased) ##
1
+ ## Spree 2.0.5 (unreleased)
2
2
 
3
- * No changes.
3
+ ## Spree 2.0.4
4
+
5
+ * PUT requests to Checkouts API endpoints now require authorization to alter an order.
6
+
7
+ *Ryan Bigg*
8
+
9
+ * The Products API endpoint now returns an additional key called `shipping_category_id`, and also requires `shipping_category_id` on create.
10
+
11
+ *Jeff Dutil*
12
+
13
+ * Checkouts API's update action will now correctly process line item attributes (either `line_items` or `line_item_attributes`)
14
+
15
+ * Ryan Bigg*
16
+
17
+ * Checkouts API now correctly processes incoming payment data during the payment step.
18
+
19
+ *Ryan Bigg*
20
+
21
+ * Fix issue where `set_current_order` before filter would be called when CheckoutsController actions were run, causing the order object to be deleted. #3306
22
+
23
+ *Ryan Bigg*
24
+
25
+ * An order can no longer transition past the "cart" state without first having a line item. #3312
26
+
27
+ *Ryan Bigg*
28
+
29
+ * Attributes other than "quantity" and "variant_id" will be added to a line item when creating along with an order. #3404
30
+
31
+ *Alex Marles & Ryan Bigg*
@@ -118,8 +118,8 @@ module Spree
118
118
  def product_scope
119
119
  if current_api_user.has_spree_role?("admin")
120
120
  scope = Product
121
- unless params[:show_deleted]
122
- scope = scope.not_deleted
121
+ if params[:show_deleted]
122
+ scope = scope.with_deleted
123
123
  end
124
124
  else
125
125
  scope = Product.active
@@ -6,6 +6,8 @@ module Spree
6
6
 
7
7
  include Spree::Core::ControllerHelpers::Auth
8
8
  include Spree::Core::ControllerHelpers::Order
9
+ # This before_filter comes from Spree::Core::ControllerHelpers::Order
10
+ skip_before_filter :set_current_order
9
11
 
10
12
  respond_to :json
11
13
 
@@ -15,8 +17,12 @@ module Spree
15
17
  end
16
18
 
17
19
  def update
18
- user_id = object_params.delete(:user_id)
19
- if @order.update_attributes(object_params)
20
+ authorize! :update, @order, params[:order_token]
21
+ order_params = object_params
22
+ user_id = order_params.delete(:user_id)
23
+ line_items = order_params.delete("line_items_attributes")
24
+ if @order.update_attributes(order_params)
25
+ @order.update_line_items(line_items)
20
26
  # TODO: Replace with better code when we switch to strong_parameters
21
27
  # Also remove above user_id stripping
22
28
  if current_api_user.has_spree_role?("admin") && user_id.present?
@@ -32,6 +38,7 @@ module Spree
32
38
 
33
39
  def next
34
40
  @order.next!
41
+ authorize! :update, @order, params[:order_token]
35
42
  respond_with(@order, :default_template => 'spree/api/orders/show', :status => 200)
36
43
  rescue StateMachine::InvalidTransition
37
44
  respond_with(@order, :default_template => 'spree/api/orders/could_not_transition', :status => 422)
@@ -42,15 +49,16 @@ module Spree
42
49
  def object_params
43
50
  # For payment step, filter order parameters to produce the expected nested attributes for a single payment and its source, discarding attributes for payment methods other than the one selected
44
51
  # respond_to check is necessary due to issue described in #2910
52
+ object_params = nested_params
45
53
  if @order.has_checkout_step?("payment") && @order.payment?
46
- if params[:payment_source].present? && source_params = params.delete(:payment_source)[params[:order][:payments_attributes].first[:payment_method_id].underscore]
47
- params[:order][:payments_attributes].first[:source_attributes] = source_params
54
+ if object_params[:payment_source].present? && source_params = object_params.delete(:payment_source)[object_params[:payments_attributes].first[:payment_method_id].underscore]
55
+ object_params[:payments_attributes].first[:source_attributes] = source_params
48
56
  end
49
- if params[:order].present? && params[:order][:payments_attributes]
50
- params[:order][:payments_attributes].first[:amount] = @order.total
57
+ if object_params.present? && object_params[:payments_attributes]
58
+ object_params[:payments_attributes].first[:amount] = @order.total
51
59
  end
52
60
  end
53
- params[:order] || {}
61
+ object_params
54
62
  end
55
63
 
56
64
  def nested_params
@@ -65,6 +73,7 @@ module Spree
65
73
 
66
74
  def load_order
67
75
  @order = Spree::Order.find_by_number!(params[:id])
76
+ authorize! :read, @order, params[:order_token]
68
77
  raise_insufficient_quantity and return if @order.insufficient_stock_lines.present?
69
78
  @order.state = params[:state] if params[:state]
70
79
  state_callback(:before)
@@ -92,11 +101,6 @@ module Spree
92
101
  @order.ship_address ||= Address.default
93
102
  end
94
103
 
95
- def before_delivery
96
- return if params[:order].present?
97
- @order.create_proposed_shipments
98
- end
99
-
100
104
  def before_payment
101
105
  @order.payments.destroy_all if request.put?
102
106
  end
@@ -27,10 +27,15 @@ module Spree
27
27
  authorize! :create, Product
28
28
  params[:product][:available_on] ||= Time.now
29
29
  @product = Product.new(params[:product])
30
- if @product.save
31
- respond_with(@product, :status => 201, :default_template => :show)
32
- else
33
- invalid_resource!(@product)
30
+ begin
31
+ if @product.save
32
+ respond_with(@product, :status => 201, :default_template => :show)
33
+ else
34
+ invalid_resource!(@product)
35
+ end
36
+ rescue ActiveRecord::RecordNotUnique
37
+ @product.permalink = nil
38
+ retry
34
39
  end
35
40
  end
36
41
 
@@ -41,6 +41,34 @@ module Spree
41
41
  respond_with(@return_authorization, :status => 204)
42
42
  end
43
43
 
44
+ def add
45
+ @return_authorization = order.return_authorizations.accessible_by(current_ability, :update).find(params[:id])
46
+ @return_authorization.add_variant params[:variant_id].to_i, params[:quantity].to_i
47
+ if @return_authorization.valid?
48
+ respond_with @return_authorization, default_template: :show
49
+ else
50
+ invalid_resource!(@return_authorization)
51
+ end
52
+ end
53
+
54
+ def receive
55
+ @return_authorization = order.return_authorizations.accessible_by(current_ability, :update).find(params[:id])
56
+ if @return_authorization.receive
57
+ respond_with @return_authorization, default_template: :show
58
+ else
59
+ invalid_resource!(@return_authorization)
60
+ end
61
+ end
62
+
63
+ def cancel
64
+ @return_authorization = order.return_authorizations.accessible_by(current_ability, :update).find(params[:id])
65
+ if @return_authorization.cancel
66
+ respond_with @return_authorization, default_template: :show
67
+ else
68
+ invalid_resource!(@return_authorization)
69
+ end
70
+ end
71
+
44
72
  private
45
73
 
46
74
  def order
@@ -21,7 +21,7 @@ module Spree
21
21
  def update
22
22
  authorize! :read, Shipment
23
23
  @shipment = @order.shipments.find_by_number!(params[:id])
24
-
24
+ params[:shipment] ||= []
25
25
  unlock = params[:shipment].delete(:unlock)
26
26
 
27
27
  if unlock == 'yes'
@@ -56,7 +56,7 @@ module Spree
56
56
  unless current_api_user.has_spree_role?("admin") || params[:show_deleted]
57
57
  variants = @product.variants_including_master
58
58
  else
59
- variants = @product.variants_including_master_and_deleted
59
+ variants = @product.variants_including_master.with_deleted
60
60
  end
61
61
  else
62
62
  variants = Variant.scoped
@@ -12,7 +12,7 @@ module Spree
12
12
  end
13
13
 
14
14
  def product_attributes
15
- [:id, :name, :description, :price, :available_on, :permalink, :meta_description, :meta_keywords, :taxon_ids]
15
+ [:id, :name, :description, :price, :available_on, :permalink, :meta_description, :meta_keywords, :shipping_category_id, :taxon_ids]
16
16
  end
17
17
 
18
18
  def product_property_attributes
@@ -32,7 +32,7 @@ module Spree
32
32
  end
33
33
 
34
34
  def order_attributes
35
- [:id, :number, :item_total, :total, :state, :adjustment_total, :user_id, :created_at, :updated_at, :completed_at, :payment_total, :shipment_state, :payment_state, :email, :special_instructions]
35
+ [:id, :number, :item_total, :total, :state, :adjustment_total, :user_id, :created_at, :updated_at, :completed_at, :payment_total, :shipment_state, :payment_state, :email, :special_instructions, :token]
36
36
  end
37
37
 
38
38
  def line_item_attributes
@@ -1,13 +1,26 @@
1
1
  Spree::Order.class_eval do
2
2
  def self.build_from_api(user, params)
3
- order = create
4
- params[:line_items_attributes] ||= []
5
- unless params[:line_items_attributes].empty?
6
- params[:line_items_attributes].each_key do |k|
7
- order.contents.add(Spree::Variant.find(params[:line_items_attributes][k][:variant_id]), params[:line_items_attributes][k][:quantity])
3
+ line_items = params.delete(:line_items_attributes) || []
4
+
5
+ order = create(params)
6
+ order.associate_user!(user)
7
+
8
+ unless line_items.empty?
9
+ line_items.each_key do |k|
10
+ line_item = line_items[k]
11
+ extra_params = line_item.except(:variant_id, :quantity)
12
+ line_item = order.contents.add(Spree::Variant.find(line_item[:variant_id]), line_item[:quantity])
13
+ line_item.update_attributes(extra_params) unless extra_params.empty?
8
14
  end
9
15
  end
10
16
 
11
17
  order
12
18
  end
19
+
20
+ def update_line_items(line_item_params)
21
+ return if line_item_params.blank?
22
+ line_item_params.each do |id, attributes|
23
+ self.line_items.find(id).update_attributes!(attributes)
24
+ end
25
+ end
13
26
  end
@@ -12,11 +12,11 @@
12
12
  <% end %>
13
13
 
14
14
  <span class="or"><%= Spree.t(:or)%></span>
15
-
15
+
16
16
  <%= form_tag spree.generate_api_key_admin_user_path(@user), :method => :put do %>
17
17
  <%= button Spree.t('regenerate_key', :scope => 'api'), 'icon-refresh' %>
18
- <% end %>
19
- </div>
18
+ <% end %>
19
+ </div>
20
20
 
21
21
  <% else %>
22
22
 
@@ -26,6 +26,6 @@
26
26
  <%= form_tag spree.generate_api_key_admin_user_path(@user), :method => :put do %>
27
27
  <%= button Spree.t('generate_key', :scope => 'api'), 'icon-key' %>
28
28
  <% end %>
29
- </div>
29
+ </div>
30
30
  <% end %>
31
31
  </fieldset>
@@ -1 +1,5 @@
1
1
  attributes *variant_attributes
2
+ node(:options_text) { |v| v.options_text }
3
+ child :option_values => :option_values do
4
+ attributes *option_value_attributes
5
+ end
@@ -21,18 +21,21 @@ Spree::Core::Engine.routes.draw do
21
21
  end
22
22
  end
23
23
 
24
- resources :variants, :only => [:index] do
25
- end
24
+ resources :variants, :only => [:index]
26
25
 
27
26
  resources :option_types do
28
27
  resources :option_values
29
28
  end
30
29
 
31
30
  resources :orders do
32
- resources :return_authorizations
31
+ resources :return_authorizations do
32
+ member do
33
+ put :add
34
+ put :cancel
35
+ put :receive
36
+ end
37
+ end
33
38
  member do
34
- put :address
35
- put :delivery
36
39
  put :cancel
37
40
  put :empty
38
41
  end
@@ -5,23 +5,10 @@ module Spree
5
5
  def sign_in_as_admin!
6
6
  let!(:current_api_user) do
7
7
  user = stub_model(Spree::LegacyUser)
8
- user.should_receive(:has_spree_role?).any_number_of_times.with("admin").and_return(true)
8
+ user.stub(:has_spree_role?).with("admin").and_return(true)
9
9
  user
10
10
  end
11
11
  end
12
-
13
- # Default kaminari's pagination to a certain range
14
- # Means that you don't need to create 25 objects to test pagination
15
- def default_per_page(count)
16
- before do
17
- @current_default_per_page = Kaminari.config.default_per_page
18
- Kaminari.config.default_per_page = 1
19
- end
20
-
21
- after do
22
- Kaminari.config.default_per_page = @current_default_per_page
23
- end
24
- end
25
12
  end
26
13
  end
27
14
  end
@@ -10,7 +10,7 @@ describe Spree::Api::BaseController do
10
10
 
11
11
  context "signed in as a user using an authentication extension" do
12
12
  before do
13
- controller.stub :try_spree_current_user => stub(:email => "spree@example.com")
13
+ controller.stub :try_spree_current_user => double(:email => "spree@example.com")
14
14
  Spree::Api::Config[:requires_authentication] = true
15
15
  end
16
16
 
@@ -49,7 +49,7 @@ describe Spree::Api::BaseController do
49
49
  end
50
50
 
51
51
  it "maps symantec keys to nested_attributes keys" do
52
- klass = stub(:nested_attributes_options => { :line_items => {},
52
+ klass = double(:nested_attributes_options => { :line_items => {},
53
53
  :bill_address => {} })
54
54
  attributes = { 'line_items' => { :id => 1 },
55
55
  'bill_address' => { :id => 2 },
@@ -15,7 +15,7 @@ module Spree
15
15
  create(:stock_location)
16
16
 
17
17
  @shipping_method = create(:shipping_method, :zones => [country_zone])
18
- @payment_method = create(:payment_method)
18
+ @payment_method = create(:bogus_payment_method)
19
19
  end
20
20
 
21
21
  after do
@@ -29,20 +29,6 @@ module Spree
29
29
  json_response['number'].should be_present
30
30
  response.status.should == 201
31
31
  end
32
-
33
- it "should not have a user by default" do
34
- api_post :create
35
-
36
- json_response['user_id'].should_not be_present
37
- response.status.should == 201
38
- end
39
-
40
- it "should not have an email by default" do
41
- api_post :create
42
-
43
- json_response['email'].should_not be_present
44
- response.status.should == 201
45
- end
46
32
  end
47
33
 
48
34
  context "PUT 'update'" do
@@ -53,26 +39,49 @@ module Spree
53
39
  Order.any_instance.stub(:payment_required? => true)
54
40
  end
55
41
 
42
+ it "cannot update without a token" do
43
+ api_put :update, :id => order.to_param
44
+ assert_unauthorized!
45
+ end
46
+
56
47
  it "will return an error if the recently created order cannot transition from cart to address" do
57
48
  order.state.should eq "cart"
58
49
  order.update_column(:email, nil) # email is necessary to transition from cart to address
59
50
 
60
- api_put :update, :id => order.to_param
51
+ api_put :update, :id => order.to_param, :order_token => order.token
61
52
 
62
53
  # Order has not transitioned
63
54
  json_response['state'].should == 'cart'
64
55
  end
65
56
 
66
- it "should transition a recently created order from cart do address" do
57
+ it "should transition a recently created order from cart to address" do
67
58
  order.state.should eq "cart"
68
59
  order.email.should_not be_nil
69
- api_put :update, :id => order.to_param
60
+ api_put :update, :id => order.to_param, :order_token => order.token
61
+ order.reload.state.should eq "address"
62
+ end
63
+
64
+ it "can take line_items_attributes as a parameter" do
65
+ line_item = order.line_items.first
66
+ api_put :update, :id => order.to_param, :order_token => order.token,
67
+ :order => { :line_items_attributes => { line_item.id => { :quantity => 1 } } }
68
+ response.status.should == 200
69
+ order.reload.state.should eq "address"
70
+ end
71
+
72
+ it "can take line_items as a parameter" do
73
+ line_item = order.line_items.first
74
+ api_put :update, :id => order.to_param, :order_token => order.token,
75
+ :order => { :line_items => { line_item.id => { :quantity => 1 } } }
76
+ response.status.should == 200
70
77
  order.reload.state.should eq "address"
71
78
  end
72
79
 
73
80
  it "will return an error if the order cannot transition" do
81
+ order.bill_address = nil
82
+ order.save
74
83
  order.update_column(:state, "address")
75
- api_put :update, :id => order.to_param
84
+ api_put :update, :id => order.to_param, :order_token => order.token
76
85
  response.status.should == 422
77
86
  end
78
87
 
@@ -89,7 +98,7 @@ module Spree
89
98
  :country_id => @country.id
90
99
  }
91
100
  api_put :update,
92
- :id => order.to_param,
101
+ :id => order.to_param, :order_token => order.token,
93
102
  :order => { :bill_address_attributes => billing_address, :ship_address_attributes => shipping_address }
94
103
  json_response['state'].should == 'delivery'
95
104
  json_response['bill_address']['firstname'].should == 'John'
@@ -101,7 +110,7 @@ module Spree
101
110
  order.update_column(:state, "delivery")
102
111
  shipment = create(:shipment, :order => order)
103
112
  shipping_rate = shipment.shipping_rates.first
104
- api_put :update, :id => order.to_param, :order => { :shipments_attributes => { "0" => { :selected_shipping_rate_id => shipping_rate.id, :id => shipment.id } } }
113
+ api_put :update, :id => order.to_param, :order_token => order.token, :order => { :shipments_attributes => { "0" => { :selected_shipping_rate_id => shipping_rate.id, :id => shipment.id } } }
105
114
  json_response['shipments'][0]['shipping_method']['name'].should == @shipping_method.name
106
115
  json_response['state'].should == 'payment'
107
116
  response.status.should == 200
@@ -109,23 +118,53 @@ module Spree
109
118
 
110
119
  it "can update payment method and transition from payment to confirm" do
111
120
  order.update_column(:state, "payment")
112
- api_put :update, :id => order.to_param, :order => { :payments_attributes => [{ :payment_method_id => @payment_method.id }] }
121
+ api_put :update, :id => order.to_param, :order_token => order.token, :order => { :payments_attributes => [{ :payment_method_id => @payment_method.id }] }
113
122
  json_response['state'].should == 'confirm'
114
123
  json_response['payments'][0]['payment_method']['name'].should == @payment_method.name
115
124
  response.status.should == 200
116
125
  end
117
126
 
127
+ it "can update payment method with source and transition from payment to confirm" do
128
+ order.update_column(:state, "payment")
129
+ source_attributes = {
130
+ "number" => "4111111111111111",
131
+ "month" => 1.month.from_now.month,
132
+ "year" => 1.month.from_now.year,
133
+ "verification_value" => "123"
134
+ }
135
+
136
+ api_put :update, :id => order.to_param, :order_token => order.token,
137
+ :order => { :payments_attributes => [{ :payment_method_id => @payment_method.id.to_s }],
138
+ :payment_source => { @payment_method.id.to_s => source_attributes } }
139
+ json_response['payments'][0]['payment_method']['name'].should == @payment_method.name
140
+ json_response['payments'][0]['amount'].should == order.total.to_s
141
+ response.status.should == 200
142
+ end
143
+
144
+ it "returns errors when source is missing attributes" do
145
+ order.update_column(:state, "payment")
146
+ api_put :update, :id => order.to_param, :order_token => order.token,
147
+ :order => { :payments_attributes => [{ :payment_method_id => @payment_method.id.to_s }],
148
+ :payment_source => { @payment_method.id.to_s => { } } }
149
+ response.status.should == 422
150
+ cc_errors = json_response['errors']['payments.Credit Card']
151
+ cc_errors.should include("Number can't be blank")
152
+ cc_errors.should include("Month is not a number")
153
+ cc_errors.should include("Year is not a number")
154
+ cc_errors.should include("Verification Value can't be blank")
155
+ end
156
+
118
157
  it "can transition from confirm to complete" do
119
158
  order.update_column(:state, "confirm")
120
159
  Spree::Order.any_instance.stub(:payment_required? => false)
121
- api_put :update, :id => order.to_param
160
+ api_put :update, :id => order.to_param, :order_token => order.token
122
161
  json_response['state'].should == 'complete'
123
162
  response.status.should == 200
124
163
  end
125
164
 
126
165
  it "returns the order if the order is already complete" do
127
166
  order.update_column(:state, "complete")
128
- api_put :update, :id => order.to_param
167
+ api_put :update, :id => order.to_param, :order_token => order.token
129
168
  json_response['number'].should == order.number
130
169
  response.status.should == 200
131
170
  end
@@ -142,7 +181,7 @@ module Spree
142
181
  end
143
182
 
144
183
  it "can assign an email to the order" do
145
- api_put :update, :id => order.to_param, :order => { :email => "guest@spreecommerce.com" }
184
+ api_put :update, :id => order.to_param, :order => { :email => "guest@spreecommerce.com" }, :order_token => order.token
146
185
  json_response['email'].should == "guest@spreecommerce.com"
147
186
  response.status.should == 200
148
187
  end
@@ -151,16 +190,32 @@ module Spree
151
190
  order.update_column(:state, "payment")
152
191
  Spree::Promo::CouponApplicator.should_receive(:new).with(order).and_call_original
153
192
  Spree::Promo::CouponApplicator.any_instance.should_receive(:apply).and_return({:coupon_applied? => true})
154
- api_put :update, :id => order.to_param, :order => { :coupon_code => "foobar" }
193
+ api_put :update, :id => order.to_param, :order => { :coupon_code => "foobar" }, :order_token => order.token
194
+ end
195
+
196
+ it "can apply a coupon code to an order" do
197
+ order.update_column(:state, "payment")
198
+ Spree::Promo::CouponApplicator.should_receive(:new).with(order).and_call_original
199
+ coupon_result = { :coupon_applied? => true }
200
+ Spree::Promo::CouponApplicator.any_instance.should_receive(:apply).and_return(coupon_result)
201
+ api_put :update, :id => order.to_param, :order_token => order.token, :order => { :coupon_code => "foobar" }
155
202
  end
156
203
  end
157
204
 
158
205
  context "PUT 'next'" do
159
- let!(:order) { create(:order) }
206
+ let!(:order) { create(:order_with_line_items) }
207
+ it "cannot transition to address without a line item" do
208
+ order.line_items.delete_all
209
+ order.update_column(:email, "spree@example.com")
210
+ api_put :next, :id => order.to_param, :order_token => order.token
211
+ response.status.should == 422
212
+ json_response["errors"]["base"].should include(Spree.t(:there_are_no_items_for_this_order))
213
+ end
214
+
160
215
  it "can transition an order to the next state" do
161
216
  order.update_column(:email, "spree@example.com")
162
217
 
163
- api_put :next, :id => order.to_param
218
+ api_put :next, :id => order.to_param, :order_token => order.token
164
219
  response.status.should == 200
165
220
  json_response['state'].should == 'address'
166
221
  end
@@ -168,17 +223,15 @@ module Spree
168
223
  it "cannot transition if order email is blank" do
169
224
  order.update_column(:email, nil)
170
225
 
171
- api_put :next, :id => order.to_param
226
+ api_put :next, :id => order.to_param, :order_token => order.token
172
227
  response.status.should == 422
173
228
  json_response['error'].should =~ /could not be transitioned/
174
229
  end
175
230
 
176
- it "can apply a coupon code to an order" do
231
+ it "returns a sensible error when no payment method is specified" do
177
232
  order.update_column(:state, "payment")
178
- Spree::Promo::CouponApplicator.should_receive(:new).with(order).and_call_original
179
- coupon_result = { :coupon_applied? => true }
180
- Spree::Promo::CouponApplicator.any_instance.should_receive(:apply).and_return(coupon_result)
181
- api_put :update, :id => order.to_param, :order => { :coupon_code => "foobar" }
233
+ api_put :next, :id => order.to_param, :order_token => order.token, :order => {}
234
+ json_response["errors"]["base"].should include(Spree.t(:no_pending_payments))
182
235
  end
183
236
  end
184
237
  end
@@ -65,7 +65,7 @@ module Spree
65
65
  line_item = order.line_items.first
66
66
  api_delete :destroy, :id => line_item.id
67
67
  assert_unauthorized!
68
- lambda { line_item.reload }.should_not raise_error(ActiveRecord::RecordNotFound)
68
+ lambda { line_item.reload }.should_not raise_error
69
69
  end
70
70
  end
71
71
 
@@ -69,7 +69,7 @@ module Spree
69
69
  it "cannot delete an option type" do
70
70
  api_delete :destroy, :id => option_type.id
71
71
  assert_unauthorized!
72
- lambda { option_type.reload }.should_not raise_error(ActiveRecord::RecordNotFound)
72
+ lambda { option_type.reload }.should_not raise_error
73
73
  end
74
74
 
75
75
  context "as an admin" do
@@ -80,7 +80,7 @@ module Spree
80
80
  it "cannot delete an option value" do
81
81
  api_delete :destroy, :id => option_type.id
82
82
  assert_unauthorized!
83
- lambda { option_type.reload }.should_not raise_error(ActiveRecord::RecordNotFound)
83
+ lambda { option_type.reload }.should_not raise_error
84
84
  end
85
85
 
86
86
  context "as an admin" do
@@ -9,7 +9,7 @@ module Spree
9
9
  :state, :adjustment_total,
10
10
  :user_id, :created_at, :updated_at,
11
11
  :completed_at, :payment_total, :shipment_state,
12
- :payment_state, :email, :special_instructions] }
12
+ :payment_state, :email, :special_instructions, :token] }
13
13
 
14
14
 
15
15
  before do
@@ -55,30 +55,88 @@ module Spree
55
55
  assert_unauthorized!
56
56
  end
57
57
 
58
- it "can create an order" do
59
- variant = create(:variant)
60
- api_post :create, :order => { :line_items => { "0" => { :variant_id => variant.to_param, :quantity => 5 } } }
61
- response.status.should == 201
62
- order = Order.last
63
- order.line_items.count.should == 1
64
- order.line_items.first.variant.should == variant
65
- order.line_items.first.quantity.should == 5
66
- json_response["state"].should == "cart"
58
+ context "create order" do
59
+ let(:current_api_user) do
60
+ user = Spree.user_class.new(:email => "spree@example.com")
61
+ user.generate_spree_api_key!
62
+ user
63
+ end
64
+
65
+ it "can create an order" do
66
+ variant = create(:variant)
67
+ api_post :create, :order => { :line_items => { "0" => { :variant_id => variant.to_param, :quantity => 5 } } }
68
+ response.status.should == 201
69
+ order = Order.last
70
+ order.line_items.count.should == 1
71
+ order.line_items.first.variant.should == variant
72
+ order.line_items.first.quantity.should == 5
73
+ json_response["state"].should == "cart"
74
+ order.user.should == current_api_user
75
+ order.email == current_api_user.email
76
+ json_response["user_id"].should == current_api_user.id
77
+ end
78
+
79
+ # Regression test for #3404
80
+ it "can specify additional parameters for a line item" do
81
+ variant = create(:variant)
82
+ Order.should_receive(:create).and_return(order = Spree::Order.new)
83
+ order.stub(:associate_user!)
84
+ order.stub_chain(:contents, :add).and_return(line_item = double('LineItem'))
85
+ line_item.should_receive(:update_attributes).with("special" => true)
86
+ api_post :create, :order => {
87
+ :line_items => {
88
+ "0" => {
89
+ :variant_id => variant.to_param, :quantity => 5, :special => true
90
+ }
91
+ }
92
+ }
93
+ response.status.should == 201
94
+ end
95
+
96
+ # Regression test for #3404
97
+ it "does not update line item needlessly" do
98
+ variant = create(:variant)
99
+ Order.should_receive(:create).and_return(order = Spree::Order.new)
100
+ order.stub(:associate_user!)
101
+ order.stub_chain(:contents, :add).and_return(line_item = double('LineItem'))
102
+ line_item.should_not_receive(:update_attributes)
103
+ api_post :create, :order => {
104
+ :line_items => {
105
+ "0" => {
106
+ :variant_id => variant.to_param, :quantity => 5
107
+ }
108
+ }
109
+ }
110
+ end
67
111
  end
68
112
 
69
113
  it "can create an order without any parameters" do
70
- lambda { api_post :create }.should_not raise_error(NoMethodError)
114
+ lambda { api_post :create }.should_not raise_error
71
115
  response.status.should == 201
72
116
  order = Order.last
73
117
  json_response["state"].should == "cart"
74
118
  end
75
119
 
76
120
  context "working with an order" do
121
+
122
+ let(:variant) { create(:variant) }
123
+ let!(:line_item) { order.contents.add(variant, 1) }
124
+ let!(:payment_method) { create(:payment_method) }
125
+
126
+ let(:address_params) { { :country_id => Country.first.id, :state_id => State.first.id } }
127
+ let(:billing_address) { { :firstname => "Tiago", :lastname => "Motta", :address1 => "Av Paulista",
128
+ :city => "Sao Paulo", :zipcode => "1234567", :phone => "12345678",
129
+ :country_id => Country.first.id, :state_id => State.first.id} }
130
+ let(:shipping_address) { { :firstname => "Tiago", :lastname => "Motta", :address1 => "Av Paulista",
131
+ :city => "Sao Paulo", :zipcode => "1234567", :phone => "12345678",
132
+ :country_id => Country.first.id, :state_id => State.first.id} }
133
+
77
134
  before do
78
135
  Order.any_instance.stub :user => current_api_user
79
- create(:payment_method)
80
136
  order.next # Switch from cart to address
81
- order.ship_address.should be_nil
137
+ order.bill_address = nil
138
+ order.ship_address = nil
139
+ order.save
82
140
  order.state.should == "address"
83
141
  end
84
142
 
@@ -102,18 +160,18 @@ module Spree
102
160
 
103
161
  response.status.should == 200
104
162
  json_response['item_total'].to_f.should_not == order.item_total.to_f
163
+ json_response['line_items'].count.should == 2
164
+ json_response['line_items'].first['quantity'].should == 1
165
+ json_response['line_items'].last['quantity'].should == 2
105
166
  end
106
167
 
107
168
  it "can add billing address" do
108
- order.bill_address.should be_nil
109
-
110
169
  api_put :update, :id => order.to_param, :order => { :bill_address_attributes => billing_address }
111
170
 
112
171
  order.reload.bill_address.should_not be_nil
113
172
  end
114
173
 
115
174
  it "receives error message if trying to add billing address with errors" do
116
- order.bill_address.should be_nil
117
175
  billing_address[:firstname] = ""
118
176
 
119
177
  api_put :update, :id => order.to_param, :order => { :bill_address_attributes => billing_address }
@@ -102,7 +102,7 @@ module Spree
102
102
  end
103
103
 
104
104
  it "returns a 422 status when authorization fails" do
105
- fake_response = stub(:success? => false, :to_s => "Could not authorize card")
105
+ fake_response = double(:success? => false, :to_s => "Could not authorize card")
106
106
  Spree::Gateway::Bogus.any_instance.should_receive(:authorize).and_return(fake_response)
107
107
  api_put :authorize, :id => payment.to_param
108
108
  response.status.should == 422
@@ -119,7 +119,7 @@ module Spree
119
119
  end
120
120
 
121
121
  it "returns a 422 status when purchasing fails" do
122
- fake_response = stub(:success? => false, :to_s => "Insufficient funds")
122
+ fake_response = double(:success? => false, :to_s => "Insufficient funds")
123
123
  Spree::Gateway::Bogus.any_instance.should_receive(:capture).and_return(fake_response)
124
124
  api_put :capture, :id => payment.to_param
125
125
  response.status.should == 422
@@ -137,7 +137,7 @@ module Spree
137
137
  end
138
138
 
139
139
  it "returns a 422 status when purchasing fails" do
140
- fake_response = stub(:success? => false, :to_s => "Insufficient funds")
140
+ fake_response = double(:success? => false, :to_s => "Insufficient funds")
141
141
  Spree::Gateway::Bogus.any_instance.should_receive(:purchase).and_return(fake_response)
142
142
  api_put :purchase, :id => payment.to_param
143
143
  response.status.should == 422
@@ -155,7 +155,7 @@ module Spree
155
155
  end
156
156
 
157
157
  it "returns a 422 status when voiding fails" do
158
- fake_response = stub(:success? => false, :to_s => "NO REFUNDS")
158
+ fake_response = double(:success? => false, :to_s => "NO REFUNDS")
159
159
  Spree::Gateway::Bogus.any_instance.should_receive(:void).and_return(fake_response)
160
160
  api_put :void, :id => payment.to_param
161
161
  response.status.should == 422
@@ -182,7 +182,7 @@ module Spree
182
182
  end
183
183
 
184
184
  it "returns a 422 status when crediting fails" do
185
- fake_response = stub(:success? => false, :to_s => "NO CREDIT FOR YOU")
185
+ fake_response = double(:success? => false, :to_s => "NO CREDIT FOR YOU")
186
186
  Spree::Gateway::Bogus.any_instance.should_receive(:credit).and_return(fake_response)
187
187
  api_put :credit, :id => payment.to_param
188
188
  response.status.should == 422
@@ -7,7 +7,7 @@ module Spree
7
7
 
8
8
  let!(:product) { create(:product) }
9
9
  let!(:inactive_product) { create(:product, :available_on => Time.now.tomorrow, :name => "inactive") }
10
- let(:attributes) { [:id, :name, :description, :price, :available_on, :permalink, :meta_description, :meta_keywords, :taxon_ids] }
10
+ let(:attributes) { [:id, :name, :description, :price, :available_on, :permalink, :meta_description, :meta_keywords, :shipping_category_id, :taxon_ids] }
11
11
 
12
12
  before do
13
13
  stub_authentication!
@@ -41,11 +41,9 @@ module Spree
41
41
  end
42
42
 
43
43
  context "pagination" do
44
- default_per_page(1)
45
-
46
44
  it "can select the next page of products" do
47
45
  second_product = create(:product)
48
- api_get :index, :page => 2
46
+ api_get :index, :page => 2, :per_page => 1
49
47
  json_response["products"].first.should have_attributes(attributes)
50
48
  json_response["total_count"].should == 2
51
49
  json_response["current_page"].should == 2
@@ -136,6 +134,7 @@ module Spree
136
134
  required_attributes = json_response["required_attributes"]
137
135
  required_attributes.should include("name")
138
136
  required_attributes.should include("price")
137
+ required_attributes.should include("shipping_category_id")
139
138
  end
140
139
 
141
140
  it_behaves_like "modifying product actions are restricted"
@@ -171,7 +170,8 @@ module Spree
171
170
 
172
171
  it "can create a new product" do
173
172
  api_post :create, :product => { :name => "The Other Product",
174
- :price => 19.99 }
173
+ :price => 19.99,
174
+ :shipping_category_id => create(:shipping_category).id }
175
175
  json_response.should have_attributes(attributes)
176
176
  response.status.should == 201
177
177
  end
@@ -188,7 +188,8 @@ module Spree
188
188
 
189
189
  it "can still create a product" do
190
190
  api_post :create, :product => { :name => "The Other Product",
191
- :price => 19.99 },
191
+ :price => 19.99,
192
+ :shipping_category_id => create(:shipping_category).id },
192
193
  :token => "fake"
193
194
  json_response.should have_attributes(attributes)
194
195
  response.status.should == 201
@@ -201,7 +202,7 @@ module Spree
201
202
  json_response["error"].should == "Invalid resource. Please fix errors and try again."
202
203
  errors = json_response["errors"]
203
204
  errors.delete("permalink") # Don't care about this one.
204
- errors.keys.should =~ ["name", "price"]
205
+ errors.keys.should =~ ["name", "price", "shipping_category_id"]
205
206
  end
206
207
 
207
208
  it "can update a product" do
@@ -44,6 +44,21 @@ module Spree
44
44
  assert_unauthorized!
45
45
  end
46
46
 
47
+ it "cannot add a variant to a return authorization" do
48
+ api_put :add
49
+ assert_unauthorized!
50
+ end
51
+
52
+ it "cannot mark a return authorization as received" do
53
+ api_put :receive
54
+ assert_unauthorized!
55
+ end
56
+
57
+ it "cannot cancel a return authorization" do
58
+ api_put :cancel
59
+ assert_unauthorized!
60
+ end
61
+
47
62
  it "cannot delete a return authorization" do
48
63
  api_delete :destroy
49
64
  assert_unauthorized!
@@ -105,6 +120,53 @@ module Spree
105
120
  json_response.should have_attributes(attributes)
106
121
  end
107
122
 
123
+ it "can add an inventory unit to a return authorization on the order" do
124
+ FactoryGirl.create(:return_authorization, :order => order)
125
+ return_authorization = order.return_authorizations.first
126
+ inventory_unit = return_authorization.returnable_inventory.first
127
+ inventory_unit.should be
128
+ return_authorization.inventory_units.should be_empty
129
+ api_put :add, :id => return_authorization.id, variant_id: inventory_unit.variant.id, quantity: 1
130
+ response.status.should == 200
131
+ json_response.should have_attributes(attributes)
132
+ return_authorization.reload.inventory_units.should_not be_empty
133
+ end
134
+
135
+ it "can mark a return authorization as received on the order with an inventory unit" do
136
+ FactoryGirl.create(:new_return_authorization, :order => order)
137
+ return_authorization = order.return_authorizations.first
138
+ return_authorization.state.should == "authorized"
139
+
140
+ # prep (use a rspec context or a factory instead?)
141
+ inventory_unit = return_authorization.returnable_inventory.first
142
+ inventory_unit.should be
143
+ return_authorization.inventory_units.should be_empty
144
+ api_put :add, :id => return_authorization.id, variant_id: inventory_unit.variant.id, quantity: 1
145
+ # end prep
146
+
147
+ api_delete :receive, :id => return_authorization.id
148
+ response.status.should == 200
149
+ return_authorization.reload.state.should == "received"
150
+ end
151
+
152
+ it "cannot mark a return authorization as received on the order with no inventory units" do
153
+ FactoryGirl.create(:new_return_authorization, :order => order)
154
+ return_authorization = order.return_authorizations.first
155
+ return_authorization.state.should == "authorized"
156
+ api_delete :receive, :id => return_authorization.id
157
+ response.status.should == 422
158
+ return_authorization.reload.state.should == "authorized"
159
+ end
160
+
161
+ it "can cancel a return authorization on the order" do
162
+ FactoryGirl.create(:new_return_authorization, :order => order)
163
+ return_authorization = order.return_authorizations.first
164
+ return_authorization.state.should == "authorized"
165
+ api_delete :cancel, :id => return_authorization.id
166
+ response.status.should == 200
167
+ return_authorization.reload.state.should == "canceled"
168
+ end
169
+
108
170
  it "can delete a return authorization on the order" do
109
171
  FactoryGirl.create(:return_authorization, :order => order)
110
172
  return_authorization = order.return_authorizations.first
@@ -140,7 +202,7 @@ module Spree
140
202
  return_authorization = order.return_authorizations.first
141
203
  api_delete :destroy, :id => return_authorization.id
142
204
  assert_unauthorized!
143
- lambda { return_authorization.reload }.should_not raise_error(ActiveRecord::RecordNotFound)
205
+ lambda { return_authorization.reload }.should_not raise_error
144
206
  end
145
207
  end
146
208
  end
@@ -19,7 +19,7 @@ module Spree
19
19
 
20
20
  context "pagination" do
21
21
  before do
22
- State.should_receive(:scoped).and_return(@scope = stub)
22
+ State.should_receive(:scoped).and_return(@scope = double)
23
23
  @scope.stub_chain(:ransack, :result, :includes, :order).and_return(@scope)
24
24
  end
25
25
 
@@ -77,11 +77,9 @@ module Spree
77
77
  end
78
78
 
79
79
  context "pagination" do
80
- default_per_page(1)
81
-
82
80
  it "can select the next page of variants" do
83
81
  second_variant = create(:variant)
84
- api_get :index, :page => 2
82
+ api_get :index, :page => 2, :per_page => 1
85
83
  json_response["variants"].first.should have_attributes(attributes)
86
84
  json_response["total_count"].should == 3
87
85
  json_response["current_page"].should == 2
@@ -167,7 +165,7 @@ module Spree
167
165
  it "can delete a variant" do
168
166
  api_delete :destroy, :id => variant.to_param
169
167
  response.status.should == 204
170
- lambda { variant.reload }.should raise_error(ActiveRecord::RecordNotFound)
168
+ lambda { Spree::Variant.find(variant.id) }.should raise_error(ActiveRecord::RecordNotFound)
171
169
  end
172
170
  end
173
171
 
@@ -16,6 +16,8 @@ ENV["RAILS_ENV"] ||= 'test'
16
16
  require File.expand_path("../dummy/config/environment", __FILE__)
17
17
  require 'rspec/rails'
18
18
  require 'rspec/autorun'
19
+ require 'database_cleaner'
20
+ require 'ffaker'
19
21
 
20
22
  # Requires supporting ruby files with custom matchers and macros, etc,
21
23
  # in spec/support/ and its subdirectories.
@@ -28,7 +30,7 @@ require 'spree/api/testing_support/helpers'
28
30
  require 'spree/api/testing_support/setup'
29
31
 
30
32
  RSpec.configure do |config|
31
- config.backtrace_clean_patterns = [/gems\/activesupport/, /gems\/actionpack/, /gems\/rspec/]
33
+ config.backtrace_exclusion_patterns = [/gems\/activesupport/, /gems\/actionpack/, /gems\/rspec/]
32
34
  config.color = true
33
35
 
34
36
  config.include FactoryGirl::Syntax::Methods
@@ -36,9 +38,28 @@ RSpec.configure do |config|
36
38
  config.extend Spree::Api::TestingSupport::Setup, :type => :controller
37
39
  config.include Spree::TestingSupport::Preferences, :type => :controller
38
40
 
41
+ config.fail_fast = ENV['FAIL_FAST'] || false
42
+
39
43
  config.before do
40
44
  Spree::Api::Config[:requires_authentication] = true
41
45
  end
42
46
 
43
- config.fail_fast = ENV['FAIL_FAST'] || false
47
+ # Using truncation to prevent Ruby 1.9.3 specific error:
48
+ # SQLite3::SQLException: cannot start a transaction within a transaction: begin transaction
49
+ # http://stackoverflow.com/questions/12220901/sqlite3sqlexception-when-using-database-cleaner-with-rails-spork-rspec
50
+ unless RUBY_VERSION >= '2.0.0'
51
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
52
+ # examples within a transaction, comment the following line or assign false
53
+ # instead of true.
54
+ config.use_transactional_fixtures = false
55
+
56
+ config.before do
57
+ DatabaseCleaner.strategy = :truncation
58
+ DatabaseCleaner.start
59
+ end
60
+
61
+ config.after do
62
+ DatabaseCleaner.clean
63
+ end
64
+ end
44
65
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Bigg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-13 00:00:00.000000000 Z
11
+ date: 2013-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: spree_core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.3
19
+ version: 2.0.4
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.3
26
+ version: 2.0.4
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rabl
29
29
  requirement: !ruby/object:Gem::Requirement