spree_api 1.1.0.rc1 → 1.1.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/app/controllers/spree/api/v1/base_controller.rb +11 -1
  2. data/app/controllers/spree/api/v1/countries_controller.rb +15 -0
  3. data/app/controllers/spree/api/v1/orders_controller.rb +16 -1
  4. data/app/controllers/spree/api/v1/payments_controller.rb +73 -0
  5. data/app/controllers/spree/api/v1/products_controller.rb +5 -0
  6. data/app/controllers/spree/api/v1/shipments_controller.rb +31 -0
  7. data/app/helpers/spree/api/api_helpers.rb +14 -2
  8. data/app/models/spree/order_decorator.rb +3 -3
  9. data/app/models/spree/payment_decorator.rb +0 -0
  10. data/app/views/spree/api/v1/countries/index.rabl +2 -0
  11. data/app/views/spree/api/v1/countries/show.rabl +5 -0
  12. data/app/views/spree/api/v1/errors/gateway_error.rabl +2 -0
  13. data/app/views/spree/api/v1/errors/invalid_resource.rabl +1 -1
  14. data/app/views/spree/api/v1/line_items/new.rabl +1 -1
  15. data/app/views/spree/api/v1/line_items/show.rabl +1 -1
  16. data/app/views/spree/api/v1/orders/address.rabl +3 -0
  17. data/app/views/spree/api/v1/orders/payment.rabl +4 -0
  18. data/app/views/spree/api/v1/orders/show.rabl +23 -0
  19. data/app/views/spree/api/v1/payments/credit_over_limit.rabl +2 -0
  20. data/app/views/spree/api/v1/payments/index.rabl +2 -0
  21. data/app/views/spree/api/v1/payments/new.rabl +6 -0
  22. data/app/views/spree/api/v1/payments/show.rabl +2 -0
  23. data/app/views/spree/api/v1/shipments/show.rabl +7 -0
  24. data/config/locales/en.yml +2 -0
  25. data/config/routes.rb +22 -0
  26. data/db/migrate/20120411123334_resize_api_key_field.rb +5 -0
  27. data/lib/spree/api/engine.rb +4 -0
  28. data/lib/spree/api/testing_support/helpers.rb +35 -0
  29. data/lib/spree/api/testing_support/setup.rb +28 -0
  30. data/spec/controllers/spree/api/v1/base_controller_spec.rb +12 -0
  31. data/spec/controllers/spree/api/v1/countries_controller_spec.rb +24 -0
  32. data/spec/controllers/spree/api/v1/line_items_controller_spec.rb +1 -1
  33. data/spec/controllers/spree/api/v1/orders_controller_spec.rb +1 -1
  34. data/spec/controllers/spree/api/v1/payments_controller_spec.rb +162 -0
  35. data/spec/controllers/spree/api/v1/products_controller_spec.rb +7 -0
  36. data/spec/controllers/spree/api/v1/shipments_controller_spec.rb +35 -0
  37. data/spec/controllers/spree/api/v1/variants_controller_spec.rb +2 -1
  38. data/spec/models/spree/order_spec.rb +1 -1
  39. data/spec/spec_helper.rb +5 -12
  40. data/spec/support/database_cleaner.rb +14 -0
  41. data/spec/support/have_attributes_matcher.rb +13 -0
  42. metadata +40 -17
  43. data/spec/support/api_helpers.rb +0 -68
@@ -14,6 +14,15 @@ module Spree
14
14
 
15
15
  helper Spree::Api::ApiHelpers
16
16
 
17
+ def map_nested_attributes_keys(klass, attributes)
18
+ nested_keys = klass.nested_attributes_options.keys
19
+ attributes.inject({}) do |h, (k,v)|
20
+ key = nested_keys.include?(k.to_sym) ? "#{k}_attributes" : k
21
+ h[key] = v
22
+ h
23
+ end.with_indifferent_access
24
+ end
25
+
17
26
  private
18
27
 
19
28
  def check_for_api_key
@@ -39,7 +48,8 @@ module Spree
39
48
  end
40
49
 
41
50
  def invalid_resource!(resource)
42
- render "spree/api/v1/errors/invalid_resource", :resource => resource, :status => 422
51
+ @resource = resource
52
+ render "spree/api/v1/errors/invalid_resource", :status => 422
43
53
  end
44
54
 
45
55
  def api_key
@@ -0,0 +1,15 @@
1
+ module Spree
2
+ module Api
3
+ module V1
4
+ class CountriesController < Spree::Api::V1::BaseController
5
+ def index
6
+ @countries = Country.includes(:states).order('name ASC')
7
+ end
8
+
9
+ def show
10
+ @country = Country.find(params[:id])
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -2,6 +2,8 @@ module Spree
2
2
  module Api
3
3
  module V1
4
4
  class OrdersController < Spree::Api::V1::BaseController
5
+ before_filter :map_nested_attributes, :only => [:create, :update]
6
+
5
7
  def index
6
8
  # should probably look at turning this into a CanCan step
7
9
  raise CanCan::AccessDenied unless current_api_user.has_role?("admin")
@@ -13,10 +15,19 @@ module Spree
13
15
  end
