spree_api 1.0.7 → 1.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +6 -0
  3. data/LICENSE +19 -23
  4. data/README.md +22 -10
  5. data/Rakefile +30 -0
  6. data/app/controllers/spree/api/v1/base_controller.rb +71 -0
  7. data/app/controllers/spree/api/v1/images_controller.rb +46 -0
  8. data/app/controllers/spree/api/v1/line_items_controller.rb +40 -0
  9. data/app/controllers/spree/api/v1/orders_controller.rb +53 -0
  10. data/app/controllers/spree/api/v1/products_controller.rb +46 -0
  11. data/app/controllers/spree/api/v1/variants_controller.rb +56 -0
  12. data/app/helpers/spree/api/api_helpers.rb +44 -0
  13. data/app/models/spree/line_item_decorator.rb +3 -0
  14. data/app/models/spree/option_value_decorator.rb +5 -0
  15. data/app/models/spree/order_decorator.rb +12 -0
  16. data/app/models/spree/user_decorator.rb +11 -0
  17. data/app/views/spree/api/v1/errors/invalid_api_key.rabl +2 -0
  18. data/app/views/spree/api/v1/errors/invalid_resource.rabl +3 -0
  19. data/app/views/spree/api/v1/errors/must_specify_api_key.rabl +2 -0
  20. data/app/views/spree/api/v1/errors/not_found.rabl +2 -0
  21. data/app/views/spree/api/v1/errors/unauthorized.rabl +2 -0
  22. data/app/views/spree/api/v1/images/show.rabl +2 -0
  23. data/app/views/spree/api/v1/line_items/new.rabl +3 -0
  24. data/app/views/spree/api/v1/line_items/show.rabl +5 -0
  25. data/app/views/spree/api/v1/orders/address.rabl +0 -0
  26. data/app/views/spree/api/v1/orders/cart.rabl +0 -0
  27. data/app/views/spree/api/v1/orders/complete.rabl +0 -0
  28. data/app/views/spree/api/v1/orders/could_not_transition.rabl +3 -0
  29. data/app/views/spree/api/v1/orders/delivery.rabl +3 -0
  30. data/app/views/spree/api/v1/orders/index.rabl +7 -0
  31. data/app/views/spree/api/v1/orders/invalid_shipping_method.rabl +2 -0
  32. data/app/views/spree/api/v1/orders/payment.rabl +0 -0
  33. data/app/views/spree/api/v1/orders/show.rabl +3 -0
  34. data/app/views/spree/api/v1/products/index.rabl +8 -0
  35. data/app/views/spree/api/v1/products/new.rabl +3 -0
  36. data/app/views/spree/api/v1/products/product.rabl +1 -0
  37. data/app/views/spree/api/v1/products/show.rabl +21 -0
  38. data/app/views/spree/api/v1/variants/index.rabl +3 -0
  39. data/app/views/spree/api/v1/variants/new.rabl +2 -0
  40. data/app/views/spree/api/v1/variants/show.rabl +3 -0
  41. data/app/views/spree/api/v1/variants/variant.rabl +1 -0
  42. data/config/locales/en.yml +11 -15
  43. data/config/routes.rb +14 -28
  44. data/db/migrate/{20100107141738_add_api_key_to_users.rb → 20100107141738_add_api_key_to_spree_users.rb} +1 -1
  45. data/lib/spree/api.rb +7 -4
  46. data/lib/spree/api/controller_setup.rb +27 -0
  47. data/lib/spree/api/engine.rb +3 -13
  48. data/lib/spree/api/version.rb +5 -0
  49. data/lib/spree_api.rb +0 -2
  50. data/script/rails +5 -0
  51. data/spec/controllers/spree/api/v1/base_controller_spec.rb +29 -0
  52. data/spec/controllers/spree/api/v1/images_controller_spec.rb +50 -0
  53. data/spec/controllers/spree/api/v1/line_items_controller_spec.rb +77 -0
  54. data/spec/controllers/spree/api/v1/orders_controller_spec.rb +148 -0
  55. data/spec/controllers/spree/api/v1/products_controller_spec.rb +159 -0
  56. data/spec/controllers/spree/api/v1/variants_controller_spec.rb +90 -0
  57. data/spec/fixtures/thinking-cat.jpg +0 -0
  58. data/spec/models/spree/order_spec.rb +18 -0
  59. data/spec/models/spree/user_spec.rb +19 -0
  60. data/spec/spec_helper.rb +28 -0
  61. data/spec/support/api_helpers.rb +68 -0
  62. data/spec/support/controller_hacks.rb +27 -0
  63. data/spree_api.gemspec +24 -0
  64. metadata +123 -56
  65. data/app/assets/javascripts/admin/spree.js +0 -4
  66. data/app/assets/javascripts/admin/spree_api.js +0 -2
  67. data/app/assets/javascripts/store/spree.js +0 -4
  68. data/app/assets/javascripts/store/spree_api.js +0 -2
  69. data/app/assets/stylesheets/admin/spree.css +0 -6
  70. data/app/assets/stylesheets/admin/spree_api.css +0 -4
  71. data/app/assets/stylesheets/store/spree.css +0 -6
  72. data/app/assets/stylesheets/store/spree_api.css +0 -4
  73. data/app/controllers/spree/admin/users_controller_decorator.rb +0 -17
  74. data/app/controllers/spree/api/base_controller.rb +0 -172
  75. data/app/controllers/spree/api/countries_controller.rb +0 -3
  76. data/app/controllers/spree/api/inventory_units_controller.rb +0 -18
  77. data/app/controllers/spree/api/line_items_controller.rb +0 -20
  78. data/app/controllers/spree/api/orders_controller.rb +0 -19
  79. data/app/controllers/spree/api/products_controller.rb +0 -14
  80. data/app/controllers/spree/api/shipments_controller.rb +0 -35
  81. data/app/controllers/spree/api/states_controller.rb +0 -8
  82. data/app/models/line_item_decorator.rb +0 -7
  83. data/app/models/order_decorator.rb +0 -9
  84. data/app/models/shipment_decorator.rb +0 -9
  85. data/app/models/user_decorator.rb +0 -19
  86. data/app/overrides/api_admin_user_edit_form.rb +0 -6
  87. data/app/views/spree/admin/users/_api_fields.html.erb +0 -16
