diff --git a/api/app/controllers/spree/api/resource_controller.rb b/api/app/controllers/spree/api/resource_controller.rb new file mode 100644 index 0000000000..60dae7d6dd --- /dev/null +++ b/api/app/controllers/spree/api/resource_controller.rb @@ -0,0 +1,75 @@ +class Spree::Api::ResourceController < Spree::Api::BaseController + before_action :load_resource, only: [:show, :update, :destroy] + + def index + @collection = model_class.accessible_by(current_ability, :read).ransack(params[:q]).result.page(params[:page]).per(params[:per_page]) + instance_variable_set("@#{controller_name}", @collection) + + respond_with(@collection) + end + + def show + respond_with(@object) + end + + def new + authorize! :new, model_class + respond_with(model_class.new) + end + + def create + authorize! :create, model_class + + @object = model_class.new(permitted_resource_params) + instance_variable_set("@#{object_name}", @object) + + if @object.save + respond_with(@object, status: 201, default_template: :show) + else + invalid_resource!(@object) + end + end + + def update + authorize! :update, @object + + if @object.update_attributes(permitted_resource_params) + respond_with(@object, status: 200, default_template: :show) + else + invalid_resource!(@object) + end + end + + def destroy + authorize! :destroy, @object + + if @object.destroy + respond_with(@object, status: 204) + else + invalid_resource!(@object) + end + end + + protected + + def load_resource + @object = model_class.accessible_by(current_ability, :read).find(params[:id]) + instance_variable_set("@#{object_name}", @object) + end + + def permitted_resource_params + params.require(object_name).permit(permitted_resource_attributes) + end + + def permitted_resource_attributes + send("permitted_#{object_name}_attributes") + end + + def model_class + "Spree::#{controller_name.classify}".constantize + end + + def object_name + controller_name.singularize + end +end diff --git a/api/app/controllers/spree/api/users_controller.rb b/api/app/controllers/spree/api/users_controller.rb index 088a0ea6ce..6f52497d74 100644 --- a/api/app/controllers/spree/api/users_controller.rb +++ b/api/app/controllers/spree/api/users_controller.rb @@ -1,56 +1,12 @@ -module Spree - module Api - class UsersController < Spree::Api::BaseController +class Spree::Api::UsersController < Spree::Api::ResourceController - def index - @users = Spree.user_class.accessible_by(current_ability,:read).ransack(params[:q]).result.page(params[:page]).per(params[:per_page]) - respond_with(@users) - end + protected - def show - respond_with(user) - end - - def new - end - - def create - authorize! :create, Spree.user_class - @user = Spree.user_class.new(user_params) - if @user.save - respond_with(@user, :status => 201, :default_template => :show) - else - invalid_resource!(@user) - end - end - - def update - authorize! :update, user - if user.update_attributes(user_params) - respond_with(user, :status => 200, :default_template => :show) - else - invalid_resource!(user) - end - end - - def destroy - authorize! :destroy, user - user.destroy - respond_with(user, :status => 204) - end - - private - - def user - @user ||= Spree.user_class.accessible_by(current_ability, :read).find(params[:id]) - end - - def user_params - params.require(:user).permit(permitted_user_attributes | - [bill_address_attributes: permitted_address_attributes, - ship_address_attributes: permitted_address_attributes]) - end + def model_class + Spree.user_class + end - end + def permitted_resource_attributes + super | [bill_address_attributes: permitted_address_attributes, ship_address_attributes: permitted_address_attributes] end end diff --git a/api/spec/controllers/spree/api/resource_controller_spec.rb b/api/spec/controllers/spree/api/resource_controller_spec.rb new file mode 100644 index 0000000000..44ef39cb5f --- /dev/null +++ b/api/spec/controllers/spree/api/resource_controller_spec.rb @@ -0,0 +1,157 @@ +require 'spec_helper' + +module Spree + module Api + class WidgetsController < Spree::Api::ResourceController + prepend_view_path('spec/test_views') + + def model_class + Widget + end + + def permitted_widget_attributes + [:name] + end + end + end + + describe Api::WidgetsController, type: :controller do + render_views + + after(:all) do + Rails.application.reload_routes! + end + + with_model 'Widget' do + table do |t| + t.string :name + t.integer :position + t.timestamps null: false + end + + model do + acts_as_list + validates :name, presence: true + end + end + + before do + Spree::Core::Engine.routes.draw do + namespace :api do + resources :widgets + end + end + end + + let(:user) { create(:user, :with_api_key) } + let(:admin_user) { create(:admin_user, :with_api_key) } + + describe "#index" do + let!(:widget) { Widget.create!(name: "a widget") } + + it "returns no widgets" do + api_get :index, token: user.spree_api_key + expect(response).to be_success + expect(json_response['widgets']).to be_blank + end + + context "it has authorization to read widgets" do + it "returns widgets" do + api_get :index, token: admin_user.spree_api_key + expect(response).to be_success + expect(json_response['widgets']).to include( + 'name' => 'a widget', + 'position' => 1 + ) + end + end + end + + describe "#show" do + let(:widget) { Widget.create!(name: "a widget") } + + it "returns not found" do + api_get :show, id: widget.to_param, token: user.spree_api_key + assert_not_found! + end + + context "it has authorization read widgets" do + it "returns widget details" do + api_get :show, id: widget.to_param, token: admin_user.spree_api_key + expect(response).to be_success + expect(json_response['name']).to eq 'a widget' + end + end + end + + describe "#new" do + it "returns unauthorized" do + api_get :new, token: user.spree_api_key + expect(response).to be_unauthorized + end + + context "it is allowed to view a new widget" do + it "can learn how to create a new widget" do + api_get :new, token: admin_user.spree_api_key + expect(response).to be_success + expect(json_response["attributes"]).to eq(['name']) + end + end + end + + describe "#create" do + it "returns unauthorized" do + expect { + api_post :create, widget: { name: "a widget" }, token: user.spree_api_key + }.not_to change(Widget, :count) + expect(response).to be_unauthorized + end + + context "it is authorized to create widgets" do + it "can create a widget" do + expect { + api_post :create, widget: { name: "a widget" }, token: admin_user.spree_api_key + }.to change(Widget, :count).by(1) + expect(response).to be_created + expect(json_response['name']).to eq 'a widget' + expect(Widget.last.name).to eq 'a widget' + end + end + end + + describe "#update" do + let!(:widget) { Widget.create!(name: "a widget") } + it "returns unauthorized" do + api_put :update, id: widget.to_param, widget: { name: "another widget" }, token: user.spree_api_key + assert_not_found! + expect(widget.reload.name).to eq 'a widget' + end + + context "it is authorized to update widgets" do + it "can update a widget" do + api_put :update, id: widget.to_param, widget: { name: "another widget" }, token: admin_user.spree_api_key + expect(response).to be_success + expect(json_response['name']).to eq 'another widget' + expect(widget.reload.name).to eq 'another widget' + end + end + end + + describe "#destroy" do + let!(:widget) { Widget.create!(name: "a widget") } + it "returns unauthorized" do + api_delete :destroy, id: widget.to_param, token: user.spree_api_key + assert_not_found! + expect { widget.reload }.not_to raise_error + end + + context "it is authorized to destroy widgets" do + it "can destroy a widget" do + api_delete :destroy, id: widget.to_param, token: admin_user.spree_api_key + expect(response.status).to eq 204 + expect { widget.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end + end +end diff --git a/api/spec/spec_helper.rb b/api/spec/spec_helper.rb index 85a2a77548..55fd413f96 100644 --- a/api/spec/spec_helper.rb +++ b/api/spec/spec_helper.rb @@ -54,6 +54,8 @@ config.include Spree::TestingSupport::Preferences config.include Spree::TestingSupport::Mail + config.extend WithModel + config.fail_fast = ENV['FAIL_FAST'] || false config.before(:each) do diff --git a/api/spec/test_views/spree/api/widgets/index.v1.rabl b/api/spec/test_views/spree/api/widgets/index.v1.rabl new file mode 100644 index 0000000000..6d6cb52f46 --- /dev/null +++ b/api/spec/test_views/spree/api/widgets/index.v1.rabl @@ -0,0 +1,7 @@ +object false +child(@collection => :widgets) do + extends "spree/api/widgets/show" +end +node(:count) { @collection.count } +node(:current_page) { params[:page] || 1 } +node(:pages) { @collection.num_pages } diff --git a/api/spec/test_views/spree/api/widgets/new.v1.rabl b/api/spec/test_views/spree/api/widgets/new.v1.rabl new file mode 100644 index 0000000000..f1a3ddd5fc --- /dev/null +++ b/api/spec/test_views/spree/api/widgets/new.v1.rabl @@ -0,0 +1,2 @@ +object false +node(:attributes) { [:name] } diff --git a/api/spec/test_views/spree/api/widgets/show.v1.rabl b/api/spec/test_views/spree/api/widgets/show.v1.rabl new file mode 100644 index 0000000000..10d957afed --- /dev/null +++ b/api/spec/test_views/spree/api/widgets/show.v1.rabl @@ -0,0 +1,2 @@ +object @object +attributes :name, :position