14
16
 
15
17
  def create
16
- @order = Order.build_from_api(current_api_user, params[:order])
18
+ @order = Order.build_from_api(current_api_user, @nested_params)
17
19
  next!
18
20
  end
19
21
 
22
+ def update
23
+ authorize! :update, Order
24
+ if order.update_attributes(@nested_params)
25
+ render :show
26
+ else
27
+ invalid_resource!(order)
28
+ end
29
+ end
30
+
20
31
  def address
21
32
  order.build_ship_address(params[:shipping_address])
22
33
  order.build_bill_address(params[:billing_address])
@@ -36,6 +47,10 @@ module Spree
36
47
 
37
48
  private
38
49
 
50
+ def map_nested_attributes
51
+ @nested_params = map_nested_attributes_keys Order, params[:order]
52
+ end
53
+
39
54
  def order
40
55
  @order ||= Order.find_by_number!(params[:id])
41
56
  end
@@ -0,0 +1,73 @@
1
+ module Spree
2
+ module Api
3
+ module V1
4
+ class PaymentsController < Spree::Api::V1::BaseController
5
+ before_filter :find_order
6
+ before_filter :find_payment, :only => [:show, :authorize, :purchase, :capture, :void, :credit]
7
+
8
+ def index
9
+ @payments = @order.payments
10
+ end
11
+
12
+ def new
13
+ @payment_methods = Spree::PaymentMethod.where(:environment => Rails.env)
14
+ end
15
+
16
+ def create
17
+ @payment = @order.payments.build(params[:payment])
18
+ if @payment.save
19
+ render :show, :status => 201
20
+ else
21
+ invalid_resource!(@payment)
22
+ end
23
+ end
24
+
25
+ def show
26
+ end
27
+
28
+ def authorize
29
+ perform_payment_action(:authorize)
30
+ end
31
+
32
+ def purchase
33
+ perform_payment_action(:purchase)
34
+ end
35
+
36
+ def void
37
+ perform_payment_action(:void_transaction)
38
+ end
39
+
40
+ def credit
41
+ if params[:amount].to_f > @payment.credit_allowed
42
+ render "spree/api/v1/payments/credit_over_limit", :status => 422
43
+ else
44
+ perform_payment_action(:credit, params[:amount])
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def find_order
51
+ @order = Order.find_by_number(params[:order_id])
52
+ authorize! :read, @order
53
+ end
54
+
55
+ def find_payment
56
+ @payment = @order.payments.find(params[:id])
57
+ end
58
+
59
+ def perform_payment_action(action, *args)
60
+ authorize! action, Payment
61
+
62
+ begin
63
+ @payment.send("#{action}!", *args)
64
+ render :show
65
+ rescue Spree::Core::GatewayError => e
66
+ @error = e.message
67
+ render "spree/api/v1/errors/gateway_error", :status => 422
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -6,6 +6,11 @@ module Spree
6
6
  @products = product_scope.page(params[:page])
7
7
  end
8
8
 
9
+ def search
10
+ @products = product_scope.search(params[:q]).result.page(params[:page])
11
+ render :index
12
+ end
13
+
9
14
  def show
10
15
  @product = find_product(params[:id])
11
16
  end
@@ -0,0 +1,31 @@
1
+ module Spree
2
+ module Api
3
+ module V1
4
+ class ShipmentsController < BaseController
5
+ before_filter :find_order
6
+ before_filter :find_and_update_shipment, :only => [:ship, :ready]
7
+
8
+ def ready
9
+ @shipment.ready!
10
+ render :show
11
+ end
12
+
13
+ def ship
14
+ @shipment.ship!
15
+ render :show
16
+ end
17
+
18
+ private
19
+
20
+ def find_order
21
+ @order = Order.find_by_number!(params[:order_id])
22
+ end
23
+
24
+ def find_and_update_shipment
25
+ @shipment = @order.shipments.find_by_number!(params[:id])
26
+ @shipment.update_attributes(params[:shipment])
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -17,7 +17,7 @@ module Spree
17
17
  end
18
18
 
19
19
  def variant_attributes
20
- [:id, :name, :count_on_hand, :sku, :price, :weight, :height, :width, :depth, :is_master, :cost_price]
20
+ [:id, :name, :count_on_hand, :sku, :price, :weight, :height, :width, :depth, :is_master, :cost_price, :permalink]
21
21
  end
22
22
 
23
23
  def image_attributes
@@ -33,12 +33,24 @@ module Spree
33
33
  end
34
34
 
35
35
  def line_item_attributes
36
- [:quantity, :price, :variant_id]
36
+ [:id, :quantity, :price, :variant_id]
37
37
  end
38
38
 
39
39
  def option_type_attributes
40
40
  [:id, :name, :presentation, :position]
41
41
  end
42
+
43
+ def payment_attributes
44
+ [:id, :source_type, :source_id, :amount, :payment_method_id, :response_code, :state, :avs_response, :created_at, :updated_at]
45
+ end
46
+
47
+ def payment_method_attributes
48
+ [:id, :name, :description]
49
+ end
50
+
51
+ def shipment_attributes
52
+ [:id, :tracking, :number, :cost, :shipped_at, :state]
53
+ end
42
54
  end
