spree_api 1.1.0.rc1 → 1.1.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
-