@@ -0,0 +1,44 @@
1
+ module Spree
2
+ module Api
3
+ module ApiHelpers
4
+ def required_fields_for(model)
5
+ required_fields = model._validators.select do |field, validations|
6
+ validations.any? { |v| v.is_a?(ActiveModel::Validations::PresenceValidator) }
7
+ end.map(&:first) # get fields that are invalid
8
+ # Permalinks presence is validated, but are really automatically generated
9
+ # Therefore we shouldn't tell API clients that they MUST send one through
10
+ required_fields.map!(&:to_s).delete("permalink")
11
+ required_fields
12
+ end
13
+
14
+ def product_attributes
15
+ [:id, :name, :description, :price,
16
+ :available_on, :permalink, :count_on_hand, :meta_description, :meta_keywords]
17
+ end
18
+
19
+ def variant_attributes
20
+ [:id, :name, :count_on_hand, :sku, :price, :weight, :height, :width, :depth, :is_master, :cost_price]
21
+ end
22
+
23
+ def image_attributes
24
+ [:id, :position, :attachment_content_type, :attachment_file_name, :type, :attachment_updated_at, :attachment_width, :attachment_height, :alt]
25
+ end
26
+
27
+ def option_value_attributes
28
+ [:id, :name, :presentation, :option_type_name, :option_type_id]
29
+ end
30
+
31
+ def order_attributes
32
+ [:id, :number, :item_total, :total, :state, :adjustment_total, :credit_total, :user_id, :created_at, :updated_at, :completed_at, :payment_total, :shipment_state, :payment_state, :email, :special_instructions]
33
+ end
34
+
35
+ def line_item_attributes
36
+ [:quantity, :price, :variant_id]
37
+ end
38
+
39
+ def option_type_attributes
40
+ [:id, :name, :presentation, :position]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ Spree::LineItem.class_eval do
2
+ attr_accessible :quantity, :variant_id, :as => :api
3
+ end
@@ -0,0 +1,5 @@
1
+ Spree::OptionValue.class_eval do
2
+ def option_type_name
3
+ option_type.name
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ Spree::Order.class_eval do
2
+ def self.build_from_api(user, params)
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)
7
+ end
8
+ order.user = user
9
+ order.email = user.email
10
+ order
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ Spree::User.class_eval do
2
+ def generate_api_key!
3
+ self.api_key = SecureRandom.hex(24)
4
+ save!
5
+ end
6
+
7
+ def clear_api_key!
8
+ self.api_key = nil
9
+ save!
10
+ end
11
+ end
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:error) { I18n.t(:invalid_api_key, :key => api_key, :scope => "spree.api") }
@@ -0,0 +1,3 @@
1
+ object false
2
+ node(:error) { I18n.t(:invalid_resource, :scope => "spree.api") }
3
+ node(:errors) { @product.errors }
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:error) { I18n.t(:must_specify_api_key, :scope => "spree.api") }
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:error) { I18n.t(:resource_not_found, :scope => "spree.api") }
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:error) { I18n.t(:unauthorized, :scope => "spree.api") }
@@ -0,0 +1,2 @@
1
+ object @image
2
+ attributes *image_attributes
@@ -0,0 +1,3 @@
1
+ object false
2
+ node(:attributes) { [*line_item_attributes] }
3
+ node(:required_attributes) { [:variant_id, :quantity] }
@@ -0,0 +1,5 @@
1
+ object @line_item
2
+ attributes :quantity, :price
3
+ child :variant do
4
+ extends "spree/api/v1/variants/variant"
5
+ end
File without changes
File without changes
File without changes
@@ -0,0 +1,3 @@
1
+ object false
2
+ node(:error) { I18n.t(:could_not_transition, :scope => "spree.api.order") }
3
+ node(:errors) { @order.errors }
@@ -0,0 +1,3 @@
1
+ child(:rate_hash => :shipping_methods) do
2
+ attributes :id, :name, :cost
3
+ end
@@ -0,0 +1,7 @@
1
+ object false
2
+ child(@orders) do
3
+ attributes *order_attributes
4
+ end
5
+ node(:count) { @orders.total_count }
6
+ node(:current_page) { params[:page] || 1 }
7
+ node(:pages) { @orders.num_pages }
@@ -0,0 +1,2 @@
1
+ object false
2
+ node(:errors) { [I18n.t(:invalid_shipping_method, :scope => "spree.api.order")] }
File without changes
@@ -0,0 +1,3 @@
1
+ object @order
2
+ attributes *order_attributes
3
+ extends "spree/api/v1/orders/#{@order.state}"
@@ -0,0 +1,8 @@
1
+ object false
2
+ node(:count) { @products.total_count }
3
+ node(:current_page) { params[:page] ? params[:page].to_i : 1 }
4
+ node(:pages) { @products.num_pages }
5
+ child(@products) do
6
+ extends "spree/api/v1/products/show"
7
+ end
8
+
@@ -0,0 +1,3 @@
1
+ object false
2
+ node(:attributes) { [*product_attributes] }
3
+ node(:required_attributes) { required_fields_for(Spree::Product) }
@@ -0,0 +1 @@
1
+ attributes *product_attributes
@@ -0,0 +1,21 @@
1
+ object @product
2
+ attributes *product_attributes
3
+ child :variants_including_master => :variants do
4
+ attributes *variant_attributes
5
+
6
+ child :option_values => :option_values do
7
+ attributes *option_value_attributes
8
+ end
9
+ end
10
+
11
+ child :images => :images do
12
+ attributes *image_attributes
13
+ end
14
+
15
+ child :option_types => :option_types do
16
+ attributes *option_type_attributes
17
+
18
+ child :option_values => :option_values do
19
+ attributes *option_value_attributes
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ collection @variants
2
+ attributes *variant_attributes
3
+ child(:option_values => :option_values) { attributes *option_value_attributes }
@@ -0,0 +1,2 @@
1
+ node(:attributes) { [*variant_attributes] }
2
+ node(:required_attributes) { [] }
@@ -0,0 +1,3 @@
1
+ object @variant
2
+ extends "spree/api/v1/variants/variant"
3
+ child(:option_values => :option_values) { attributes *option_value_attributes }
@@ -0,0 +1 @@
1
+ attributes *variant_attributes
@@ -1,16 +1,12 @@
1
- ---
2
1
  en:
3
- api: "API"
4
- api:
5
- access: "API Access"
6
- clear_key: "Clear API key"
7
- errors:
8
- invalid_event: "Invalid event name, valid names are %{events}"
9
- invalid_event_for_object: "Valid event name but not allowed for this object, valid names are %{events}"
10
- missing_event: "No event name supplied"
11
- generate_key: "Generate API key"
12
- key: "API Key"
13
- regenerate_key: "Regenerate API key"
14
- no_key: "No key defined"
15
- key_generated: "API key generated"
16
- key_cleared: "API key cleared"
2
+ spree:
3
+ api:
4
+ must_specify_api_key: "You must specify an API key."
5
+ invalid_api_key: "Invalid API key (%{key}) specified."
6
+ unauthorized: "You are not authorized to perform that action."
7
+ invalid_resource: "Invalid resource. Please fix errors and try again."
8
+ resource_not_found: "The resource you were looking for could not be found."
9
+
10
+ order:
11
+ could_not_transition: "The order could not be transitioned. Please fix the errors and try again."
12
+ invalid_shipping_method: "Invalid shipping method specified."
data/config/routes.rb CHANGED
@@ -1,36 +1,22 @@
1
1
  Spree::Core::Engine.routes.prepend do
2
- namespace :admin do
3
- resources :users do
4
- member do
5
- put :generate_api_key
6
- put :clear_api_key
2
+ namespace :api do
3
+ scope :module => :v1 do
4
+ resources :products do
5
+ resources :variants
6
+ resources :images
7
7
  end