43
55
  end
44
56
  end
@@ -1,10 +1,10 @@
1
1
  Spree::Order.class_eval do
2
2
  def self.build_from_api(user, params)
3
3
  order = create
4
- params[:line_items].each do |variant_id, quantity|
5
- line_item_params = { :variant_id => variant_id, :quantity => quantity }
6
- line_item = order.add_variant(Spree::Variant.find(variant_id), quantity)
4
+ params[:line_items_attributes].each do |line_item|
5
+ order.add_variant(Spree::Variant.find(line_item[:variant_id]), line_item[:quantity])
7
6
  end
7
+
8
8
  order.user = user
9
9
  order.email = user.email
10
10
  order
File without changes
@@ -0,0 +1,2 @@
1
+ collection @countries
2
+ extends "spree/api/v1/countries/show"
@@ -0,0 +1,5 @@
1
+ object @country
2
+ attributes :id, :iso_name, :iso, :iso3, :name, :numcode
3
+ child :states => :states do
4
+ attributes :id, :name, :abbr, :country_id
5
+ end
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:error) { I18n.t(:gateway_error, :scope => "spree.api", :text => @error) }
@@ -1,3 +1,3 @@
1
1
  object false
2
2
  node(:error) { I18n.t(:invalid_resource, :scope => "spree.api") }
3
- node(:errors) { @product.errors }
3
+ node(:errors) { @resource.errors }
@@ -1,3 +1,3 @@
1
1
  object false
2
- node(:attributes) { [*line_item_attributes] }
2
+ node(:attributes) { [*line_item_attributes] - [:id] }
3
3
  node(:required_attributes) { [:variant_id, :quantity] }
@@ -1,5 +1,5 @@
1
1
  object @line_item
2
- attributes :quantity, :price
2
+ attributes *line_item_attributes
3
3
  child :variant do
4
4
  extends "spree/api/v1/variants/variant"
5
5
  end
@@ -0,0 +1,3 @@
1
+ attributes :id, :firstname, :lastname, :address1, :address2,
2
+ :city, :zipcode, :country, :state, :phone, :state_name,
3
+ :company, :alternative_phone, :country_id, :state_id
@@ -0,0 +1,4 @@
1
+ attributes :id, :amount, :payment_method_id
2
+ child :payment_method => :payment_method do
3
+ attributes :id, :name, :environment
4
+ end
@@ -1,3 +1,26 @@
1
1
  object @order
2
2
  attributes *order_attributes
3
3
  extends "spree/api/v1/orders/#{@order.state}"
4
+
5
+ child :billing_address => :bill_address do
6
+ extends "spree/api/v1/orders/address"
7
+ end
8
+
9
+ child :shipping_address => :ship_address do
10
+ extends "spree/api/v1/orders/address"
11
+ end
12
+
13
+ child :line_items => :line_items do
14
+ extends "spree/api/v1/line_items/show"
15
+ end
16
+
17
+ child :payments => :payments do
18
+ attributes :id, :amount, :state, :payment_method_id
19
+ child :payment_method => :payment_method do
20
+ attributes :id, :name, :environment
21
+ end
22
+ end
23
+
24
+ child :shipments => :shipments do
25
+ extends "spree/api/v1/shipments/show"
26
+ end
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:error) { I18n.t(:credit_over_limit, :limit => @payment.credit_allowed, :scope => 'spree.api') }
@@ -0,0 +1,2 @@
1
+ collection @payments
2
+ attributes *payment_attributes
@@ -0,0 +1,6 @@
1
+ object false
2
+ node(:attributes) { [*payment_attributes] }
3
+ child @payment_methods => :payment_methods do
4
+ attributes *payment_method_attributes
5
+ end
6
+
@@ -0,0 +1,2 @@
1
+ object @payment
2
+ attributes *payment_attributes
@@ -0,0 +1,7 @@
1
+ object @shipment
2
+ attributes *shipment_attributes
3
+ node(:order_id) { |shipment| shipment.order.number }
4
+ child :shipping_method => :shipping_method do
5
+ attributes :name, :zone_id, :shipping_category_id
6
+ end
7
+
@@ -6,6 +6,8 @@ en:
6
6
  unauthorized: "You are not authorized to perform that action."
7
7
  invalid_resource: "Invalid resource. Please fix errors and try again."
8
8
  resource_not_found: "The resource you were looking for could not be found."
9
+ gateway_error: "There was a problem with the payment gateway: %{text}"
10
+ credit_over_limit: "This payment can only be credited up to %{limit}. Please specify an amount less than or equal to this number."
9
11
 
10
12
  order:
11
13
  could_not_transition: "The order could not be transitioned. Please fix the errors and try again."
data/config/routes.rb CHANGED
@@ -2,6 +2,10 @@ Spree::Core::Engine.routes.prepend do
2
2
  namespace :api do
3
3
  scope :module => :v1 do
4
4
  resources :products do
5
+ collection do
6
+ get :search
7
+ end
8
+
5
9
  resources :variants
6
10
  resources :images
