This is the end of the 18-part serialization.
This time we will create a user controller. It's a compilation of the past, so we recommend that you try it without looking at the example code.
It is mainly intended for use by administrators to manage users and as a group of functions for users to update and delete themselves.
I will proceed with the procedure.
$ rails g controller v1/users
Here is a group of files that have been slightly modified so that rubocop does not get angry.
app/controllers/v1/users_controller.rb
# frozen_string_literal: true
module V1
#
# users controller
#
class UsersController < ApplicationController
end
end
spec/requests/v1/users_request_spec.rb
# frozen_string_literal: true
require "rails_helper"
RSpec.describe "V1::Users", type: :request do
end
Hit the command to create a file.
$ rails g pundit:policy user
After making minor modifications so that rubocop does not get angry, it is completed for the time being.
app/policies/user_policy.rb
# frozen_string_literal: true
#
#user policy class
#
class UserPolicy < ApplicationPolicy
#
# scope
#
class Scope < Scope
def resolve
scope.all
end
end
end
spec/policies/user_policy_spec.rb
# frozen_string_literal: true
require "rails_helper"
RSpec.describe UserPolicy, type: :policy do
let(:user) { User.new }
subject { described_class }
permissions ".scope" do
pending "add some examples to (or delete) #{__FILE__}"
end
permissions :show? do
pending "add some examples to (or delete) #{__FILE__}"
end
permissions :create? do
pending "add some examples to (or delete) #{__FILE__}"
end
permissions :update? do
pending "add some examples to (or delete) #{__FILE__}"
end
permissions :destroy? do
pending "add some examples to (or delete) #{__FILE__}"
end
end
Review of specifications.
Here is the implementation with that in mind.
spec/policies/user_policy_spec.rb
# frozen_string_literal: true
require "rails_helper"
RSpec.describe UserPolicy, type: :policy do
let(:user) { create(:user) }
let(:another_user) { create(:user) }
let(:admin_user) { create(:user, :admin) }
subject { described_class }
permissions :index?, :create?, :destroy? do
it "Not allowed when not logged in" do
expect(subject).not_to permit(nil, user)
end
it "Not allowed when logged in as a non-admin user" do
expect(subject).not_to permit(user, user)
end
it "Allowed when logged in as admin user" do
expect(subject).to permit(admin_user, user)
end
end
permissions :show?, :update? do
it "Not allowed when not logged in" do
expect(subject).not_to permit(nil, user)
end
it "Not allowed when logged in but another user" do
expect(subject).not_to permit(user, another_user)
end
it "Allowed when logged in as admin user" do
expect(subject).to permit(admin_user, user)
end
it "Allowed when logged in and the same user" do
expect(subject).to permit(user, user)
end
end
end
The point is that another user called ʻanother_user` is defined. Now, at this point, we haven't set a policy, so make sure that some tests are moss.
app/policies/user_policy.rb
# frozen_string_literal: true
#
#user policy class
#
class UserPolicy < ApplicationPolicy
def index?
admin?
end
def show?
me? || admin?
end
def create?
admin?
end
def update?
me? || admin?
end
def destroy?
admin?
end
private
def me?
@record == @user
end
#
# scope
#
class Scope < Scope
def resolve
scope.all
end
end
end
The point is to define a private method called me?
in user_policy.rb.
Mine?
In application_policy.rb compared @ record.user == @user
with @ record
's ʻuser. However, this time we will check if
@ record and
@ usermatch, so the English grammar will be
me?instead of
mine?. Besides, it seems that only users_controller can be compared with ʻuser
itself at the moment, so I proposed to user_policy instead of application_policy.
The test should have passed without any special mention. </ div> </ details>
spec/requests/v1/users_request_spec.rb
# frozen_string_literal: true
require "rails_helper"
RSpec.describe "V1::Users", type: :request do
before do
@user = create(:user, name: "user test")
@authorized_headers = authorized_user_headers @user
admin = create(:user, :admin)
@authorized_admin_headers = authorized_user_headers admin
end
describe "GET /v1/users#index" do
before do
create_list(:user, 3)
end
it "Normal response code is returned" do
get v1_users_url, headers: @authorized_admin_headers
expect(response.status).to eq 200
end
it "The number is returned correctly" do
get v1_users_url, headers: @authorized_admin_headers
json = JSON.parse(response.body)
expect(json["users"].length).to eq(3 + 2) #Includes 2 for headers
end
it "Responses are returned in descending order of id" do
get v1_users_url, headers: @authorized_admin_headers
json = JSON.parse(response.body)
first_id = json["users"][0]["id"]
expect(json["users"][1]["id"]).to eq(first_id - 1)
expect(json["users"][2]["id"]).to eq(first_id - 2)
expect(json["users"][3]["id"]).to eq(first_id - 3)
expect(json["users"][4]["id"]).to eq(first_id - 4)
end
end
describe "GET /v1/users#show" do
it "Normal response code is returned" do
get v1_user_url({ id: @user.id }), headers: @authorized_headers
expect(response.status).to eq 200
end
it "name is returned correctly" do
get v1_user_url({ id: @user.id }), headers: @authorized_headers
json = JSON.parse(response.body)
expect(json["user"]["name"]).to eq("user test")
end
it "404 response is returned when id does not exist" do
last_user = User.last
get v1_user_url({ id: last_user.id + 1 }), headers: @authorized_headers
expect(response.status).to eq 404
end
end
describe "POST /v1/users#create" do
let(:new_user) do
attributes_for(:user, name: "create_name test", email: "[email protected]", admin: true)
end
it "Normal response code is returned" do
post v1_users_url, params: new_user, headers: @authorized_admin_headers
expect(response.status).to eq 200
end
it "One more case will be returned" do
expect do
post v1_users_url, params: new_user, headers: @authorized_admin_headers
end.to change { User.count }.by(1)
end
it "name, email,admin returns correctly" do
post v1_users_url, params: new_user, headers: @authorized_admin_headers
json = JSON.parse(response.body)
expect(json["user"]["name"]).to eq("create_name test")
expect(json["user"]["email"]).to eq("[email protected]")
expect(json["user"]["admin"]).to be true
end
it "Errors are returned when the parameter is invalid" do
post v1_users_url, params: {}, headers: @authorized_admin_headers
json = JSON.parse(response.body)
expect(json.key?("errors")).to be true
end
end
describe "PUT /v1/users#update" do
let(:update_param) do
update_param = attributes_for(:user, name: "update_name test", email: "[email protected]", admin: true)
update_param[:id] = @user.id
update_param
end
it "Normal response code is returned" do
put v1_user_url({ id: update_param[:id] }), params: update_param, headers: @authorized_headers
expect(response.status).to eq 200
end
it "name, email,admin returns correctly" do
put v1_user_url({ id: update_param[:id] }), params: update_param, headers: @authorized_headers
json = JSON.parse(response.body)
expect(json["user"]["name"]).to eq("update_name test")
expect(json["user"]["email"]).to eq("[email protected]")
expect(json["user"]["admin"]).to be false #Since it is a problem if the admin authority can be rewritten, leave it as false
end
it "Errors are returned when the parameter is invalid" do
put v1_user_url({ id: update_param[:id] }), params: { name: "" }, headers: @authorized_headers
json = JSON.parse(response.body)
expect(json.key?("errors")).to be true
end
it "404 response is returned when id does not exist" do
last_user = User.last
put v1_user_url({ id: last_user.id + 1 }), params: update_param, headers: @authorized_admin_headers
expect(response.status).to eq 404
end
end
describe "DELETE /v1/users#destroy" do
it "Normal response code is returned" do
delete v1_user_url({ id: @user.id }), headers: @authorized_admin_headers
expect(response.status).to eq 200
end
it "One less and returns" do
expect do
delete v1_user_url({ id: @user.id }), headers: @authorized_admin_headers
end.to change { User.count }.by(-1)
end
it "404 response is returned when id does not exist" do
last_user = User.last
delete v1_user_url({ id: last_user.id + 1 }), headers: @authorized_admin_headers
expect(response.status).to eq 404
end
end
end
There are some considerations that are different from those of post.
create (: user)
is done when creating header, write the test in consideration of those two cases.I'm incorporating around.
Also, since there is an admin judgment in the response, it is necessary to modify the serializer.
app/serializers/user_serializer.rb
# user serializer
#
class UserSerializer < ActiveModel::Serializer
- attributes :id, :name, :email
+ attributes :id, :name, :email, :admin
end
Now you are ready. Next we will enter the controller implementation. </ div> </ details>
Modify the routes so that you can access the controller.
config/routes.rb
Rails.application.routes.draw do
namespace "v1" do
resources :posts
+ resources :users
mount_devise_token_auth_for "User", at: "auth"
end
Next is the controller. This can be almost diverted from post.
app/controllers/v1/users_controller.rb
# frozen_string_literal: true
module V1
#
# users controller
#
class UsersController < ApplicationController
before_action :set_user, only: %i[show update destroy]
def index
users = User.order(created_at: :desc).limit(20)
authorize users
render json: users
end
def show
authorize @user
render json: @user
end
def create
user = User.new(user_create_params)
user[:provider] = :email
authorize user
if user.save
render json: user
else
render json: { errors: user.errors }
end
end
def update
authorize @user
if @user.update(user_params)
render json: @user
else
render json: { errors: @user.errors }
end
end
def destroy
authorize @user
@user.destroy
render json: @user
end
private
def set_user
@user = User.find(params[:id])
end
def user_create_params
#Allow admin privileges to be set only when creating
params.permit(:name, :email, :admin, :password)
end
def user_params
params.permit(:name, :email, :password)
end
end
end
As I wrote in the test section, admin only allows it when creating. </ div> </ details>
that's all. Thank you for visiting our website 18 times.
Based on this, please expand the functions such as comment function and social login.