8
- end
9
- end
10
8
 
11
- namespace :api do
12
- resources :shipments, :except => [:new,:edit] do
13
- put :event, :on => :member
14
- resources :inventory_units, :except => [:new,:edit] do
15
- put :event, :on => :member
9
+ resources :variants, :only => [:index] do
10
+ resources :images
16
11
  end
17
- end
18
- resources :orders, :except => [:new,:edit] do
19
- put :event, :on => :member
20
- resources :shipments, :except => [:new,:edit]
21
- resources :line_items, :except => [:new,:edit]
22
- resources :inventory_units, :except => [:new,:edit] do
23
- put :event, :on => :member
12
+
13
+ resources :orders do
14
+ member do
15
+ put :address
16
+ end
17
+
18
+ resources :line_items
24
19
  end
25
20
  end
26
- resources :inventory_units, :except => [:new,:edit] do
27
- put :event, :on => :member
28
- end
29
- resources :products, :except => [:new,:edit]
30
- resources :countries, :except => [:new,:edit] do
31
- resources :states, :except => [:new,:edit]
32
- end
33
- resources :states, :except => [:new,:edit]
34
21
  end
35
-
36
22
  end
@@ -1,4 +1,4 @@
1
- class AddApiKeyToUsers < ActiveRecord::Migration
1
+ class AddApiKeyToSpreeUsers < ActiveRecord::Migration
2
2
  def change
3
3
  add_column :spree_users, :api_key, :string, :limit => 40
4
4
  end
data/lib/spree/api.rb CHANGED
@@ -1,10 +1,13 @@
1
- require 'spree_core'
2
- require 'spree_auth'
1
+ require 'spree/core'
2
+ require 'spree/auth'
3
+
4
+ require 'spree/api/controller_setup'
5
+
6
+ require 'rabl'
3
7
 
4
8
  module Spree
5
9
  module Api
6
-
7
10
  end
8
11
  end
9
12
 
10
- require 'spree/api/engine'
13
+ require 'spree/api/engine'
@@ -0,0 +1,27 @@
1
+ module Spree
2
+ module Api
3
+ module ControllerSetup
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ include AbstractController::Rendering
7
+ include AbstractController::ViewPaths
8
+ include AbstractController::Callbacks
9
+ include AbstractController::Helpers
10
+
11
+ include ActiveSupport::Rescuable
12
+
13
+ include ActionController::Rendering
14
+ include ActionController::ImplicitRender
15
+ include ActionController::Rescue
16
+ include ActionController::MimeResponds
17
+
18
+ include CanCan::ControllerAdditions
19
+ append_view_path File.expand_path("../../../app/views", File.dirname(__FILE__))
20
+ append_view_path Rails.root + "app/views"
21
+
22
+ respond_to :json
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,5 @@
1
+ require 'rails/engine'
2
+
1
3
  module Spree