7
11
  end
@@ -13,10 +17,28 @@ Spree::Core::Engine.routes.prepend do
13
17
  resources :orders do
14
18
  member do
15
19
  put :address
20
+ put :delivery
16
21
  end
17
22
 
18
23
  resources :line_items
24
+ resources :payments do
25
+ member do
26
+ put :authorize
27
+ put :purchase
28
+ put :void
29
+ put :credit
30
+ end
31
+ end
32
+
33
+ resources :shipments do
34
+ member do
35
+ put :ready
36
+ put :ship
37
+ end
38
+ end
19
39
  end
40
+
41
+ resources :countries, :only => [:index, :show]
20
42
  end
21
43
  end
22
44
  end
@@ -0,0 +1,5 @@
1
+ class ResizeApiKeyField < ActiveRecord::Migration
2
+ def change
3
+ change_column :spree_users, :api_key, :string, :limit => 48
4
+ end
5
+ end
@@ -13,6 +13,10 @@ module Spree
13
13
  end
14
14
  config.to_prepare &method(:activate).to_proc
15
15
 
16
+ def self.root
17
+ @root ||= Pathname.new(File.expand_path('../../../../', __FILE__))
18
+ end
19
+
16
20
  end
17
21
  end
18
22
  end
@@ -0,0 +1,35 @@
1
+ module Spree
2
+ module Api
3
+ module TestingSupport
4
+ module Helpers
5
+ def json_response
6
+ JSON.parse(response.body)
7
+ end
8
+
9
+ def assert_unauthorized!
10
+ json_response.should == { "error" => "You are not authorized to perform that action." }
11
+ response.status.should == 401
12
+ end
13
+
14
+ def stub_authentication!
15
+ controller.stub :check_for_api_key
16
+ Spree::User.stub :find_by_api_key => current_api_user
17
+ end
18
+
19
+ # This method can be overriden (with a let block) inside a context
20
+ # For instance, if you wanted to have an admin user instead.
21
+ def current_api_user
22
+ @current_api_user ||= stub_model(Spree::User, :email => "spree@example.com")
23
+ end
24
+
25
+ def image(filename)
26
+ File.open(Spree::Api::Engine.root + "spec/fixtures" + filename)
27
+ end
28
+
29
+ def upload_image(filename)
30
+ fixture_file_upload(image(filename).path)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,28 @@
1
+ module Spree
2
+ module Api
3
+ module TestingSupport
4
+ module Setup
5
+ def sign_in_as_admin!
6
+ let!(:current_api_user) do
7
+ user = stub_model(Spree::User)
8
+ user.should_receive(:has_role?).any_number_of_times.with("admin").and_return(true)
9
+ user
10
+ end
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
+ end
26
+ end
27
+ end
28
+ end
@@ -26,4 +26,16 @@ describe Spree::Api::V1::BaseController do
26
26
  json_response.should == { "error" => "Invalid API key (fake_key) specified." }
27
27
  end
28
28
  end
29
+
30
+ it "maps symantec keys to nested_attributes keys" do
31
+ klass = stub(:nested_attributes_options => { :line_items => {},
32
+ :bill_address => {} })
33
+ attributes = { 'line_items' => { :id => 1 },
34
+ 'bill_address' => { :id => 2 },
35
+ 'name' => 'test order' }
36
+
37
+ mapped = subject.map_nested_attributes_keys(klass, attributes)
38
+ mapped.has_key?('line_items_attributes').should be_true
39
+ mapped.has_key?('name').should be_true
40
+ end
29
41
  end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Api::V1::CountriesController do
5
+ render_views
6
+
7
+ before do
8
+ stub_authentication!
9
+ @state = Factory(:state)
10
+ @country = @state.country
11
+ end
12
+
13
+ it "gets all countries" do
14
+ api_get :index
15
+ json_response.first['country']['iso3'].should eq @country.iso3
16
+ end
17
+
18
+ it "includes states" do
19
+ api_get :show, :id => @country.id
20
+ states = json_response['country']['states']
21
+ states.first['state']['name'].should eq @state.name
22
+ end
23
+ end
24
+ end
@@ -11,7 +11,7 @@ module Spree
11
11
  end
12
12
 
13
13
  let(:product) { Factory(:product) }
14
- let(:attributes) { [:quantity, :price, :variant] }
14
+ let(:attributes) { [:id, :quantity, :price, :variant] }
15
15
  let(:resource_scoping) { { :order_id => order.to_param } }
16
16
 
17
17
  before do
@@ -36,7 +36,7 @@ module Spree
36
36
 
37
37
  it "can create an order" do
38
38
  variant = Factory(:variant)
39
- api_post :create, :order => { :line_items => { variant.to_param => 5 } }
39
+ api_post :create, :order => { :line_items => [{ :variant_id => variant.to_param, :quantity => 5 }] }
40
40
  response.status.should == 200
41
41
  order = Order.last
42
42
  order.line_items.count.should == 1
