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.
- data/app/controllers/spree/api/v1/base_controller.rb +11 -1
- data/app/controllers/spree/api/v1/countries_controller.rb +15 -0
- data/app/controllers/spree/api/v1/orders_controller.rb +16 -1
- data/app/controllers/spree/api/v1/payments_controller.rb +73 -0
- data/app/controllers/spree/api/v1/products_controller.rb +5 -0
- data/app/controllers/spree/api/v1/shipments_controller.rb +31 -0
- data/app/helpers/spree/api/api_helpers.rb +14 -2
- data/app/models/spree/order_decorator.rb +3 -3
- data/app/models/spree/payment_decorator.rb +0 -0
- data/app/views/spree/api/v1/countries/index.rabl +2 -0
- data/app/views/spree/api/v1/countries/show.rabl +5 -0
- data/app/views/spree/api/v1/errors/gateway_error.rabl +2 -0
- data/app/views/spree/api/v1/errors/invalid_resource.rabl +1 -1
- data/app/views/spree/api/v1/line_items/new.rabl +1 -1
- data/app/views/spree/api/v1/line_items/show.rabl +1 -1
- data/app/views/spree/api/v1/orders/address.rabl +3 -0
- data/app/views/spree/api/v1/orders/payment.rabl +4 -0
- data/app/views/spree/api/v1/orders/show.rabl +23 -0
- data/app/views/spree/api/v1/payments/credit_over_limit.rabl +2 -0
- data/app/views/spree/api/v1/payments/index.rabl +2 -0
- data/app/views/spree/api/v1/payments/new.rabl +6 -0
- data/app/views/spree/api/v1/payments/show.rabl +2 -0
- data/app/views/spree/api/v1/shipments/show.rabl +7 -0
- data/config/locales/en.yml +2 -0
- data/config/routes.rb +22 -0
- data/db/migrate/20120411123334_resize_api_key_field.rb +5 -0
- data/lib/spree/api/engine.rb +4 -0
- data/lib/spree/api/testing_support/helpers.rb +35 -0
- data/lib/spree/api/testing_support/setup.rb +28 -0
- data/spec/controllers/spree/api/v1/base_controller_spec.rb +12 -0
- data/spec/controllers/spree/api/v1/countries_controller_spec.rb +24 -0
- data/spec/controllers/spree/api/v1/line_items_controller_spec.rb +1 -1
- data/spec/controllers/spree/api/v1/orders_controller_spec.rb +1 -1
- data/spec/controllers/spree/api/v1/payments_controller_spec.rb +162 -0
- data/spec/controllers/spree/api/v1/products_controller_spec.rb +7 -0
- data/spec/controllers/spree/api/v1/shipments_controller_spec.rb +35 -0
- data/spec/controllers/spree/api/v1/variants_controller_spec.rb +2 -1
- data/spec/models/spree/order_spec.rb +1 -1
- data/spec/spec_helper.rb +5 -12
- data/spec/support/database_cleaner.rb +14 -0
- data/spec/support/have_attributes_matcher.rb +13 -0
- metadata +40 -17
- 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
|
-
|
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,
|
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
|
@@ -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[:
|
5
|
-
|
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
|
@@ -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
|
data/config/locales/en.yml
CHANGED
@@ -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
|
data/lib/spree/api/engine.rb
CHANGED
@@ -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
|
@@ -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
|
@@ -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, { :
|
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.
|
17
|
-
|
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.
|
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-
|
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: &
|
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.
|
21
|
+
version: 1.1.0.rc2
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70332729484720
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: spree_auth
|
27
|
-
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.
|
32
|
+
version: 1.1.0.rc2
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70332729479800
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rabl
|
38
|
-
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: *
|
46
|
+
version_requirements: *70332729477860
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec-rails
|
49
|
-
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: *
|
57
|
+
version_requirements: *70332729346420
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: database_cleaner
|
60
|
-
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: *
|
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: -
|
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
|
data/spec/support/api_helpers.rb
DELETED
@@ -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
|
-
|