2
4
  module Api
3
5
  class Engine < Rails::Engine
@@ -9,20 +11,8 @@ module Spree
9
11
  Rails.configuration.cache_classes ? require(c) : load(c)
10
12
  end
11
13
  end
12
-
13
- config.autoload_paths += %W(#{config.root}/lib)
14
14
  config.to_prepare &method(:activate).to_proc
15
- end
16
- end
17
- end
18
15
 
19
- # add helper to all the base controllers
20
- # Spree::BaseController includes Spree::Core::ControllerHelpers
21
- require 'spree/core/controller_helpers'
22
- class << Spree::Core::ControllerHelpers
23
- def included_with_analytics(receiver)
24
- included_without_analytics(receiver)
25
- receiver.send :helper, 'spree/analytics'
16
+ end
26
17
  end
27
- alias_method_chain :included, :analytics
28
18
  end
@@ -0,0 +1,5 @@
1
+ module Spree
2
+ module Api
3
+ VERSION = "1.1.0.beta"
4
+ end
5
+ end
data/lib/spree_api.rb CHANGED
@@ -1,3 +1 @@
1
1
  require 'spree/api'
2
- require 'spree/core'
3
- require 'spree_auth'
data/script/rails ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ ENGINE_PATH = File.expand_path('../..', __FILE__)
5
+ load File.expand_path('../../spec/dummy/script/rails', __FILE__)
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Api::V1::BaseController do
4
+ controller(Spree::Api::V1::BaseController) do
5
+ def index
6
+ render :json => { "products" => [] }
7
+ end
8
+ end
9
+
10
+ context "cannot make a request to the API" do
11
+ it "without an API key" do
12
+ api_get :index
13
+ json_response.should == { "error" => "You must specify an API key." }
14
+ response.status.should == 401
15
+ end
16
+
17
+ it "with an invalid API key" do
18
+ request.env["X-Spree-Token"] = "fake_key"
19
+ get :index, {}
20
+ json_response.should == { "error" => "Invalid API key (fake_key) specified." }
21
+ response.status.should == 401
22
+ end
23
+
24
+ it "using an invalid token param" do
25
+ get :index, :token => "fake_key"
26
+ json_response.should == { "error" => "Invalid API key (fake_key) specified." }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Spree::Api::V1::ImagesController do
5
+ render_views
6
+
7
+ let!(:product) { Factory(:product) }
8
+ let!(:attributes) { [:id, :position, :attachment_content_type,
9
+ :attachment_file_name, :type, :attachment_updated_at, :attachment_width,
10
+ :attachment_height, :alt] }
11
+
12
+ before do
13
+ stub_authentication!
14
+ end
15
+
16
+ it "can upload a new image for a product" do
17
+ product.images.count.should == 0
18
+ api_post :create, :product_id => product.to_param, :image => { :attachment => upload_image("thinking-cat.jpg") }
19
+ response.status.should == 201
20
+ json_response.should have_attributes(attributes)
21
+ product.images.count.should == 1
22
+ end
23
+
24
+ it "can upload a new image for a variant" do
25
+ product.master.images.count.should == 0
26
+ api_post :create, :variant_id => product.master.to_param, :image => { :attachment => upload_image("thinking-cat.jpg") }
27
+ response.status.should == 201
28
+ json_response.should have_attributes(attributes)
29
+ product.images.count.should == 1
30
+ end
31
+
32
+ context "working with an existing image" do
33
+ let!(:product_image) { product.master.images.create!(:attachment => image("thinking-cat.jpg")) }
34
+
35
+ it "can update image data" do
36
+ product_image.position.should == 1
37
+ api_post :update, :variant_id => product.master.to_param, :image => { :position => 2 }, :id => product_image.id
38
+ response.status.should == 200
39
+ json_response.should have_attributes(attributes)
40
+ product_image.reload.position.should == 2
41
+ end
42
+
43
+ it "can delete an image" do
44
+ api_delete :destroy, :variant_id => product.master.to_param, :id => product_image.id
45
+ response.status.should == 200
46
+ lambda { product_image.reload }.should raise_error(ActiveRecord::RecordNotFound)
47
+ end
48
+ end
49
+ end
50
+ end