@@ -0,0 +1,162 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Spree::Api::V1::PaymentsController do
5
+ let!(:order) { Factory(:order) }
6
+ let!(:payment) { Factory(:payment, :order => order) }
7
+ let!(:attributes) { [:id, :source_type, :source_id, :amount,
8
+ :payment_method_id, :response_code, :state, :avs_response,
9
+ :created_at, :updated_at] }
10
+
11
+ let(:resource_scoping) { { :order_id => order.to_param } }
12
+ before do
13
+ stub_authentication!
14
+ end
15
+
16
+ context "as a user" do
17
+ context "when the order belongs to the user" do
18
+ before do
19
+ Order.any_instance.stub :user => current_api_user
20
+ end
21
+
22
+ it "can view the payments for their order" do
23
+ api_get :index
24
+ json_response.first.should have_attributes(attributes)
25
+ end
26
+
27
+ it "can learn how to create a new payment" do
28
+ api_get :new
29
+ json_response["attributes"].should == attributes.map(&:to_s)
30
+ json_response["payment_methods"].should_not be_empty
31
+ json_response["payment_methods"].first.should have_attributes([:id, :name, :description])
32
+ end
33
+
34
+ it "can create a new payment" do
35
+ api_post :create, :payment => { :payment_method_id => PaymentMethod.first.id, :amount => 50 }
36
+ response.status.should == 201
37
+ json_response.should have_attributes(attributes)
38
+ end
39
+
40
+ it "can view a pre-existing payment's details" do
41
+ api_get :show, :id => payment.to_param
42
+ json_response.should have_attributes(attributes)
43
+ end
44
+
45
+ it "cannot authorize a payment" do
46
+ api_put :authorize, :id => payment.to_param
47
+ assert_unauthorized!
48
+ end
49
+ end
50
+
51
+ context "when the order does not belong to the user" do
52
+ before do
53
+ Order.any_instance.stub :user => stub_model(User)
54
+ end
55
+
56
+ it "cannot view payments for somebody else's order" do
57
+ api_get :index, :order_id => order.to_param
58
+ assert_unauthorized!
59
+ end
60
+ end
61
+ end
62
+
63
+ context "as an admin" do
64
+ sign_in_as_admin!
65
+
66
+ it "can view the payments on any order" do
67
+ api_get :index
68
+ response.status.should == 200
69
+ json_response.first.should have_attributes(attributes)
70
+ end
71
+
72
+ context "for a given payment" do
73
+
74
+ it "can authorize" do
75
+ api_put :authorize, :id => payment.to_param
76
+ response.status.should == 200
77
+ payment.reload
78
+ payment.state.should == "pending"
79
+ end
80
+
81
+ it "returns a 422 status when authorization fails" do
82
+ fake_response = stub(:success? => false, :to_s => "Could not authorize card")
83
+ Spree::Gateway::Bogus.any_instance.should_receive(:authorize).and_return(fake_response)
84
+ api_put :authorize, :id => payment.to_param
85
+ response.status.should == 422
86
+ json_response["error"].should == "There was a problem with the payment gateway: Could not authorize card"
87
+ payment.reload
88
+ payment.state.should == "failed"
89
+ end
90
+
91
+ it "can purchase" do
92
+ api_put :purchase, :id => payment.to_param
93
+ response.status.should == 200
94
+ payment.reload
95
+ payment.state.should == "completed"
96
+ end
97
+
98
+ it "returns a 422 status when purchasing fails" do
99
+ fake_response = stub(:success? => false, :to_s => "Insufficient funds")
100
+ Spree::Gateway::Bogus.any_instance.should_receive(:purchase).and_return(fake_response)
101
+ api_put :purchase, :id => payment.to_param
102
+ response.status.should == 422
103
+ json_response["error"].should == "There was a problem with the payment gateway: Insufficient funds"
104
+
105
+ payment.reload
106
+ payment.state.should == "failed"
107
+ end
108
+
109
+ it "can void" do
110
+ api_put :void, :id => payment.to_param
111
+ response.status.should == 200
112
+ payment.reload
113
+ payment.state.should == "void"
114
+ end
115
+
116
+ it "returns a 422 status when voiding fails" do
117
+ fake_response = stub(:success? => false, :to_s => "NO REFUNDS")
118
+ Spree::Gateway::Bogus.any_instance.should_receive(:void).and_return(fake_response)
119
+ api_put :void, :id => payment.to_param
120
+ response.status.should == 422
121
+ json_response["error"].should == "There was a problem with the payment gateway: NO REFUNDS"
122
+
123
+ payment.reload
124
+ payment.state.should == "pending"
125
+ end
126
+
127
+ context "crediting" do
128
+ before do
129
+ payment.purchase!
130
+ end
131
+
132
+ it "can credit" do
133
+ api_put :credit, :id => payment.to_param
134
+ response.status.should == 200
135
+ payment.reload
136
+ payment.state.should == "completed"
137
+
138
+ # Ensur that a credit payment was created, and it has correct credit amount
139
+ credit_payment = Payment.where(:source_type => 'Spree::Payment', :source_id => payment.id).last
140
+ credit_payment.amount.to_f.should == -45.75
141
+ end
142
+
143
+ it "returns a 422 status when crediting fails" do
144
+ fake_response = stub(:success? => false, :to_s => "NO CREDIT FOR YOU")
145
+ Spree::Gateway::Bogus.any_instance.should_receive(:credit).and_return(fake_response)
146
+ api_put :credit, :id => payment.to_param
147
+ response.status.should == 422
148
+ json_response["error"].should == "There was a problem with the payment gateway: NO CREDIT FOR YOU"
149
+ end
150
+
151
+ it "cannot credit over credit_allowed limit" do
152
+ api_put :credit, :id => payment.to_param, :amount => 1000000
153
+ response.status.should == 422
154
+ json_response["error"].should == "This payment can only be credited up to 45.75. Please specify an amount less than or equal to this number."
155
+ end
156
+ end
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+ end
@@ -39,6 +39,13 @@ module Spree
39
39
  end
40
40
  end
41
41
 
42
+ it "can search for products" do
43
+ Factory(:product, :name => "The best product in the world")
44
+ api_get :search, :q => { :name_cont => "best" }
45
+ json_response["products"].first.should have_attributes(attributes)
46
+ json_response["count"].should == 1
47
+ end
48
+
42
49
  it "gets a single product" do
43
50
  product.master.images.create!(:attachment => image("thinking-cat.jpg"))
44
51
  api_get :show, :id => product.to_param
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Api::V1::ShipmentsController do
4
+ let!(:shipment) { Factory(:shipment) }
5
+ let!(:attributes) { [:id, :tracking, :number, :cost, :shipped_at] }
6
+
7
+ before do
8
+ shipment.order.payment_state == 'paid'
9
+ stub_authentication!
10
+ end
11
+
12
+ context "working with a shipment" do
13
+ let!(:resource_scoping) { { :order_id => shipment.order.to_param, :id => shipment.to_param } }
14
+
15
+ it "can make a shipment ready" do
16
+ api_put :ready
17
+ json_response.should have_attributes(attributes)
18
+ json_response["shipment"]["state"].should == "ready"
19
+ end
20
+
21
+ context "can transition a shipment from ready to ship" do
22
+ before do
23
+ shipment.order.update_attribute(:payment_state, 'paid')
24
+ shipment.update!(shipment.order)
25
+ end
26
+
27
+ it "can transition a shipment from ready to ship" do
28
+ shipment.reload
29
+ api_put :ship, :order_id => shipment.order.to_param, :id => shipment.to_param, :shipment => { :tracking => "123123" }
30
+ json_response.should have_attributes(attributes)
31
+ json_response["shipment"]["state"].should == "shipped"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -13,7 +13,8 @@ module Spree
13
13
  end
14
14
  let!(:attributes) { [:id, :name, :count_on_hand,
15
15
  :sku, :price, :weight, :height,
16
- :width, :depth, :is_master, :cost_price] }
16
+ :width, :depth, :is_master, :cost_price,
17
+ :permalink] }
17
18
 
18
19
  before do
19
20
  stub_authentication!
@@ -7,7 +7,7 @@ module Spree
7
7
  it 'can build an order from API parameters' do
8
8
 
9
9
  Spree::Variant.should_receive(:find).and_return(stub_model(Variant, :id => 1))
10
- order = Order.build_from_api(user, { :line_items => { 1 => 5 }})
10
+ order = Order.build_from_api(user, { :line_items_attributes => [{ :variant_id => 1, :quantity => 5 }]})
11
11
 
12
12
  order.user.should == user
13
13
  line_item = order.line_items.first
data/spec/spec_helper.rb CHANGED
@@ -10,19 +10,12 @@ Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
10
10
 
11
11
  require 'spree/core/testing_support/factories'
12
12
 
13
+ require 'spree/api/testing_support/helpers'
14
+ require 'spree/api/testing_support/setup'
15
+
13
16
  RSpec.configure do |config|
14
17
  config.backtrace_clean_patterns = [/gems\/activesupport/, /gems\/actionpack/, /gems\/rspec/]
15
18
 
16
- config.before(:suite) do
17
- DatabaseCleaner.strategy = :transaction
18
- DatabaseCleaner.clean_with(:truncation)
19
- end
20
-
21
- config.before(:each) do
22
- DatabaseCleaner.start
23
- end
24
-
25
- config.after(:each) do
26
- DatabaseCleaner.clean
27
- end
19
+ config.include Spree::Api::TestingSupport::Helpers, :type => :controller
20
+ config.extend Spree::Api::TestingSupport::Setup, :type => :controller
28
21
  end
@@ -0,0 +1,14 @@
1
+ RSpec.configure do |config|
2
+ config.before(:suite) do
3
+ DatabaseCleaner.strategy = :transaction
4
+ DatabaseCleaner.clean_with(:truncation)
5
+ end
6
+
7
+ config.before(:each) do
8
+ DatabaseCleaner.start
9
+ end
10
+
11
+ config.after(:each) do
12
+ DatabaseCleaner.clean
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+
2
+
3
+
4
+
5
+ RSpec::Matchers.define :have_attributes do |expected_attributes|
6
+ match do |actual|
7
+ # actual is a Hash object representing an object, like this:
8
+ # { "product" => { "name" => "Product #1" } }
9
+ actual_attributes = actual.values.first.keys.map(&:to_sym)
10
+ expected_attributes.map(&:to_sym).all? { |attr| actual_attributes.include?(attr) }
11
+ end
12
+ end
13
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0.rc1
4
+ version: 1.1.0.rc2
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,33 +9,33 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-09 00:00:00.000000000 Z
12
+ date: 2012-04-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: spree_core
16
- requirement: &70247364495880 !ruby/object:Gem::Requirement
16
+ requirement: &70332729484720 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - =
20
20
  - !ruby/object:Gem::Version
21
- version: 1.1.0.rc1
21
+ version: 1.1.0.rc2
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70247364495880
24
+ version_requirements: *70332729484720
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: spree_auth
27
- requirement: &70247364495000 !ruby/object:Gem::Requirement
27
+ requirement: &70332729479800 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - =
31
31
  - !ruby/object:Gem::Version
32
- version: 1.1.0.rc1
32
+ version: 1.1.0.rc2
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70247364495000
35
+ version_requirements: *70332729479800
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rabl
38
- requirement: &70247364494060 !ruby/object:Gem::Requirement
38
+ requirement: &70332729477860 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - =
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 0.6.5
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70247364494060
46
+ version_requirements: *70332729477860
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rspec-rails
49
- requirement: &70247364509720 !ruby/object:Gem::Requirement
49
+ requirement: &70332729346420 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - =
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 2.9.0
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70247364509720
57
+ version_requirements: *70332729346420
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: database_cleaner
60
- requirement: &70247364509000 !ruby/object:Gem::Requirement
60
+ requirement: &70332729344960 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70247364509000
68
+ version_requirements: *70332729344960
69
69
  description: Spree's API
70
70
  email:
71
71
  - ryan@spreecommerce.com
@@ -79,16 +79,23 @@ files:
79
79
  - README.md
80
80
  - Rakefile
81
81
  - app/controllers/spree/api/v1/base_controller.rb
82
+ - app/controllers/spree/api/v1/countries_controller.rb
82
83
  - app/controllers/spree/api/v1/images_controller.rb
83
84
  - app/controllers/spree/api/v1/line_items_controller.rb
84
85
  - app/controllers/spree/api/v1/orders_controller.rb
86
+ - app/controllers/spree/api/v1/payments_controller.rb
85
87
  - app/controllers/spree/api/v1/products_controller.rb
88
+ - app/controllers/spree/api/v1/shipments_controller.rb
86
89
  - app/controllers/spree/api/v1/variants_controller.rb
87
90
  - app/helpers/spree/api/api_helpers.rb
88
91
  - app/models/spree/line_item_decorator.rb
89
92
  - app/models/spree/option_value_decorator.rb
90
93
  - app/models/spree/order_decorator.rb
94
+ - app/models/spree/payment_decorator.rb
91
95
  - app/models/spree/user_decorator.rb
96
+ - app/views/spree/api/v1/countries/index.rabl
97
+ - app/views/spree/api/v1/countries/show.rabl
98
+ - app/views/spree/api/v1/errors/gateway_error.rabl
92
99
  - app/views/spree/api/v1/errors/invalid_api_key.rabl
93
100
  - app/views/spree/api/v1/errors/invalid_resource.rabl
94
101
  - app/views/spree/api/v1/errors/must_specify_api_key.rabl
@@ -106,10 +113,15 @@ files:
106
113
  - app/views/spree/api/v1/orders/invalid_shipping_method.rabl
107
114
  - app/views/spree/api/v1/orders/payment.rabl
108
115
  - app/views/spree/api/v1/orders/show.rabl
116
+ - app/views/spree/api/v1/payments/credit_over_limit.rabl
117
+ - app/views/spree/api/v1/payments/index.rabl
118
+ - app/views/spree/api/v1/payments/new.rabl
119
+ - app/views/spree/api/v1/payments/show.rabl
109
120
  - app/views/spree/api/v1/products/index.rabl
110
121
  - app/views/spree/api/v1/products/new.rabl
111
122
  - app/views/spree/api/v1/products/product.rabl
112
123
  - app/views/spree/api/v1/products/show.rabl
124
+ - app/views/spree/api/v1/shipments/show.rabl
113
125
  - app/views/spree/api/v1/variants/index.rabl
114
126
  - app/views/spree/api/v1/variants/new.rabl
115
127
  - app/views/spree/api/v1/variants/show.rabl
@@ -117,24 +129,31 @@ files:
117
129
  - config/locales/en.yml
118
130
  - config/routes.rb
119
131
  - db/migrate/20100107141738_add_api_key_to_spree_users.rb
132
+ - db/migrate/20120411123334_resize_api_key_field.rb
120
133
  - lib/spree/api.rb
121
134
  - lib/spree/api/controller_setup.rb
122
135
  - lib/spree/api/engine.rb
136
+ - lib/spree/api/testing_support/helpers.rb
137
+ - lib/spree/api/testing_support/setup.rb
123
138
  - lib/spree/api/version.rb
124
139
  - lib/spree_api.rb
125
140
  - script/rails
126
141
  - spec/controllers/spree/api/v1/base_controller_spec.rb
142
+ - spec/controllers/spree/api/v1/countries_controller_spec.rb
127
143
  - spec/controllers/spree/api/v1/images_controller_spec.rb
128
144
  - spec/controllers/spree/api/v1/line_items_controller_spec.rb
129
145
  - spec/controllers/spree/api/v1/orders_controller_spec.rb
146
+ - spec/controllers/spree/api/v1/payments_controller_spec.rb
130
147
  - spec/controllers/spree/api/v1/products_controller_spec.rb
148
+ - spec/controllers/spree/api/v1/shipments_controller_spec.rb
131
149
  - spec/controllers/spree/api/v1/variants_controller_spec.rb
132
150
  - spec/fixtures/thinking-cat.jpg
133
151
  - spec/models/spree/order_spec.rb
134
152
  - spec/models/spree/user_spec.rb
135
153
  - spec/spec_helper.rb
136
- - spec/support/api_helpers.rb
137
154
  - spec/support/controller_hacks.rb
155
+ - spec/support/database_cleaner.rb
156
+ - spec/support/have_attributes_matcher.rb
138
157
  - spree_api.gemspec
139
158
  homepage: ''
140
159
  licenses: []
@@ -150,7 +169,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
150
169
  version: '0'
151
170
  segments:
152
171
  - 0
153
- hash: -3641673168610111779
172
+ hash: -1673968369250392994
154
173
  required_rubygems_version: !ruby/object:Gem::Requirement
155
174
  none: false
156
175
  requirements:
@@ -165,14 +184,18 @@ specification_version: 3
165
184
  summary: Spree's API
166
185
  test_files:
167
186
  - spec/controllers/spree/api/v1/base_controller_spec.rb
187
+ - spec/controllers/spree/api/v1/countries_controller_spec.rb
168
188
  - spec/controllers/spree/api/v1/images_controller_spec.rb
169
189
  - spec/controllers/spree/api/v1/line_items_controller_spec.rb
170
190
  - spec/controllers/spree/api/v1/orders_controller_spec.rb
191
+ - spec/controllers/spree/api/v1/payments_controller_spec.rb
171
192
  - spec/controllers/spree/api/v1/products_controller_spec.rb
193
+ - spec/controllers/spree/api/v1/shipments_controller_spec.rb
172
194
  - spec/controllers/spree/api/v1/variants_controller_spec.rb
173
195
  - spec/fixtures/thinking-cat.jpg
174
196
  - spec/models/spree/order_spec.rb
175
197
  - spec/models/spree/user_spec.rb
176
198
  - spec/spec_helper.rb
177
- - spec/support/api_helpers.rb
178
199
  - spec/support/controller_hacks.rb
200
+ - spec/support/database_cleaner.rb
201
+ - spec/support/have_attributes_matcher.rb
@@ -1,68 +0,0 @@
1
- module ApiHelpers
2
- def json_response
3
- JSON.parse(response.body)
4
- end
5
-
6
- def assert_unauthorized!
7
- json_response.should == { "error" => "You are not authorized to perform that action." }
8
- response.status.should == 401
9
- end
10
-
11
- def stub_authentication!
12
- controller.stub :check_for_api_key
13
- Spree::User.stub :find_by_api_key => current_api_user
14
- end
15
-
16
- # This method can be overriden (with a let block) inside a context
17
- # For instance, if you wanted to have an admin user instead.
18
- def current_api_user
19
- @current_api_user ||= stub_model(Spree::User, :email => "spree@example.com")
20
- end
21
-
22
- def image(filename)
23
- path = Pathname.new(__FILE__) + "../../../spec/fixtures" + filename
24
- File.open(path)
25
- end
26
-
27
- def upload_image(filename)
28
- fixture_file_upload(image(filename).path)
29
- end
30
- end
31
-
32
- module ApiTestSetup
33
- def sign_in_as_admin!
34
- let!(:current_api_user) do
35
- user = stub_model(Spree::User)
36
- user.should_receive(:has_role?).any_number_of_times.with("admin").and_return(true)
37
- user
38
- end
39
- end
40
-
41
- # Default kaminari's pagination to a certain range
42
- # Means that you don't need to create 25 objects to test pagination
43
- def default_per_page(count)
44
- before do
45
- @current_default_per_page = Kaminari.config.default_per_page
46
- Kaminari.config.default_per_page = 1
47
- end
48
-
49
- after do
50
- Kaminari.config.default_per_page = @current_default_per_page
51
- end
52
- end
53
- end
54
-
55
- RSpec.configure do |config|
56
- config.include ApiHelpers, :type => :controller
57
- config.extend ApiTestSetup, :type => :controller
58
- end
59
-
60
- RSpec::Matchers.define :have_attributes do |expected_attributes|
61
- match do |actual|
62
- # actual is a Hash object representing an object, like this:
63
- # { "product" => { "name" => "Product #1" } }
64
- actual_attributes = actual.values.first.keys.map(&:to_sym)
65
- expected_attributes.map(&:to_sym).all? { |attr| actual_attributes.include?(attr) }
66
- end
67
- end
68
-